纯 css 无法实现真实涟漪,因其不能获取鼠标点击坐标;必须用 javascript 计算点击点相对于按钮的偏移(clientx/y − getboundingclientrect),动态创建并定位波纹元素,配合 tailwind 自定义 ripple 动画及定时清理 dom。

为什么直接用 transition 和 ::after 无法实现真实涟漪?
因为涟漪需要从点击位置(而非元素中心)出发,动态生成圆形波纹并扩散,纯 CSS 无法读取鼠标坐标。常见错误是写个固定居中的 ::after 圆形 + scale 动画,结果所有按钮涟漪都从中间炸开,和点击点完全脱节。
必须靠 JavaScript 获取 clientX/clientY,再结合 Tailwind 的工具类动态插入带定位的波纹元素。
用 document.createElement + getBoundingClientRect() 定位波纹
核心是算出点击点相对于按钮左上角的偏移,再转成 left 和 top 值。不能直接用 event.pageX —— 它是相对于整个视口的坐标。
-
const rect = button.getBoundingClientRect()获取按钮在视口中的位置 -
const x = e.clientX - rect.left得到点击点相对按钮左侧距离 -
const y = e.clientY - rect.top同理得顶部距离 - 新创建的
span元素需添加absolute、rounded-full、bg-black/20等类,并内联style.left和style.top
示例关键代码:
const ripple = document.createElement('span');
ripple.className = 'absolute rounded-full bg-black/20 animate-ripple';
ripple.style.left = `${x}px`;
ripple.style.top = `${y}px`;
button.appendChild(ripple);
Tailwind 中必须手动注册 @keyframes ripple
Tailwind 默认不带涟漪动画,animate-ripple 是自定义名,需在 tailwind.config.js 的 theme.extend.animation 和 keyframes 里补全。漏掉这步,波纹只会瞬间出现、不扩散。
- 在
tailwind.config.js的theme.extend下加:
animation: {
ripple: 'ripple 600ms linear'
},
keyframes: theme => ({
ripple: {
'0%': { transform: 'scale(0)', opacity: '1' },
'100%': { transform: 'scale(4)', opacity: '0' }
}
})
注意:动画时长(600ms)要和实际移除 DOM 的时机对齐,否则波纹消失前就被 JS 删掉了。
点击后必须主动清理涟漪 DOM,否则内存泄漏
每个点击都新建一个 span,不删就会越积越多。不能依赖 CSS 动画结束事件(兼容性差),稳妥做法是用 setTimeout 在动画时长后移除。
- 在插入
ripple后立即加:
setTimeout(() => {
ripple.remove();
}, 600); // 必须和 keyframes 里的时长一致
更健壮的做法是监听 animationend 并检查 animationName === 'ripple',但 IE 不支持,所以多数项目仍用 setTimeout。
容易被忽略的是:如果用户快速连续点击,可能多个涟漪同时存在,但清理逻辑只管自己——这没问题,只要每个都独立计时、独立删除即可。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











