纯CSS无法实现从真实点击位置出发的水波纹,因:active伪类不提供坐标信息且无法动态设置transform-origin;必须用JS计算e.clientX/clientY与getBoundingClientRect的相对偏移,存入CSS自定义属性,再由CSS通过var(--x)/var(--y)定位::after伪元素并以该点为原点执行scale和opacity动画。

纯 CSS 做不出真正从点击位置出发的水波纹,:active 没法读取坐标,伪元素也动不了 transform-origin。要“指哪打哪”,必须用 JS 算坐标 + CSS 控制动画——但 JS 只管定位,动画全交给 CSS,这样才不卡顿。
为什么 :active::after 总是从按钮中心炸开
因为 :active 是个布尔状态,不带任何坐标信息;top: 50%; left: 50% 永远锚定父容器中心,和你点在哪毫无关系。更麻烦的是::active 在移动端很多 WebView 里根本不触发,松手就结束,撑不住 400ms 动画。伪元素也没法动态设 transform-origin,而水波纹必须以点击点为缩放原点。
- 别写
button:active { background: radial-gradient(circle at center, ...); }——这会覆盖原有背景,且永远居中 - 忘了给按钮加
position: relative?波纹会飞到页面左上角 -
::after没加pointer-events: none?它会拦截后续点击
JS 怎么算出真实点击坐标并喂给 CSS
核心就三步:获取按钮边界、减去左上角偏移、转成相对坐标。别用 e.offsetX/e.offsetY——页面缩放或滚动时它们会飘。
- 正确写法:
e.clientX - e.target.getBoundingClientRect().left算 X,同理算 Y - 移动端要用
e.touches[0].clientX替代e.clientX,否则真机点不动 - 把坐标存进自定义属性:
button.style.setProperty('--x', x + 'px')和--y - CSS 里直接读:
left: var(--x); top: var(--y); transform-origin: var(--x) var(--y);
CSS 动画怎么写才不卡、不溢出、不重叠
动画逻辑全丢给 CSS,JS 只负责“下单”,性能才稳。关键不是改 width/height(触发布局重排),而是用 transform: scale() + opacity。
立即学习“前端免费学习笔记(深入)”;
- 按钮必须设
position: relative; overflow: hidden; -
::after要有content: ""、position: absolute、border-radius: 50% - 初始状态:
transform: translate(-50%, -50%) scale(0); opacity: 0.3; - 动画终点:
scale(3)或更大力度(建议用Math.max(width, height) * 2算直径) - 动画时长控制在
300ms–400ms,太短感知弱,太长拖操作节奏
最易被忽略的是:移动端 touchstart 必须设 { passive: true },否则滚动会卡;每次点击前得先 remove() 已存在的波纹节点,否则快速连点会堆叠卡住;深色模式下 --ripple-color 得单独配,不然白底黑字里涟漪几乎看不见。



















