纯css无法实现真实波纹效果,必须用js计算点击坐标并动态控制::after伪元素的定位、缩放与淡出。

纯 CSS 无法实现“点击哪、波纹从哪起”的真实波纹效果——button:active 没有坐标信息,@keyframes 无法动态读取 clientX/clientY,必须用 JS 计算点击位置并驱动伪元素。绕不开这一步,否则波纹只会从左上角或中心硬播。
为什么不能只靠 :active + transition
因为 :active 是瞬时状态,松手即失效,动画根本播不完;transition 只能做两态变化(比如 scale(0) → scale(1)),但波纹需要「出现→扩散→淡出」三阶段,且圆心必须随点击点变化。浏览器不提供点击坐标的 CSS 变量,:active 里写死 left: 50%; top: 50% 就只能固定从中心炸开。
-
:active在 iOS Safari 默认被禁用,需额外加touch-action: manipulation或cursor: pointer才勉强触发 - 即使强制启用,
transition也无法在:active中动态设置left/top,坐标还是写死的 - 快速连点时,多个
:active状态重叠,JS 无法清理旧动画,DOM 和渲染层都会堆积残留
必须用 ::after 伪元素 + JS 动态定位
核心是让 ::after 成为波纹载体:它不占 DOM、可绝对定位、支持 transform: scale() 硬件加速,且能用 pointer-events: none 避免拦截后续点击。
- 按钮必须加
position: relative,否则::after的absolute定位会相对于 body 偏移 -
::after要设content: ""、border-radius: 50%、z-index: -1(压在文字下方)、pointer-events: none - JS 中用
e.clientX - button.getBoundingClientRect().left算出相对 X,同理算 Y;再通过style.left = x + "px"和style.top = y + "px"注入(别用attr(),Safari 不支持) - 关键一步:加
transform: translate(-50%, -50%),让伪元素中心对准点击点,否则圆心会偏到左上角
Safari 渲染异常必须手动兜底
iOS Safari(尤其 15 以下)对 ::after + transform: scale() 有重绘 bug:波纹卡顿、闪烁、甚至不触发。这不是加 -webkit- 能解决的。
- 给
::after加will-change: transform,强制开启 GPU 加速 - 圆心定位必须用
transform: translate(x, y),别混用top/left—— Safari 对混合定位的合成逻辑不稳定 - 如果按钮本身有
border-radius,父级要加overflow: hidden,否则 Safari 会裁切动画轨迹 - 动画结束必须监听
animationend事件并调用element.remove(),不能靠setTimeout,否则真机上容易残留
动画参数不是越炫越好
600ms 左右最稳:太短像闪一下,太长拖沓;缓动函数用 cubic-bezier(0.2, 0, 0.1, 1) 比 ease-out 更自然——先快后慢,模拟水波衰减。
- 初始状态必须是
opacity: 0; transform: scale(0),否则缩放前会闪出实心点 - 直径至少覆盖按钮对角线:
Math.hypot(width, height),否则角落点击时波纹被截断 - 每次点击前清掉上一个波纹:用
button.querySelectorAll('.ripple').forEach(el => el.remove()),或统一用 dataset 标记后清除 - 移动端优先用
touchstart替代click,避免 300ms 延迟;但要注意touchstart可能触发多次,得加e.preventDefault()防默认行为
真正卡住人的永远是 Safari 的渲染逻辑和连点时的动画状态管理——这两个点不手动兜底,效果大概率在真机上崩。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











