:active波纹总从按钮正中间开始是因为css无法读取点击坐标,:active是状态伪类而非事件处理器,只能通过transform-origin:center+scale()实现固定圆心缩放动画。

纯 CSS 的 :active 波纹只能从中心炸开,做不到点击哪就从哪扩散;真要落点定位,必须用 JavaScript 动态算坐标并插入元素。
为什么 :active 波纹总从按钮正中间开始
CSS 无法读取鼠标或手指的点击坐标,:active 是个状态伪类,不是事件处理器。它只能触发预设动画,圆心固定在 left: 50%、top: 50%。看起来像“水波”,其实是视觉欺骗——靠 transform-origin: center + scale() 实现的缩放动画。
- 如果按钮是
<div> 或其他非表单元素,先加 <code>tabindex="0"或改用语义化<button></button>,否则:active根本不触发 - 别指望
cursor: pointer能激活:active,那是桌面端视觉提示,和可激活状态无关 - 移动端 Safari 对无事件监听的页面,
:active可能直接失效,加个空onclick="void(0)"有时能救急 - 监听
pointerdown,不是click或touchstart单独绑——一个事件覆盖所有设备,且passive: false必须带上,否则 iOS Safari 会忽略preventDefault() - 必须调用
e.target.getBoundingClientRect(),再算:const x = e.clientX - rect.left、const y = e.clientY - rect.top(触屏用e.touches[0]) - 插入的
<span></span>要设position: absolute、border-radius: 50%、transform: translate(-50%, -50%) scale(0)、pointer-events: none,否则挡点击 - 动画只过渡
transform和opacity,禁用transition: all - 务必加
overflow: hidden到父容器,否则波纹放大后会撑出圆角边界 - 动画结束必须清理 DOM:
ripple.addEventListener('animationend', () => ripple.remove()),不然连点几十次内存就涨上去了 - 移动端加
touch-action: manipulation到按钮自身,不是父级,否则 Safari 还是等双指缩放判定
用 JS 实现点击位置精准波纹的关键三步
核心是:获取容器视口位置 → 算出相对偏移 → 插入带 transform: scale() 的圆形元素。不能用 event.offsetX(IE 不支持、嵌套 transform 下失真),也不能用 clientX - offsetLeft(滚动时错)。
文章转信息图。将文章/笔记转化为手机可读的 HTML 信息图,自动匹配视觉风格。触发场景:文章转图、笔记转图、信息图、转小红书图、做张图、可视化这篇文章、文生图。
动画性能和兼容性最容易翻车的地方
改 width/height 触发重排,低端机掉帧严重;Safari 15.4+ 才支持 clip-path 动画;而 transform: scale() 走合成层,60fps 更稳。
真正难的不是写动画,而是每次点击都得重新校准坐标、防重复插入、清旧留新、兼顾键盘焦点和屏幕阅读器——这些细节漏一个,波纹就卡、偏、闪、堆满内存。










