纯css无法实现真实水波纹效果,必须用js获取点击坐标并设置transform-origin,再由css通过transform和opacity动画渲染。

纯 CSS 无法实现真正点击位置出发的水波纹效果,:active 只能触发全局状态,没法读取 clientX/clientY。必须用 JS 获取坐标,再靠 CSS 动画渲染——但 JS 只管“定位”,动画逻辑全交给 CSS,这样性能才稳。
为什么:active + ::after 做不出真实水波纹
常见错误是写一堆 :active::after + transform: scale(),结果点击 anywhere 都从按钮中心炸开。这是因为 :active 不带坐标信息,top: 50%; left: 50% 永远固定居中。更糟的是:移动端 touch 事件不触发 :active,某些 WebView 直接跳过该伪类。
-
:active生命周期极短,松手即失效,撑不起 400ms+ 的扩散动画 - 伪元素无法动态设置
transform-origin,而水波纹必须以点击点为原点缩放 - 如果按钮父容器有
overflow: hidden,且没设position: relative,伪元素会被裁掉一半
JS 怎么精准算出点击坐标并传给 CSS
核心就三步:获取按钮边界、减去左上角偏移、转成相对坐标。不要用 e.offsetX/e.offsetY——它们在缩放或滚动页面时不准。
- 用
e.clientX - e.target.getBoundingClientRect().left算 X,e.clientY - e.target.getBoundingClientRect().top算 Y - 创建
span.ripple后,立刻设ripple.style.left = x + 'px'和ripple.style.top = y + 'px' - 关键一步:加
ripple.style.transformOrigin = `${x}px ${y}px`,否则scale()还是从中心扩 - 直径建议设为
Math.max(buttonWidth, buttonHeight) * 2,确保角落点击也能盖满整个按钮
CSS 动画必须用 transform + opacity 组合
任何用 width/height 或 top/left 做扩散的写法,在中低端安卓机上都会卡顿。浏览器只对这两个属性做 GPU 加速。
- 动画帧里只改
transform: scale(0)→scale(3)和opacity: 0.4→0 - 必须加
will-change: transform, opacity,否则首次动画可能掉帧 - 避免在动画中改
border-radius或box-shadow,它们会触发重绘,拖慢 60fps 边界设备 - 动画时长控制在
400ms–600ms,太短像抽搐,太长用户会误以为没点上
移动端必须监听 touchstart 而不是 click
click 在 iOS 上有 300ms 延迟,Android 部分机型 touch 后不触发 click。直接监听 touchstart 才能保证即时反馈。
- 用
e.touches[0].clientX替代e.clientX,同理取clientY - 记得加
touch-action: manipulation到按钮上,禁用双指缩放等干扰手势 - 如果同时支持鼠标和触摸,可以用
if ('ontouchstart' in window)分支处理 - 别忘了给按钮加
position: relative和overflow: hidden,这是 ripple 定位和裁剪的基础
最易被忽略的一点:每次点击都要新建 span 并在 animationend 后 remove(),DOM 泄漏比卡顿更隐蔽——尤其高频点击场景下,几百个残留 span 会让页面越来越慢。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











