<p>必须用 getBoundingClientRect() 计算点击坐标,因 e.offsetX 在 IE 中不支持且在 transform、scale 或 iframe 中不可靠;需用 clientX - rect.left 算出相对按钮的 x 偏移,再通过 setProperty('--x', x + 'px') 写入带单位的 CSS 变量,配合伪元素 translate(-50%,-50%) 锚定波纹中心。</p>
纯 CSS 变量无法直接响应点击事件,必须靠 JS 把 e.clientX 和 e.clientY 转成按钮内坐标,再写入 --x / --y 才能驱动波纹从点击点扩散——漏掉这一步,波纹永远只从中心炸开。
getBoundingClientRect() 算坐标,不能直接用 e.offsetX
因为 e.offsetX 在 IE 中不支持,且在有 transform、scale 或 iframe 嵌套的容器里返回值不可靠。真实项目中 90% 的波纹偏移都源于没做坐标归一化。
button.getBoundingClientRect() 返回按钮左上角相对于视口的位置(left/top)e.clientX - rect.left 得到 X 方向相对按钮的偏移,Y 同理container.scrollTop(但多数情况只需减 rect.top)button.style.setProperty('--x', x + 'px'),单位必须显式带 px,否则 CSS 里 var(--x) 无法参与定位计算button::after 如何读取并精准锚定到 --x / --y
伪元素不能用 attr(data-ripple-x) 读取坐标——Safari 不支持,且不支持单位运算;唯一可靠方式是用 CSS 变量 + transform: translate(-50%, -50%) 把圆心拉到点击点。
position: absolute; top: 0; left: 0;,否则 var(--x) 会相对于内容盒而非边框盒解析left: var(--x); top: var(--y); transform: translate(-50%, -50%) scale(0);
border-radius: 50%,否则是方块扩散button.style.setProperty('--x', '0px'),否则连续点击时新波纹从旧位置起始仅靠 :active 触发波纹动画不可靠:移动端可能不触发,鼠标抬起过快会导致动画中断,且无法控制多点点击。
立即学习“前端免费学习笔记(深入)”;
is-rippling,CSS 写 .btn.is-rippling::after { transform: scale(1); opacity: 0; }
animationend 事件移除类,而不是用 setTimeout——浏览器动画帧可能延迟,setTimeout 容易误判if (button.rippling) return; button.rippling = true;,动画结束再置 false
pointer-events: none 到伪元素上,否则波纹层会拦截后续点击最常被忽略的是:伪元素插入后,第一帧渲染需要等待浏览器合成层就绪。若 JS 设置变量后立即触发动画,Safari 会出现首帧空白——必须用 requestAnimationFrame 包一层再更新类名或 scale 值,否则“点了没反应”不是代码问题,而是渲染流水线没对齐。