mask-position动画动不起来,主因是漏设mask-size:100% 100%和mask-repeat: no-repeat;Safari需-webkit-mask-image+纯灰度渐变+mask-mode:luminance;Firefox暂不支持渐变遮罩动画,应改用SVG方案。

mask-image 擦除动画为什么动不起来
写了 mask-image: linear-gradient(to right, #fff, #000) 加上 transition: mask-position 0.5s,但图片纹丝不动——大概率是漏了 mask-size: 100% 100% 或 mask-repeat: no-repeat。默认 mask-repeat: repeat 会让渐变横向平铺成无数条细线,mask-position 动的只是其中一条,视觉上毫无变化。
必须确保遮罩图像在元素内“只有一份、铺满、不缩放失真”:
-
mask-size: 100% 100%强制拉伸渐变覆盖整个盒模型(注意:不是内容区) -
mask-repeat: no-repeat禁用重复,否则动画失去意义 -
mask-position: 0 0或具体值,作为动画起始锚点;不设会按 center 对齐,导致方向错乱 - 对
<img>直接加 mask 时,务必确认其父容器有明确宽高或aspect-ratio,否则 Safari 可能算出 0×0 遮罩区域
Safari 下 mask-position 动画完全没反应
Chrome 和 Firefox 能跑通的 mask-position: 0 0 → 100% 0,在 Safari 里静默失效——这不是 bug,是 Safari 的渲染策略:它只在 -webkit-mask-image 存在且渐变使用纯灰度(#000/#fff)时才启用 luminance 模式下的位置动画。
正确写法必须同时满足三项:
立即学习“前端免费学习笔记(深入)”;
- 显式声明
-webkit-mask-image,不能只靠无前缀版本 - 渐变必须用
#000和#fff,禁用transparent、rgba()、black/white别名(Safari 解析不稳定) - 补上
mask-mode: luminance,避免旧版 Chrome/Firefox 因默认行为差异导致跳帧或错位
示例关键段:
div {
-webkit-mask-image: linear-gradient(to right, #fff, #000);
mask-image: linear-gradient(to right, #fff, #000);
mask-mode: luminance;
mask-size: 100% 100%;
mask-repeat: no-repeat;
mask-position: 0 0;
transition: mask-position 0.6s ease-out;
}
div:hover {
mask-position: 100% 0;
}
如何实现从右向左的擦除揭示效果
想让图片像被“从右往左擦掉遮罩”一样逐渐显示?仅把渐变改成 to left 不够,因为 mask-position 默认居中,动画仍从中部展开。
真正起作用的是三者配合:
-
mask-image: linear-gradient(to left, #fff, #000)→ 白色在右、黑色在左,意味着右侧可见、左侧被盖住 -
mask-position: right→ 初始时遮罩右边缘对齐元素右边缘,此时整张图几乎全被黑色覆盖(仅最右像素可见) -
transition: mask-position 0.5s→ 动画中mask-position移向left,白色区域逐步向左推进,实现“擦除揭示”
注意:若用 @keyframes,需写成 from { mask-position: right; } to { mask-position: left; },不能只写数值百分比,Safari 对 mask-position: 100% 0 的解析不如关键词稳定。
Firefox 不支持 mask-image 渐变动画怎么办
截至 2026 年中,Firefox 仍把 mask-image: linear-gradient() 当作无效声明直接忽略——这不是兼容性问题,是尚未实现该特性。别指望加前缀或 polyfill 能绕过。
可行方案只有两个:
- 降级为 SVG 遮罩:用
<svg>内嵌<mask>+<linearGradient>,再通过mask-image: url(#myMask)引用。SVG 方案在 Firefox 中完整支持动画,但需额外 DOM 结构和 ID 绑定 - 放弃 Firefox 的遮罩动画,改用
clip-path+inset()或polygon()模拟类似擦除(边缘硬、无渐变过渡,但至少动得起来)
如果项目必须兼顾 Firefox 且要求柔边,建议优先考虑 SVG 方案,并把渐变定义在 <defs> 里复用,避免每个元素都塞一整段 SVG。
最容易被忽略的一点:mask 是作用在整个盒模型上的,padding、border 都算进遮罩范围。做擦除动画前,先用 outline: 1px solid red 看清元素真实尺寸边界——很多“动不了”其实是因为遮罩区域比预期小太多,动画根本没空间发生。


















