纯 CSS 的 :active 无法实现中心发散波纹,必须结合 JS 动态计算点击坐标并操控伪元素动画;需设置 position: relative、overflow: hidden 及 iOS 兼容样式 cursor: pointer 和 touch-action: manipulation。

用 CSS :active 实现最简波纹,但只在点击瞬间生效
纯 CSS 方案里,:active 是最直接的点击反馈入口,但它只能触发“按下态”,无法模拟从点击点向外扩散的波纹动画。适合按钮类元素,但不满足“中心发散”需求。
常见错误是直接给 div 或 a 加 :active { background: radial-gradient(...) } —— 这样波纹永远居中,且无法随点击位置变化。
- 必须用 JavaScript 捕获
clientX/clientY计算相对坐标 -
:active本身不支持transition到radial-gradient的尺寸变化,得靠伪元素 + 动画 - 移动端需额外处理
touchstart,否则 iOS Safari 点击无响应
用 ::after 伪元素 + JS 动态定位生成波纹层
核心思路:点击时动态创建一个圆形 ::after 伪元素,以点击坐标为圆心,初始半径为 0,然后用 transform: scale() 动画撑开,配合 opacity 淡出。
示例代码(作用于所有带 ripple 类的元素):
立即学习“前端免费学习笔记(深入)”;
.ripple {
position: relative;
overflow: hidden;
}
.ripple::after {
content: '';
position: absolute;
border-radius: 50%;
background: rgba(0,0,0,0.1);
transform: scale(0);
transform-origin: center;
transition: transform 0.6s, opacity 0.6s;
opacity: 0;
}
.ripple.ripple-active::after {
transform: scale(1);
opacity: 1;
}对应 JS 只需监听点击、计算偏移、加 class:
document.querySelectorAll('.ripple').forEach(el => {
el.addEventListener('click', (e) => {
const rect = el.getBoundingClientRect();
const x = e.clientX - rect.left - rect.width / 2;
const y = e.clientY - rect.top - rect.height / 2;
el.style.setProperty('--x', `${x}px`);
el.style.setProperty('--y', `${y}px`);
el.classList.add('ripple-active');
setTimeout(() => el.classList.remove('ripple-active'), 600);
});
});兼容性关键:iOS Safari 需要 cursor: pointer 和 touch-action: manipulation
没加这两条,iOS 上点击会延迟约 300ms,且波纹可能完全不触发。这不是 bug,是 Safari 对非可点击元素的主动拦截。
-
cursor: pointer告诉浏览器“这个元素可交互”,激活点击事件流 -
touch-action: manipulation禁用双指缩放和长按菜单,让click和touchstart正常冒泡 - 如果用
button或a标签,部分情况可省略,但自定义div必须显式声明
性能注意:避免在滚动容器或列表项上滥用
每个波纹都新建一层渲染,如果页面有几十个 .ripple 元素同时监听,频繁点击会造成重排压力,尤其低端 Android 设备。
- 优先用在主操作按钮(如
Submit、Menu),别铺满整个卡片区域 - 动画时长控制在 400–600ms,过长会拖慢用户感知节奏
- 用
will-change: transform提前提示浏览器优化,但仅对高频点击区域启用
真正难的不是写出波纹,而是判断哪里该有、哪里该去掉——比如表单输入框获得焦点时,再加波纹反而干扰用户。


















