纯 CSS 无法实现真实波纹效果,因 :active 不携带坐标信息且 iOS Safari 默认不触发;必须用 JavaScript 获取点击坐标并写入 CSS 变量,配合 position: relative、pointer-events: none、transform: scale() 和 transform-origin: center 等关键样式才能实现从点击点炸开的物理感波纹。

必须用 JavaScript 获取点击坐标并写入 CSS 变量,否则波纹永远从左上角或中心炸开——CSS 本身无法读取鼠标位置。
为什么纯 CSS 的 :active 无法实现真实波纹
:active 是布尔状态,不携带坐标信息。哪怕你写 button:active::after { left: var(--x); top: var(--y); },--x 和 --y 若没被 JS 更新,就还是初始值(比如 50% 或 0),结果就是所有点击都从同一位置开始扩散。
移动端更麻烦::active 在 iOS Safari 默认不触发,除非加 touch-action: manipulation 或 * { cursor: pointer; },但即便触发,它仍无法定位。
- 别指望
radial-gradient(circle at click-x click-y)这种写法能生效——CSS 没有click-x这个变量 - 伪类动画只能做固定中心缩放,不是“从指尖炸开”,物理感直接归零
- 如果按钮是 flex 子项且父容器没设
position,::after会相对 body 定位,偏移更严重
::after 波纹必须配 position: relative 和 pointer-events: none
波纹是盖在按钮上的 ::after,它需要两个硬性条件才能对齐、不拦截点击:
立即学习“前端免费学习笔记(深入)”;
- 触发元素(如
button)必须设position: relative,否则::after的left/top会向上找最近的定位祖先,大概率错位到页面顶部 -
::after必须显式声明pointer-events: none,否则快速连点时,上一个还没消失的波纹会挡住下一次click事件 - 漏掉
overflow: hidden,波纹超出按钮边界时会“穿帮”,尤其圆角按钮
常见错误:用 position: absolute 直接给按钮加定位——这会破坏文档流,影响布局;应该只加 relative,不改变占位。
用 transform: scale() 替代 width/height 动画
改 width 和 height 触发重排(reflow),低端设备卡顿明显;transform: scale() 只重绘(repaint),GPU 加速更稳。
- 伪元素初始设
scale(0),动画终点建议scale(3)(覆盖常见按钮尺寸足够) - 必须加
transform-origin: center center,否则缩放中心默认在左上角,波纹歪斜 - 配合
border-radius: 50%才是正圆,别靠宽高比“凑”圆形 - 动画曲线推荐
cubic-bezier(0.2, 0.8, 0.3, 1)或ease-out,线性过渡显得机械
别忘了加 will-change: transform, opacity,尤其在移动端防掉帧。
JS 必须动态算坐标 + 清理旧波纹
CSS 负责动效,JS 负责“从哪炸”。每次点击都要:
- 调用
getBoundingClientRect()获取按钮视口位置 - 用
e.clientX - rect.left和e.clientY - rect.top算出相对偏移 - 通过
element.style.setProperty('--x', x + 'px')写入自定义属性 - 动画结束(
transitionend或setTimeout)后手动清理,否则 DOM 节点堆积
容易忽略的细节:插入波纹元素后,得用 requestAnimationFrame 延迟一帧再触发动画,否则首帧可能被浏览器跳过,出现“点了没反应”。


















