波纹效果必须用::after或::before伪元素实现,需配合JS记录点击坐标并存入CSS变量,再通过transform:scale()实现平滑圆形扩散动画。

波纹效果必须用 ::after 或 ::before 伪元素实现
直接在按钮上加背景动画会覆盖原有样式,且无法独立控制圆形扩散;伪元素能脱离文档流、层叠可控、支持 transform: scale() 平滑缩放。关键点是:伪元素初始透明、无尺寸,点击时动态设置坐标和尺寸。
- 必须设
position: relative在按钮上,否则伪元素的absolute定位会相对于 body 偏移 - 伪元素需设
content: "",否则不渲染 -
border-radius: 50%是圆形基础,漏掉就变方块扩散 - 动画必须用
transform: scale(),不要用width/height动画——后者触发重排,卡顿明显
用 data-ripple-x 和 data-ripple-y 记录点击坐标
CSS 本身不能读取鼠标事件,所以必须靠 JS 把点击位置存进自定义属性,再用 CSS 变量接住。这是“纯 CSS 波纹”里唯一绕不开的 JS 步骤。
- JS 中获取坐标要减去按钮左上角偏移:
e.clientX - button.getBoundingClientRect().left - 存入时建议转成
px单位(如button.dataset.rippleX = (x) + "px"),避免 CSS 里还要做 calc - CSS 中用
left: var(--x)而不是left: attr(data-ripple-x)——attr()不支持单位运算,且兼容性差(Safari 不支持) - 务必在每次点击前清空旧伪元素:用
getComputedStyle(button).getPropertyValue('--x')判断是否存在,或直接button.style.setProperty('--x', '0px')重置
transition 的时间与缓动必须匹配扩散逻辑
波纹不是匀速放大,而是先快后慢,同时要兼顾视觉节奏和响应感。过渡太长像卡顿,太短像没反应。
- 推荐用
transition: transform 0.5s cubic-bezier(0.2, 0.8, 0.2, 1):比ease-out更自然,收尾更柔和 - 别给
opacity单独设 transition——它和transform合并在一条声明里更稳定(transition: transform 0.5s, opacity 0.5s) - 如果按钮有多个状态(如
:hover,:active),确保波纹动画不被覆盖:伪元素的z-index至少为 1,且:active里不要重置transform - 移动端要注意
touchstart替代click,否则有 300ms 延迟
IE 和旧 Safari 下伪元素定位会偏移
老浏览器对 getBoundingClientRect() 返回值处理不一致,尤其当按钮在缩放容器(transform: scale())或 iframe 里时,坐标计算容易失准。
立即学习“前端免费学习笔记(深入)”;
- 兼容方案:不用
getBoundingClientRect(),改用e.pageX - e.target.offsetLeft(但需确保父级无border或padding干扰) - Safari 14.1 之前不支持
attr()用于calc(),所以依赖 CSS 变量传参是唯一可靠路径 - 如果项目必须支持 IE11,放弃伪元素方案,改用 JS 创建
span插入并绝对定位——这时data-属性只是备用,核心逻辑仍在 JS - 测试时务必在真机上点按,模拟器的
clientX/Y常常不准
实际最难的不是写动画,是让每一次点击的坐标都精准落到按钮内容区中心——滚动、缩放、iframe、CSS transforms 都会悄悄扭曲这个“原点”。多数人调通一次就以为成了,结果上线后在某个安卓 WebView 里波纹总往右下角跑。


















