纯 CSS 可实现高性能移动端波纹动画,核心用 radial-gradient + background-size 动画模拟扩散,需 touchstart 捕获坐标、transitionend 清理状态、will-change 提升兼容性。

纯 CSS 实现移动端波纹动画,完全可行,且必须避开 box-shadow 扩散、transform: scale() 配合 overflow: hidden 这类高开销写法——它们在低端安卓机上掉帧明显。核心是用 radial-gradient + background-size 动画模拟波纹扩散,零 DOM 操作、零 JS 触发、硬件加速友好。
为什么不用 :active 伪类直接做波纹
因为 iOS Safari 和部分安卓 WebView 对 :active 的触发有延迟或不响应(尤其在非 cursor: pointer 元素上),且无法控制波纹起点坐标。真实点击位置必须由 JS 捕获,但动画本身仍可交由 CSS 完成。
- 必须监听
touchstart(而非click),防止 300ms 延迟干扰反馈节奏 -
e.preventDefault()在touchstart中调用,避免长按唤出菜单打断交互 - 用
e.touches[0].clientX/clientY计算相对于按钮左上角的偏移,转为background-position值
radial-gradient 波纹的最小可行代码结构
关键不是“画个圆”,而是让这个圆从点击点开始、透明度渐变消失、尺寸线性放大——三者缺一不可。下面这段就是能跑通的最小闭环:
button {
position: relative;
overflow: hidden;
}
button::before {
content: "";
position: absolute;
top: 50%;
left: 50%;
width: 0;
height: 0;
border-radius: 100%;
background: radial-gradient(circle, rgba(0,0,0,0.2) 0%, transparent 70%);
transform: translate(-50%, -50%);
transition: width 0.4s ease-out, height 0.4s ease-out, opacity 0.4s ease-out;
opacity: 0;
}
button.is-rippling::before {
width: 300px;
height: 300px;
opacity: 1;
}-
width/height控制扩散半径,比transform: scale()更稳定(后者在某些安卓机型上会触发重排) -
opacity必须参与过渡,否则波纹消失时会生硬“咔”一下 -
transition-timing-function推荐用cubic-bezier(0.22, 0.61, 0.36, 1),比ease-out更跟手
如何避免重复点击导致波纹叠加或卡顿
波纹类名(如 is-rippled)不能靠 JS 一次性加完就不管——必须监听动画结束并主动清理,否则连续点击会让多个 ::before 层叠、内存泄漏、甚至阻塞后续事件。
立即学习“前端免费学习笔记(深入)”;
- 监听
transitionend(不是animationend),因为这里用的是transition而非@keyframes - 只监听
width或height属性的结束事件,避免opacity提前触发误清理 - 清理后立即重置
button::before的width/height为0,否则下次触发会从旧尺寸继续放大 - 加防抖:在
touchstart回调里加if (this.rippleActive) return;标记位
兼容性与性能被忽略的关键点
很多人测试只看 Chrome,但真正在意的是 Android 8–10 的 UC 浏览器、微信内置 X5 内核、以及 iOS 14.5 以下的 Safari —— 它们对 radial-gradient 的硬件加速支持不一致,且 transition 在 ::before 上可能被降级为软件渲染。
- 务必给按钮加
will-change: transform(哪怕没用到transform),强制开启图层合成 - 避免在
::before中使用box-shadow或多层background,X5 内核下极易闪屏 - 波纹最大尺寸别超过
min(300px, 1.5 * button.clientWidth),防止在小屏上溢出失真 - 如果项目已用
animate.min.css,别混用它的animate__pulse类——那是@keyframes实现,和波纹逻辑冲突


















