扫光效果本质是用mask-image配合linear-gradient定义透光“光条”,通过mask-size缩窄、mask-position动画实现移动;需设mask-mode、扩展渐变过渡区间防毛刺,并注意浏览器兼容性与交互重置。

mask-image 配合 linear-gradient 实现扫光起点
扫光效果本质是让一个高亮区域从左到右(或任意方向)划过文字,而 mask-image 控制可见区域,linear-gradient 定义透明到不透明的过渡带就是那个“光条”。关键不是用图片做遮罩,而是用渐变本身——它天然支持动画。
常见错误是直接写 mask-image: linear-gradient(to right, transparent, #000, transparent),但这样默认生成的是 100% 宽高的渐变,不会“扫”;必须配合 mask-size 和 mask-position 手动缩窄光条宽度,并驱动 mask-position 移动。
实操建议:
-
mask-image用linear-gradient(to right, transparent, black, transparent),中间黑区即为透光部分(注意:mask 中黑色 = 完全显示,透明 = 完全隐藏) -
mask-size设为例如200% 100%,让渐变水平拉伸,确保光条能平滑横移 -
mask-position初始设为-100% 0,动画终点设为200% 0,实现从左外进入、扫过、再向右外离开 - 务必加
mask-mode: luminance或mask-mode: alpha明确解析方式,避免 Safari 等浏览器默认行为不一致
动画触发需搭配 mask-repeat 和 transform 兼容性兜底
纯 mask-position 动画在部分旧版 Chrome 和 Safari 上有闪烁或卡顿,尤其文字重绘频繁时。这不是写法错,而是 mask 渲染管线未充分优化。
立即学习“前端免费学习笔记(深入)”;
更稳的做法是把光条做成独立伪元素,用 transform: translateX() 移动它,再通过 mask 把它“投射”到文字上。但这要求父容器设 mask,子伪元素设 mask-image 并用 mask-composite: intersect(支持度有限),所以实际中更推荐简化路径:
- 对现代浏览器(Chrome 110+、Firefox 115+、Safari 16.4+),坚持用
@keyframes动画mask-position,性能足够 - 需要兼容 Safari ≤15.6 时,改用
background+background-clip: text+text-fill-color: transparent模拟,虽然不算 mask,但视觉一致且稳定 - 避免同时设置
mask-repeat: no-repeat和动画——某些版本 Safari 会忽略mask-position变化,删掉这行反而正常
文字抗锯齿与 mask 颜色值的隐含约定
很多人调不出干净的光边,是因为没意识到 mask 的 luminance 模式下,灰度值决定透明度:纯黑(#000)= 100% 显示,纯白(#fff)= 0% 显示,灰色是中间态。而文字本身有抗锯齿边缘(半透明像素),如果 mask 渐变过渡太硬(比如 transparent, black, transparent 无缓冲),边缘会出现毛刺或断层。
解决方法很具体:
- 把渐变写成
linear-gradient(to right, transparent 0%, transparent 30%, black 40%, black 60%, transparent 70%, transparent 100%),人为扩展过渡区间 - 不要用
rgba(0,0,0,0.8)这类非纯黑值——luminance 模式下它会被转为灰度,导致本该全显的区域变半透 - 确保文字
font-smoothing: antialiased或-webkit-font-smoothing: antialiased开启,否则 mask 对齐时容易漏像素
动画循环与交互中断的 reset 逻辑
扫光动画常用于 hover 提示,用户快速进出时,CSS 动画不会自动重置,可能卡在中间帧或反向播放,造成光条停在文字中央。这不是 bug,是 CSS 动画的自然行为。
必须手动控制:
- hover 触发时用
animation: shine 2s ease-out forwards,forwards保证结束帧保留 - 但更可靠的是用 JS 监听
mouseenter,每次触发前先移除动画类,再重加——避免连续 hover 积累多个动画实例 - 若需点击激活后常驻高亮,别用
infinite,改用 JS 控制mask-position的实时值,例如element.style.maskPosition = '50% 0' - 注意:
mask-position是可被 JS 直接设置的 CSS 属性,无需依赖getComputedStyle回读,更新及时
真正难的不是写出第一版扫光,而是当文字字号响应式变化、容器宽高不定、又叠加了 backdrop-filter 模糊背景时,mask 的尺寸比例和位移基准会悄悄偏移——这时候得回到 mask-size 和 mask-position 的百分比锚点逻辑重新校准,而不是盲目调数值。


















