poster是静态图片,不自动提取视频首帧;首帧截取需通过Canvas+Video API,在loadedmetadata后设currentTime=0,等待seeked事件再drawImage,跨域需crossOrigin="anonymous",iOS需play后pause截帧。

poster 属性和首帧截取是两套完全独立的机制,不能互相替代,也不自动兼容。
为什么 poster 不等于首帧截图
poster 是一个静态图片地址,浏览器只在视频未加载、未播放或加载失败时显示它;它不会读取视频内容,更不会解析帧数据。即使你把首帧截图手动设为 poster 的值,那也只是“恰好用了首帧”,不是“自动提取首帧”。
- 常见错误:直接写
—— 浏览器会当成图片路径去请求,404 后退回到无封面状态 -
poster图片必须是 JPG/PNG 等可渲染图像格式,且需提前生成好 - 移动端 Safari 对
poster加载时机敏感:若视频资源跨域但没配CORS,poster可能不显示(哪怕图片本身没问题)
首帧截取必须用 Canvas + Video API
真正从视频里抽第一帧,只能靠 drawImage() 把当前视频画面画到 上,再转成 base64 或 Blob。这个过程依赖视频元数据就绪,不能跳过等待逻辑。
- 关键事件顺序必须是:
loadedmetadata→ 设置currentTime = 0→ 等待seeked(不是loadeddata)→ 调用drawImage - 只监听
loadeddata容易截出黑图:因为有些编码下第 0 秒未必有 I 帧,currentTime = 0实际定位到的是上一个关键帧,可能为空白 - 跨域视频必须加
crossOrigin="anonymous",否则drawImage会触发 canvas 污染(SecurityError),后续toDataURL失败 - 示例片段:
video.crossOrigin = 'anonymous'; video.src = url; video.addEventListener('loadedmetadata', () => { video.currentTime = 0; }); video.addEventListener('seeked', () => { ctx.drawImage(video, 0, 0, canvas.width, canvas.height); const base64 = canvas.toDataURL('image/jpeg', 0.8); video.poster = base64; // 这里才真正赋值 });
移动端 H5 的特殊坑:iOS Safari 不支持 currentTime = 0 同步触发 seeked
iOS 15+ 上,本地 File 对象生成的 objectURL 视频,currentTime = 0 后可能不触发 seeked,导致截帧卡死。这不是 bug,是 Safari 对非网络视频的 seek 行为限制。
- 绕过方式:改用
play().then(() => { video.pause(); ... }),在play成功后立刻暂停并截帧 - 注意
play()必须由用户手势触发(如按钮点击),不能在页面加载时自动调用 - 部分安卓 WebView(如微信内置)对
drawImage的时序更宽松,但不能依赖——务必统一走seeked+play/pause双保险 - 不要用
setTimeout等固定延时,不同设备解码速度差异大,黑图率高
真正难的不是“怎么截”,而是“什么时候才算准备好截”。很多黑图、空白、跨域报错,都卡在事件监听漏掉或时机错位。别省那几行事件绑定代码。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











