用 mask-image 做鼠标点击涟漪效果可行,但需配合 mask-position 动画与 js 动态计算坐标,因 mask-image 本身不可过渡,safari 和 firefox 对 mask-position 动画支持不稳定,纯 css 无法实现跨浏览器平滑涟漪。

直接说结论:用 mask-image 做鼠标点击涟漪效果可行,但必须配合 mask-position 动画 + JS 动态计算坐标,且 Safari 和 Firefox 对 mask-position 的动画支持不稳定——别指望纯 CSS 实现跨浏览器平滑涟漪。
为什么不能只用 mask-image: radial-gradient() 静态写死?
很多人一上来就写:mask-image: radial-gradient(circle at 50% 50%, white, transparent),再加个 transition: mask-image 0.3s——这完全无效。mask-image 本身不可过渡(transition),浏览器不支持对渐变函数做插值动画。你看到的“变化”,其实是整个遮罩图被替换,没有中间帧,视觉上就是突变或卡顿。
真正能动画的只有这些属性:mask-position、mask-size、mask-repeat(仅部分值)。所以涟漪必须靠「固定形状遮罩 + 移动位置 + 扩大尺寸」来模拟。
- 遮罩图用固定大小的圆形 SVG 或
radial-gradient(white, transparent)(注意:必须是white起始,不是black) - JS 拿到点击
clientX/clientY,换算成相对于元素左上角的百分比坐标,设为mask-position - 同时用
mask-size从0动画到足够覆盖全元素的尺寸(比如300%) - 动画结束后重置
mask-size和mask-position,否则后续点击会叠加
mask-position 坐标怎么算才准?别被 offsetLeft 坑了
元素若有 padding、border、transform,或者父级用了 scroll,直接用 e.clientX - element.offsetLeft 会偏移。正确做法是用 element.getBoundingClientRect():
const rect = element.getBoundingClientRect(); const x = e.clientX - rect.left; const y = e.clientY - rect.top;
然后转成百分比传给 mask-position:
element.style.maskPosition = `${x}px ${y}px`;
注意:mask-position 默认单位是像素,不是百分比;设成 "50% 50%" 是居中,但点击点要精准定位,必须用 px 值。另外,Safari 对非整数像素的 mask-position 动画有渲染抖动,建议四舍五入到整数。
PigX UI Pro 前端开发指南 - Vue 3 + TypeScript + Element Plus。当用户提到 PigX UI、PigX 前端、lgb-mgui 项目、Vue 3 企业级后台开发、Element Plus 后台开发时使用此技能。
Firefox 和 Safari 的兼容性雷区
Firefox 要求 mask-image 的 SVG 必须带 <mask></mask> 标签且内部图形 fill="white";Safari 在 transform 容器里对 mask-position 解析错位——这两个问题会让涟漪在对应浏览器里偏移、消失或不动。
- 降级方案:用
@supports (mask-image: none)包裹涟漪逻辑,不支持的浏览器 fallback 到clip-path: circle(0 at ...) - 避免在
transform: scale()或rotate()的父元素里触发涟漪,改用position: relative+top/left模拟位移 - SVG 遮罩必须 inline 或同源,
url("mask.svg")在 Firefox 下若没配 CORS 会静默失败
性能关键:别让涟漪触发重排
每次点击都设置 style.maskPosition 和 style.maskSize 是安全的(属于合成属性),但如果你在动画中同时改 width、height 或 margin,就会强制触发 layout —— 涟漪卡顿立马出现。
更隐蔽的问题是:如果元素用了 will-change: transform,但没实际做 transform,Chrome 会误判合成层,导致 mask 渲染异常。稳妥做法是只对涟漪容器加:
element { will-change: mask-position, mask-size; }
且动画一结束立刻清除:element.style.willChange = "auto"。否则长页面滚动时内存占用飙升。
最常被忽略的一点:涟漪动画时间别设太短(如 0.15s),低端设备跟不上帧率,看起来像没动;也别太长(>0.4s),用户会觉得响应迟钝。实测 0.25s 是多数场景的平衡点。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










