直接用 :active 无法实现点击波纹效果,因其仅控制按下瞬间样式、无动画和坐标定位能力;真正实现需 JavaScript 动态创建 ripple 元素、结合 getBoundingClientRect() 计算位置、CSS transform: scale() 动画及 animationend 后清理 DOM。

用 :active 做点击波纹,为什么点不动?
直接用 :active 无法做出扩散波纹——它只控制按下瞬间的样式,没有动画、没有定位、不记录点击坐标。你看到的“一闪而过”就是全部效果,连圆心都固定在元素中心,更别说响应式定位了。
真正能实现波纹的,是结合 JavaScript 动态创建 span 元素 + CSS 动画 + getBoundingClientRect() 计算坐标。伪类只是起点,不是解法。
波纹元素必须用 JS 动态插入,不能写死在 HTML 里
每个按钮点击位置不同,波纹圆心要实时计算;多个按钮共用同一套逻辑,但波纹必须独立存在、互不干扰。写死 <span class="ripple"></span> 在 HTML 里会导致:复用困难、位置错乱、动画叠加残留、无法清理。
- 每次点击前,用
document.createElement('span')新建一个ripple元素 - 用
event.clientX和event.clientY减去按钮左上角坐标,算出相对偏移 - 把
ripple的left和top设为该偏移值,并设position: absolute - 插入到按钮内部(
button.appendChild(ripple)),再触发动画
CSS 动画关键帧必须用 transform: scale(),别用 width/height
用 width 和 height 动画会触发重排(reflow),卡顿明显,尤其在低端设备上。而 transform: scale() 只走合成层,60fps 更稳。
立即学习“前端免费学习笔记(深入)”;
同时要注意:波纹默认透明度为 0,动画结束时设为 0,否则会残留半透明圆斑。
.ripple {
position: absolute;
border-radius: 50%;
background: rgba(0, 0, 0, 0.2);
transform: scale(0);
animation: ripple 600ms ease-out;
}
@keyframes ripple {
to {
transform: scale(4);
opacity: 0;
}
}
必须手动清理已播放完的波纹 DOM 节点
动画结束后,ripple 元素仍挂在 DOM 上,不断累积会导致内存占用上升、事件监听器泄漏、甚至页面卡死。Chrome DevTools 的 “Rendering” 面板里能看到大量残留 span。
- 监听
animationend事件,而不是靠定时器猜测时间 - 在回调里调用
ripple.remove()或ripple.parentElement.removeChild(ripple) - 加个兜底判断:
if (ripple.parentNode) ripple.remove(),防止异步时机错乱
波纹看似简单,但坐标计算、图层合成、DOM 清理这三处,任一漏掉都会让效果在真实项目中逐渐失灵。


















