纯css无法实现符合material design规范的波纹效果,因其依赖点击坐标而:active伪类仅支持全局状态;必须结合js计算坐标并用::after伪元素驱动三段式径向动画。

纯 CSS 无法真正实现符合 Material Design 规范的波纹效果——它必须响应点击坐标,而 :active 伪类只能触发全局状态,无法获取 clientX/clientY。所有“纯 CSS 波纹”要么是固定中心、要么是假动画(如 scale(1.2) + opacity 过渡),和真实涟漪有本质区别。
为什么:active + transition 不够用
Material 波纹要求:从用户点击位置开始扩散,圆心随点击点变化;动画需三段(出现→放大→淡出);边缘不能溢出按钮边界。而 :active 只能触发改元素整体状态,transition 又仅支持两态插值,无法驱动基于坐标的径向动画。常见错误是给按钮加 transition: all 0.4s,结果点击 anywhere 都只从左上角或中心“弹一下”,完全违背交互直觉。
必须用::after伪元素 + JS 计算坐标
核心链路是:监听 mousedown → 获取 getBoundingClientRect() 偏移 → 注入 left/top 到 ::after → 启动 @keyframes 动画。关键细节:
-
::after必须设position: absolute; z-index: -1; pointer-events: none;,否则会被按钮文字遮住或拦截后续点击 - 圆心坐标要约束在按钮内:
left: Math.max(0, Math.min(x - radius, width - diameter)) - 初始
transform: scale(0)比width/height: 0更高性能,且兼容硬件加速 - 动画结束必须加
animation-fill-mode: forwards,否则波纹会缩回原形
Safari 下波纹卡顿/不触发的绕过写法
iOS Safari(尤其 15 及更早)对 transform: scale() + 伪元素组合有重绘 bug,表现是波纹一闪而过、放大中途卡死,甚至完全不播。实测有效的修复项:
- 给
::after加will-change: transform,强制开启 GPU 加速 - 放弃
top/left定位圆心,统一改用transform: translate(x, y) scale(s) - 若按钮本身有
border-radius,父容器必须加overflow: hidden,否则 Safari 会裁切动画轨迹 - 避免在动画中同时修改
opacity和transform外的其他属性(比如background),会触发 layout
移动端touchstart与mousedown共存的防重逻辑
安卓 Chrome 和 iOS Safari 对事件响应不一致:前者 mousedown 几乎即时,后者有 300ms 延迟且 touchstart 更可靠。但单独绑 touchstart 会导致桌面端失效。正确做法是:
- 同时监听
mousedown和touchstart - 用
event.touches?.[0]优先取触点,fallback 到event.clientX/clientY - 在事件开头加节流:
if (btn.rippleActive) return;,并在mouseup/touchend清除标记 - 动画清理不用
setTimeout(..., 600),改用element.addEventListener('animationend', handler, { once: true })
最易被忽略的是坐标计算时没处理 scrollLeft/scrollTop —— 如果按钮在滚动容器里,clientX 直接赋给 left 会导致波纹偏移。真实项目里,这个 bug 往往到上线后才被用户截图反馈出来。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











