
为什么直接用 :active 无法实现精准涟漪
因为 :active 伪类不感知点击坐标,只能触发全局样式变化;涟漪必须从鼠标/手指落点中心扩散,否则视觉上会“偏移”或“卡顿”。CSS 变量本身不响应事件,得靠 JavaScript 注入坐标值,再由 CSS 动画读取——这是唯一能兼顾性能与精度的路径。
如何用 getBoundingClientRect() 算出相对于按钮左上角的点击偏移
关键不是屏幕坐标,而是相对于按钮自身的 x 和 y。漏掉这步换算,涟漪圆心就会错位(比如点在右下角,却从左上角开始扩散)。
- 先用
event.clientX/event.clientY获取视口坐标 - 再调用按钮元素的
getBoundingClientRect(),拿到left、top、width、height - 计算:
x = event.clientX - rect.left,y = event.clientY - rect.top - 为防小数导致渲染模糊,建议四舍五入:
Math.round(x)
如何把坐标存进 CSS 变量并触发动画
CSS 变量必须写在元素自身(不能只写在 :root),否则动画无法绑定到具体点击位置。同时要重置前一次的变量值,否则连续点击会复用旧坐标。
- JS 中设置:
button.style.setProperty('--ripple-x', x + 'px') - 同理设置
--ripple-y,别忘了单位(px) - CSS 动画需监听这两个变量变化,但变量本身不触发重绘——所以得靠添加临时 class 或切换
opacity来强制触发 - 推荐做法:点击后立即加 class(如
ripple-active),动画定义在该 class 下,用var(--ripple-x)定位::after圆形伪元素
button::after {
content: '';
position: absolute;
border-radius: 50%;
background: rgba(255,255,255,0.6);
transform: translate(-50%, -50%);
left: var(--ripple-x);
top: var(--ripple-y);
width: 0; height: 0;
transition: width 0.6s, height 0.6s, opacity 0.6s;
}
button.ripple-active::after {
width: 200px; height: 200px; opacity: 0;
}
移动端点击坐标不准?记得处理 touchstart 和 clientX/Y 兼容性
移动端 event.clientX 在某些浏览器(如 Safari iOS)中可能返回 0,必须改用 touches[0].clientX。而且 touch 事件默认不冒泡,别忘了给按钮加 touch-action: manipulation 提升响应速度。
- 统一入口:监听
mousedown和touchstart,用event.touches?.[0] || event取坐标 - 避免重复触发:对同一按钮,一次点击只运行一次动画逻辑(可用
pointer-events: none临时禁用,动画结束后恢复) - 真机调试时,检查是否因
user-select: none或外部preventDefault干扰了 touch 坐标获取
CSS 变量只是“传参管道”,真正决定涟漪是否自然的是坐标换算精度、伪元素定位方式、以及动画 timing-function 的选择——贝塞尔曲线比线性更接近物理水波衰减,这点容易被跳过。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











