纯CSS无法实现真实点击位置的波纹效果,必须用JS动态计算坐标并驱动::after伪元素;因:active无坐标信息、动画阶段不足、iOS Safari限制及渲染bug等多重原因。

纯 CSS 无法实现真实点击位置出发的波纹涟漪效果——button:active 没有坐标信息,@keyframes 无法读取 clientX/clientY,必须靠 JavaScript 动态计算并驱动伪元素。
为什么不能只用 :active + transition
因为 :active 是瞬时状态,松手即失效,动画根本播不完;transition 只能做两态变化(比如 scale(0) → scale(1)),但真实波纹需要「出现→扩散→淡出」三阶段,且圆心必须随点击点实时变化。iOS Safari 默认禁用 :active,即使强制启用,也无法动态设置 left/top 坐标——写死的 50% 只能从中心炸开。
必须用 ::after 伪元素 + JS 动态定位
核心是让 ::after 成为波纹载体:它不占 DOM、支持硬件加速的 transform: scale(),还能用 pointer-events: none 避免拦截后续点击。
- 按钮必须设
position: relative,否则::after会相对于body定位,导致波纹从页面左上角炸开 -
::after要设content: ""、position: absolute、border-radius: 50%、z-index: -1(压在文字下方) - JS 中用
e.clientX - button.getBoundingClientRect().left算出相对 X,同理算 Y;再通过style.left = x + "px"和style.top = y + "px"注入(别用attr(),Safari 不支持) - 关键一步:加
transform: translate(-50%, -50%),让伪元素中心对准点击点,否则圆心偏到左上角
Safari 渲染异常必须手动兜底
iOS Safari(尤其 15 以下)对 ::after + transform: scale() 有重绘 bug:波纹卡顿、闪烁、甚至不触发。这不是加 -webkit- 前缀能解决的。
立即学习“前端免费学习笔记(深入)”;
- 给
::after加will-change: transform,强制开启 GPU 加速 - 圆心定位统一用
transform: translate(x, y) scale(s),别混用top/left——Safari 对混合定位的合成逻辑不稳定 - 如果按钮本身有
border-radius,父级要加overflow: hidden,否则 Safari 会裁切动画轨迹 - 动画结束必须监听
animationend事件并调用element.remove(),不能靠setTimeout,否则真机上容易残留
动画参数不是越炫越好
600ms 左右最稳:太短像闪一下,太长拖沓;缓动函数用 cubic-bezier(0.2, 0, 0.1, 1) 比 ease-out 更自然——先快后慢,模拟真实水波衰减。
-
animation-fill-mode: forwards必须加,否则动画结束就回退到初始状态 - 背景色建议用
rgba(0,0,0,0.1)这类低饱和值,避免遮盖按钮文字 - 快速连点时,每次点击前先清除已存在的波纹(
button.querySelectorAll('.ripple')或统一用一个::after并重置left/top) - 移动端必须用
touchstart替代click,避免 300ms 延迟;同时监听mousedown兼容桌面端
真正卡住人的永远是 Safari 的渲染逻辑和连点时的动画状态管理——这两个点不手动兜底,效果大概率在真机上崩。


















