必须用::after伪元素实现波纹圆:绝对定位+z-index负值压底,pointer-events:none防拦截;JS计算点击坐标并约束范围,配合@keyframes控制scale与opacity三段动画,Safari需will-change:transform及translate替代top/left定位。

用 ::after 伪元素画波纹圆,别用 ::before
波纹效果本质是点击位置生成一个透明圆,再放大+淡出。必须用 ::after,因为 ::before 会被按钮内容层遮住,动画根本看不见。圆要绝对定位、z-index 负值压在按钮下方,同时设 pointer-events: none,否则会拦截后续点击。
- 圆的初始尺寸设为
0,transform: scale(0)比直接改width/height性能更好 - 圆心坐标靠 JS 注入:用
getBoundingClientRect()算出点击点相对按钮左上角的偏移,再设为left和top - 务必加
border-radius: 50%,否则是方块扩散
transition 不够用,得上 @keyframes
transition 只能做两态变化,但波纹需要「从无到有→放大→透明消失」三阶段,必须用帧动画。关键不是只放大,而是同步控制 opacity:起始不透明(否则看不到出现过程),中途保持可见,末尾快速归零。
- 推荐动画时长
600ms左右,太短像闪一下,太长拖沓 - 动画函数用
cubic-bezier(0.2, 0, 0.1, 1),比ease-out更自然——先快后慢,模拟真实水波衰减 - 别忘了给伪元素加
animation-fill-mode: forwards,否则动画结束就回退到初始状态
JS 绑定点击要防重复触发和边界越界
用户快速连点或点在按钮边缘时,容易出现多个波纹重叠、圆心坐标错乱甚至负值溢出。核心是每次点击前清掉上一个动画,并严格约束坐标范围。
- 用
element.style.animation = 'none'瞬间重置动画状态,再赋新值,避免队列堆积 - 计算
left和top时,用Math.max(0, Math.min(x, width - diameter))限制圆心不超出按钮边界 - 给按钮加
overflow: hidden,否则波纹放大时会撑出容器 - 移动端注意
touchstart替代click,避免 300ms 延迟
兼容性坑:Safari 对 transform + scale 的渲染异常
Safari(尤其 iOS 15 以下)对伪元素 + transform: scale() 的组合有重绘 bug:波纹可能卡顿、闪烁,甚至不触发动画。绕过方法很具体,不是加 -webkit- 前缀就能解决。
立即学习“前端免费学习笔记(深入)”;
- 必须给伪元素加
will-change: transform,强制开启硬件加速 - 避免同时用
transform和top/left定位圆心——统一用transform: translate(x, y) scale(s) - 如果按钮有
border-radius,波纹圆要额外加overflow: hidden在父级,否则 Safari 会裁切动画轨迹
波纹看似简单,真正卡住人的永远是 Safari 的渲染逻辑和连点时的动画状态管理——这两个点不手动兜底,效果大概率在真机上崩。


















