用 radial-gradient 实现老电影扫描效果需:中心透明渐变至边缘不透明的软边圆环遮罩,配合 cubic-bezier 缓动、±1px translate 抖动及 scale 扩张动画,并用 mask 层叠加,禁用 clip-path,必加 will-change: transform 保性能。

用 radial-gradient 模拟扫描圆环的起点和形状
老电影倒计时那种从中心向外扩散、边缘带轻微抖动的“光栅扫过”效果,本质不是真的逐行扫描,而是用一个动态扩张的圆形遮罩模拟视觉错觉。关键在于遮罩本身要透明中心 + 不透明边缘(或反之),且边缘不能太硬——radial-gradient 是最轻量、兼容性最好的实现方式。
常见错误是直接写 radial-gradient(circle, black 0%, black 50%, transparent 50%),结果边缘生硬、没有“晕开感”,扫过时像盖章而不是扫描。
- 正确做法:用多层色标制造软边,比如
radial-gradient(circle, transparent 40%, rgba(0,0,0,0.1) 45%, rgba(0,0,0,0.8) 55%, black 60%) - 起始半径必须为
0%或接近0%,否则动画一开始就有“洞” - 别用
ellipse——老电影扫描是正圆,拉伸会失真
@keyframes 动画中控制缩放与抖动的关键节奏
纯线性 scale 扩张看着机械,真实胶片扫描有轻微加速、停顿和微抖。CSS 动画里不需要 JS 插帧,靠 animation-timing-function 和关键帧偏移就能逼近。
典型错误是把抖动写成独立动画叠加,导致层级错乱或性能掉帧;或者用 transform: rotate() 假抖动,方向不对。
立即学习“前端免费学习笔记(深入)”;
- 抖动必须用
transform: translate()的 XY 微偏移,幅度控制在±1px内,频率约每 0.1s 一次 - 主扫描用
cubic-bezier(0.3, 0, 0.7, 1)模拟先慢后快再微顿 - 关键帧建议分三段:
0% { scale(0) translate(0, 0) }→95% { scale(2) translate(-0.5px, 0.3px) }→100% { scale(2.2) translate(0, 0) },收尾回正能强化“扫完”感
用 mask 或 background-blend-mode 实现安全的图层叠加
扫描效果必须作用于视频或图片内容之上,但直接改原图 opacity 或套 filter 会污染整层。现代方案首选 mask,兼容性差时降级用 background-blend-mode。
容易踩的坑是误用 clip-path——它裁剪的是容器区域,不是“透出内容”,无法做出中心亮、边缘暗的扫描感。
- 推荐结构:
div包裹目标媒体,自身设mask: radial-gradient(...),动画只驱动 mask 的scale - 如需支持 Safari 15.4 以下,改用两层:底层放图,上层用
background: radial-gradient(...)+background-blend-mode: multiply - 切记给 mask 层加
will-change: transform,否则 iOS 上动画卡顿明显
兼容性与性能陷阱:哪些 CSS 属性真不能省
这个动画看似简单,但在低端安卓机或旧版 Safari 上极易掉帧甚至闪退,问题常出在“以为可以省”的地方。
最常被砍掉的是 transform: translateZ(0) 或 will-change,结果动画跑在主线程,一卡全卡。
- 必须保留
transform: scale()而非width/height——后者触发布局重排 -
radial-gradient的颜色停止点别超过 5 个,Safari 对复杂渐变解析慢 - 动画时长建议设为
2.4s(老电影标准倒计时节奏),太短看不出扫描,太长显拖沓 - 如果用于视频,确保
video元素设了object-fit: cover,否则 mask 缩放会错位
真正难的不是写出第一版效果,而是让抖动不飘、边缘不锯齿、在 iPhone SE 上也不掉帧——这些细节没单独调试过,上线准出问题。









