水波纹效果必须用::after伪元素实现,通过javascript记录点击坐标并设置left/top,再触发transform:scale()和opacity动画;关键点包括position:absolute、pointer-events:none、border-radius:50%、父容器position:relative,且动画仅限transform和opacity以避免重排。

水波纹效果必须用 ::after 伪元素实现
直接在按钮上加 background 动画会覆盖原有样式,且无法定位到点击坐标。真正可行的方案是:用 ::after 伪元素作为水波纹载体,通过 JavaScript 记录点击位置并设置 left、top,再触发 transform: scale() 动画。
关键点在于伪元素需满足:position: absolute、pointer-events: none(避免干扰后续点击)、border-radius: 50%,且父容器(按钮)必须是 position: relative。
- 不要用
::before—— 容易和图标/文字图层冲突 - 伪元素初始状态要
opacity: 0,动画结束时用opacity: 0+transform: scale(4)实现淡出扩散 - 动画时长建议
600ms,太短感知不到,太长显得卡顿
JavaScript 获取点击坐标要减去元素偏移
event.clientX 和 event.clientY 返回的是视口坐标,必须减去按钮左上角相对于视口的位置,才能让水波纹中心精准落在点击处。
正确做法是用 getBoundingClientRect() 获取按钮位置,再计算相对偏移:
button.addEventListener('click', (e) => {
const rect = e.currentTarget.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
const ripple = document.createElement('span');
ripple.style.left = `${x}px`;
ripple.style.top = `${y}px`;
// … 后续插入和动画逻辑
});
- 别用
e.offsetX/e.offsetY—— 在某些浏览器中不兼容,且在有 padding 的按钮里会偏差 - 动态创建
<span></span>比复用一个伪元素更可控;伪元素无法动态设left/top(除非用 CSS 变量,但兼容性差) - 每次点击后记得
ripple.remove(),否则 DOM 节点堆积
动画性能必须用 transform 和 opacity
如果对 width、height 或 left/top 做动画,会触发频繁重排(layout),严重掉帧。水波纹必须只动 transform: scale() 和 opacity。
CSS 动画定义示例:
@keyframes ripple {
0% {
transform: scale(0);
opacity: 0.7;
}
100% {
transform: scale(4);
opacity: 0;
}
}
- 动画时间函数推荐
ease-out,模拟真实水波衰减感 - 务必加
will-change: transform到水波纹元素(仅动画期间),让浏览器提前优化图层 - 移动端要注意
touchstart替代click,否则有 300ms 延迟
圆角按钮的水波纹要适配 overflow: hidden
带 border-radius 的按钮,水波纹扩散时会被裁剪——这是正常行为,但若裁剪过度(比如小圆角+大扩散),视觉上像“没动”。解决方法不是去掉 overflow: hidden,而是控制最大扩散半径。
- 计算最大半径:
Math.max(rect.width, rect.height) * 1.2,然后设为width/height上限 - 水波纹元素的
border-radius必须设为50%,哪怕父容器是4px圆角,否则边缘发虚 - 深色背景上水波纹透明度建议从
0.5起始,浅色背景用0.3,避免过曝
最易被忽略的是:水波纹动画结束后未清理 transform 值,导致后续 JS 获取 getBoundingClientRect() 出错;每次动画完成回调里务必重置或移除元素。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











