视频元素不支持直接CSS过渡,应使用双video实例配合visibility和opacity实现平滑切换;需预加载、监听canplaythrough/loadeddata、设置preload="auto"和poster,并在现代浏览器中用requestVideoFrameCallback精准帧同步。

video元素本身不支持transition
直接给 <video></video> 加 opacity 或 transform 过渡,看起来会动,但实际播放状态、解码器、帧渲染全被打断——常见表现是切换时黑屏、卡顿、音频撕裂,甚至触发浏览器重载视频源。
根本原因是:浏览器对 <video></video> 的硬件加速路径和合成层管理非常敏感,CSS 动画无法协调其内部播放管线。
- 不要对正在播放的
<video></video>元素做opacity: 0 → 1过渡 - 不要用
display: none/block切换,它会中断解码、丢弃已缓冲帧 - 避免在
video上叠加filter(如blur()),某些版本 Chrome 会强制切回 CPU 渲染
用两个<video></video> + visibility + opacity 控制切换
核心思路是预加载下一视频,保持两个 <video></video> 实例始终在 DOM 中,仅通过视觉层控制显隐。关键不是“过渡 video”,而是“过渡容器”。
示例结构:
立即学习“前端免费学习笔记(深入)”;
<div class="video-container"> <video class="video-active" muted autoplay loop><source src="a.mp4"></video> <video class="video-next" muted loop><source src="b.mp4"></video> </div>
-
.video-active和.video-next都设position: absolute; top: 0; left: 0; width: 100%; height: 100% - 切换前,确保
.video-next已触发canplaythrough事件(可用readyState === 4判断) - 用
visibility: hidden隐藏旧视频(保留布局和解码状态),再用opacity过渡新视频 - CSS 过渡只加在
.video-next上:transition: opacity 0.6s ease-out
preload 和 poster 是平滑切换的前提
如果下一视频没缓冲完就触发淡入,会先白/黑屏再突然出画面——这不是过渡问题,是资源准备不足。
- 两个
<video></video>都必须设preload="auto"(尤其 Safari 要求严格) - 为防首帧闪动,给
.video-next设poster属性,指向首帧截图(格式建议.webp,体积小加载快) - 监听
loadeddata比canplay更稳妥:前者保证首帧已解码,后者可能只有元数据 - 移动端注意:iOS Safari 默认禁止自动播放带声音的视频,务必加
muted,否则autoplay失效,preload也可能被忽略
用requestVideoFrameCallback同步帧率(进阶)
当需要逐帧控制(比如做时间轴对齐、多视频帧精度切换),CSS transition 的定时器不可靠——它不与视频帧同步,容易出现微卡顿或跳帧。
现代浏览器(Chrome 94+、Edge 94+、Safari 17.4+)支持:
videoElement.requestVideoFrameCallback((now, metadata) => {
// 在视频帧渲染前执行,可精准控制切换时机
if (shouldSwitch && nextVideo.readyState === 4) {
switchVideos(); // 执行 visibility/opacity 切换
}
});
- 比
setTimeout或requestAnimationFrame更准,直接挂钩视频合成管线 - 不兼容老浏览器时,降级回
loadeddata+ 延迟setTimeout(建议 ≥ 100ms) - 注意:该 API 只在
video开始播放后才有效,首次调用需等play()成功后
真正难的不是写过渡动画,是让两个视频在解码、缓冲、渲染节奏上对齐。多数人卡在 preload 没生效、muted 忘加、或者用 display 切换——这些细节一错,再漂亮的 CSS 也救不回卡顿。


















