应使用 ::after 而非 ::before,因其配合 z-index: -1 和 pointer-events: none 可稳定置于最底层且不拦截点击;::before 易被内容遮盖,且需严格满足 position: relative、content: ""、绝对定位及正确坐标计算等硬性条件。

为什么不用 ::before 而要用 ::after?
伪元素层叠顺序直接决定水波纹是否可见。::before 默认在按钮内容(文字、图标)下方,但仍在内容层之上;而按钮的背景、边框、文字都可能遮盖它——尤其当按钮有 background 或 color 时,::before 的圆经常被“吃掉”,动画根本看不到。真正能稳定压在最底层的只有 ::after + z-index: -1,且必须配合 pointer-events: none,否则它会拦截后续点击。
::before 写出来却没反应?检查这三处硬性条件
即使强行用 ::before,以下约束缺一不可,否则定位错乱或动画卡死:
- 按钮必须设
position: relative,否则left/top相对于视口而非按钮自身 - ::before 必须含
content: ""、position: absolute、border-radius: 50%,且初始opacity: 0 - 坐标计算不能用
e.offsetX——IE 不支持,且在transform: scale()或 iframe 嵌套下严重失真;必须用getBoundingClientRect()算相对偏移:e.clientX - rect.left和e.clientY - rect.top
动画必须用 transform + opacity,别碰 width/height
改 width/height 会触发 layout(重排),帧率暴跌;尤其在移动端连续点击时,波纹明显卡顿。正确做法是:
- 伪元素初始设
transform: scale(0),动画终点用scale(4)(太大易出屏) - 同步控制
opacity:起始1(确保出现可见),中途保持,末尾快速归0 - 不要只依赖
:active——它在 iOS Safari 和部分安卓 WebView 中对非语义元素(如div)不触发;必须用mousedown或touchstart事件手动触发动画
多按钮共用逻辑时,最容易漏掉的清理动作
每个按钮点击后,::before 的 left/top 值会残留。下次点击若未重置,新波纹就从旧坐标炸开。必须在每次事件开头清空:
button.style.removeProperty('left');
button.style.removeProperty('top');
或者更稳妥地,用 CSS 变量驱动:button.style.setProperty('--x', '0px'),再在 CSS 里写 left: var(--x)。注意单位必须显式加 px,否则 var(--x) 在定位中无效。
真实项目里,90% 的“波纹偏移”问题都卡在这一步——不是 JS 坐标算错,而是上一次的 left/top 没清掉。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











