用 background-size + background-position 实现预加载动画,因不受布局影响、无重绘撕裂,且通过 calc(var(--ui-buffered)*-1) 驱动左移模拟缓冲延伸;需监听 loadedmetadata 后绑定 progress 事件,并兼容 iOS Safari 降级方案。

用 background-size 实现视频进度条的预加载动画,本质是模拟“已缓冲区域”的视觉反馈,不是控制播放位置——它和 video.buffered 有映射关系,但动画本身纯 CSS 驱动,不依赖 JS 更新 DOM。
为什么不用 width 动画?
视频进度条容器常嵌套在 flex 或 transform 父级中,width 动画会受父级布局影响:缩放时错位、flex 压缩时截断、iOS Safari 下偶发重绘撕裂。而 background-size 是绘制层属性,不受 layout 影响,且能配合 background-position 做无缝拉伸过渡。
background-size + background-position 的组合写法
关键不是单纯改 background-size,而是用它配合定位实现“从左向右铺满”的错觉:
- 背景图用
linear-gradient(to right, #4a90e2 0%, #4a90e2 100%)(单色即可,避免多色渐变在缩放时模糊) - 设
background-size: 0% 100%→background-size: 100% 100%动画,但这样会从左边缘开始“撑开”,视觉上像膨胀而非推进 - 真正平滑的做法是:固定背景图宽度(比如
200%),靠background-position左移来“露出”更多已缓冲部分:background: linear-gradient(to right, #4a90e2, #4a90e2) 0% 0% / 200% 100% no-repeat;
动画终点设为background-position: -100% 0%; - 必须加
transition: background-position 0.3s ease-out,不能只靠@keyframes——因为 JS 会频繁更新缓冲比例,CSS 过渡比帧动画更顺
如何把 video.buffered 映射到 background-position
JS 侧只需监听 progress 事件,读取 video.buffered.end(0) 并换算成百分比,再设置 CSS 变量:
立即学习“前端免费学习笔记(深入)”;
video.addEventListener('progress', () => {
const buffered = video.buffered.length ? video.buffered.end(0) : 0;
const percent = Math.min(100, (buffered / video.duration) * 100);
progressBar.style.setProperty('--ui-buffered', `${percent}%`);
});
CSS 里用变量驱动 position:
.progress-bar::before {
content: '';
position: absolute;
top: 0; left: 0; bottom: 0;
background: linear-gradient(to right, #4a90e2, #4a90e2) 0% 0% / 200% 100% no-repeat;
background-position: calc(var(--ui-buffered, 0%) * -1) 0;
transition: background-position 0.3s ease-out;
}
注意:calc(var(--ui-buffered) * -1) 是核心,负号让数值越大,背景越往左移,视觉上就是“缓冲区向右延伸”。
移动端 iOS Safari 的坑
直接用 background-position: calc(...) 在旧版 iOS Safari(≤16.4)里不生效,得降级为 JS 设置内联 style:
- 别用
element.style.backgroundPosition拼字符串(易出错),改用element.style.setProperty('background-position', `calc(${percent} * -1) 0`) - 如果用了 CSS-in-JS 库(如 Emotion),确保变量注入时机早于首次
progress事件,否则首帧会 fallback 到 0% - 避免给进度条加
border-radius同时做background-position动画——圆角参与重绘,iOS 上掉帧明显;真要圆角,用overflow: hidden容器裁切更稳
最易被忽略的一点:video 元素本身没加载元数据(duration 为 NaN)时,buffered.end(0) 会报错,必须先等 loadedmetadata 事件再绑定 progress 监听。



















