双波纹动画必须用::before和::after两个伪元素分别实现内圈快波纹(300ms、scale(0→2)、opacity(0.4→0))和外圈慢波纹(600ms、scale(0→4)、opacity(0.25→0)),均需content: ""、position: absolute、border-radius: 50%、pointer-events: none,并统一由:active触发,配合对应cubic-bezier缓动与overflow: hidden防裁切。

双波纹动画必须用两个独立 ::after 伪元素
纯 CSS 实现双波纹,不能靠一个伪元素叠加两段动画——::after 只能有一个,且无法在同一个伪元素上定义两套独立的 transform 和 opacity 轨迹。必须显式声明两个伪元素:::before 和 ::after,或用两个 ::after(但后者需 hack,不推荐)。最稳妥的是:一个用 ::before 做内圈快波纹,一个用 ::after 做外圈慢波纹。
常见错误是给同一个 ::after 写两套 @keyframes 并试图用 animation-delay 错开——这会导致第二段动画被第一段覆盖,或在 Safari 中直接失效。
- 两个伪元素都必须设
content: ""、position: absolute、border-radius: 50% - 内圈波纹(
::before)建议用scale(0) → scale(2),持续 300ms,起始opacity: 0.4,结束opacity: 0 - 外圈波纹(
::after)用scale(0) → scale(4),持续 600ms,起始opacity: 0.25,结束opacity: 0 - 两者都要加
pointer-events: none,否则会拦截后续点击
:active 状态下同时触发动画但不同步执行
双波纹不是“先内圈再外圈”,而是点击瞬间两个伪元素同时开始动画,但靠时长和缓动函数制造视觉分层。关键在于:不能依赖 animation-delay 模拟先后——移动端 touch 事件触发有延迟,delay 容易错位;应统一用 :active 触发,靠动画本身节奏拉开层次。
- 内圈用
cubic-bezier(0.4, 0, 0.2, 1),偏快收尾,显得“弹” - 外圈用
cubic-bezier(0.2, 0, 0.1, 1),起始更缓,拉长扩散感 - 两个伪元素的
transition都要写全:例如transition: transform 300ms cubic-bezier(0.4, 0, 0.2, 1), opacity 300ms - 务必在按钮上设
overflow: hidden,否则外圈波纹超出按钮边界时会在 Safari 中裁切异常
移动端点击坐标错位?别用 clientX/clientY 直接算
如果你改用 JS 动态创建双波纹(比如一个 span 做内圈 + 一个 span 做外圈),那 clientX/clientY 在 iOS Safari 上会严重偏移——因为 touch 事件坐标和 mouse 事件坐标系统不一致,且页面缩放、滚动条宽度都会干扰。
立即学习“前端免费学习笔记(深入)”;
- 必须统一用
event.touches?.[0] || event获取原始事件对象 - 计算相对坐标前,先调
button.getBoundingClientRect(),再减去rect.left和rect.top - 内圈波纹圆心用原坐标,外圈波纹圆心建议微调:例如
x + 2、y - 1,避免像素对齐导致两圈完全重叠 - 两个波纹元素的
style.transform = translate(-50%, -50%) scale(0)必须写死,不能混用top/left定位——否则在 Chrome Android 上动画会抖动
动画结束后 DOM 清理不干净,连点就卡顿
双波纹意味着每次点击要插入/移除两个元素,如果没清干净,DOM 节点会越积越多,尤其在快速连点时,内存占用飙升,iOS 上直接掉帧。
- 每个波纹
span插入后,必须监听animationend事件,而不是靠setTimeout - 移除前先设
el.style.animation = 'none',再读一次offsetHeight强制重排,最后el.remove() - 不要用
innerHTML = ''清空按钮内容——会误删按钮文字或图标 - 若需高频点击(如游戏 UI),建议预建两个波纹元素池,点击时复用而非新建
双波纹真正的难点不在动画本身,而在 Safari 对多层 transform + opacity 同时动画的重绘调度——哪怕代码逻辑完全正确,漏掉 will-change: transform 或父容器没设 overflow: hidden,iOS 上就会出现波纹撕裂或卡顿一帧。动手前先在真机上测三秒连点。



















