纯css波纹只能居中扩散,无法实现“点哪炸哪”;必须用js监听touchstart获取坐标、动态设置--x/--y变量,并配合position: relative和overflow: hidden等css属性才能精准定位与清理。

纯 CSS 波纹只能居中扩散,别指望“点哪炸哪”
Bootstrap 按钮本身不带波纹动画,官方 CSS 里压根没有 @keyframes ripple,也没有 .ripple 类。你看到的居中波纹效果,全是靠 ::after 伪元素 + :active 状态硬凑出来的——它无法读取 clientX/clientY,所以无论鼠标点在按钮左上角还是右下角,波纹永远从 top: 50%; left: 50% 开始放大。
这种方案在桌面端能用,但 iOS Safari 对 :active 支持极差:要么不触发,要么延迟 300ms;移动端基本不可靠。如果项目要求 WCAG 可访问性或真实点击反馈,这方案就该直接排除。
必须加的两个 CSS 属性:position: relative 和 overflow: hidden
漏掉任一个,波纹就会飘出按钮甚至页面外。这不是可选项,是生效前提:
-
position: relative必须加在按钮自身上,否则::after的绝对定位会相对于最近的定位祖先计算,位置完全错乱 -
overflow: hidden也必须加在按钮上(不是父容器),否则波纹放大后直接溢出边界,视觉上像“炸开”了整个页面 - 别给
::after设width/height动画——会触发重排,卡顿明显;只用transform: scale()+opacity,走 GPU 加速 -
transform-origin: center必须显式声明,否则缩放中心偏移,波纹歪斜变形
移动端必须用 JS 监听 touchstart,不能只靠 :active
iOS 和安卓触摸设备上,:active 基本失效。真要让波纹响应点击,得用 JS 绑定 touchstart(不是 click)并动态加 class:
- 监听
touchstart时取e.touches[0].clientX,兼容性比clientX更稳 - 给按钮临时加 class,比如
btn-ripple-active,对应 CSS 写成.btn-ripple-active::after { transform: translate(-50%, -50%) scale(3); opacity: 1; } -
touchend后立刻用classList.remove('btn-ripple-active')清理,否则波纹残留、连点失效 - 别把
ontouchstart="this.classList.add()"写进 HTML——维护困难,且无法复用逻辑
JS 才能实现“点哪炸哪”,坐标计算三步不能错
想让波纹精准锚定点击位置,必须 JS 计算并注入 CSS 变量或内联样式。核心就三步:
- 调用
btn.getBoundingClientRect()获取按钮视口坐标 - 用
e.clientX - rect.left和e.clientY - rect.top算出相对坐标(不能用offsetX/offsetY,Firefox 不支持) - 把结果写进
--x/--y自定义属性,再在::after里用left: var(--x)定位 - 如果按钮父级有
transform(比如scale(0.95)),getBoundingClientRect()会失真——此时要把波纹元素挂到那个父级下,并重新计算坐标 - 动画结束必须监听
animationend,而不是setTimeout;否则快速连点会导致 DOM 节点堆积
真正难的不是写动画,而是让每个水波纹都毫秒级对准点击点。坐标、定位上下文、清理逻辑,三者缺一不可——少一个,效果就断在用户手指落下的那一瞬。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











