波纹效果本质是伪元素+动画+transform,用::after生成圆形遮罩并scale放大,Sass统一控制变量,JS触发is-rippling类实现交互。

波纹效果的本质是伪元素 + 动画 + transform
点击波纹(Ripple)不是靠 JS 画圆,而是用 ::after 伪元素动态生成一个圆形遮罩,再用 transform: scale() 从中心点放大、透明度渐变收尾。Sass 的价值不在“实现动画”,而在统一控制尺寸、颜色、持续时间这些变量,避免每个按钮都写一遍重复的 @keyframes 和 transition。
关键约束:伪元素必须设 position: absolute,父容器得是 position: relative,否则波纹会脱出边界;而且不能用 width/height 动画——性能差,还容易触发重排。
用 @mixin ripple 封装时必须传入颜色和尺寸参数
硬编码 $color: rgba(255,255,255,0.3) 看似省事,但深色主题按钮需要浅色波纹,禁用态又要灰掉,不传参就只能复制 mixin 或后期覆盖。尺寸同理:$size 决定波纹最大半径,它应该和按钮高度相关(比如 max(100px, 2 * $button-height)),而不是固定 200px。
-
@mixin ripple($color, $size: 200px, $duration: 0.5s)—— 三个参数都设默认值,但调用时至少传$color - 内部用
abs($size)防止负值传入导致scale()异常 - 动画曲线建议用
cubic-bezier(0.25, 0.46, 0.45, 0.94),比ease-out更自然
绑定事件要靠 JS 触发 .is-rippling 类,不是纯 CSS
Sass 只管样式,无法监听点击。必须用 JS 在 mousedown 时加类,在 animationend 后移除。常见错误是直接监听 click——波纹还没开始放缩就移除了类,动画被中断;或者忘了清空旧的 ::after,导致多次点击堆叠多个伪元素。
- JS 只需操作类名:
el.classList.add('is-rippling'),Sass 里写.btn.is-rippling::after即可 - 务必在
animationend回调里用el.classList.remove('is-rippling'),别依赖setTimeout - 如果按钮有
disabled属性,JS 层要提前 return,否则 disabled 按钮也能触发波纹
移动端点击延迟和多点触控会破坏波纹定位
PC 上用 clientX/clientY 算偏移很稳,但移动端 touchstart 的 touches[0] 坐标才是真实触点;更麻烦的是,iOS Safari 默认 300ms 点击延迟,mousedown 触发太晚,波纹看起来“卡一下”。另外,双指点击会触发两次 touchstart,可能生成两个波纹。
- 优先监听
touchstart,降级到mousedown;用event.touches?.[0] || event兼容取坐标 - 给按钮加
touch-action: manipulation,消除 iOS 300ms 延迟 - 在 JS 中加节流:
if (el.rippling) return; el.rippling = true;,动画结束再置false
CSS 波纹的复杂点从来不在动画本身,而在于坐标计算的时机、伪元素的层叠上下文、以及移动端事件模型和 PC 的差异。少一个 position: relative,或 JS 里漏掉一次 remove,效果就断掉。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











