真实水波纹无法用:active实现,必须通过getboundingclientrect()获取点击坐标,结合::after伪元素与radial-gradient(circle at xpx ypx)实现以点击点为圆心的圆形扩散动画。

用 :active 伪类根本做不出真实水波纹
直接写 button:active { transform: scale(0.95); } 只能压扁按钮,不是水波纹。水波纹必须满足三个硬性条件:以点击点为圆心、圆形扩散、动画结束后透明消失。而 :active 无法获取坐标、不能创建新元素、也不支持径向渐变或动态定位——它只是个状态开关,连圆心在哪都不知道。
常见错误是把 :active 和水波纹混为一谈,结果点了半天只看到按钮缩放或背景色闪一下,圆没出来,位置还总偏。
getBoundingClientRect() 是定位水波纹圆心的唯一可靠方式
所有“鼠标点哪,波纹就从哪起”的效果,都依赖这个 API 算出相对偏移。不调它,clientX/clientY 直接赋值给 left/top 在滚动页、缩放视口、父容器有 transform 时必错位。
- 先执行
const rect = target.getBoundingClientRect() - 再算
const x = e.clientX - rect.left和const y = e.clientY - rect.top - 这两个值才是目标元素内部的像素坐标,可安全用于
left/top或 CSS 自定义属性
伪元素 + JS 注入坐标比动态创建 DOM 更轻量也更稳
用 ::after 承载水波纹,JS 只负责设置 left、top 和触发动画类,CSS 主导渲染——这样既避免频繁增删节点,又绕开 layout thrashing 风险。
关键配置不能漏:
- 目标元素必须设
position: relative,否则::after会相对于body定位 -
::after必须含content: ""、position: absolute、border-radius: 50%、pointer-events: none - 初始状态用
opacity: 0; transform: scale(0),别用display: none - 动画结束靠
animation-fill-mode: forwards固定终态,不用setTimeout清理
radial-gradient 圆心写法必须用 px 单位,不能用百分比
想让渐变圆精准锚定在点击点,CSS 背景里的 radial-gradient(circle at Xpx Ypx, ...) 中的 X 和 Y 必须是像素值,且是相对于父容器左上角的绝对偏移。写成 at 50% 50% 就永远居中,和点击位置无关。
如果用 JS 创建临时 <span></span> 节点替代伪元素,也要注意:节点得 append 到目标元素内部(不是 document.body),且该元素已设 position: relative,否则 left/top 会失准。
并发点击时,每个水波纹都是独立节点或独立伪元素实例,但圆心计算逻辑不能复用上一次的 left/top ——每次点击都得重算,否则第二下还在旧位置扩散。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











