poster是视频未播放且未加载首帧时的兜底占位图,非可靠封面控制手段;首帧截图需等loadeddata事件、设crossOrigin、调整currentTime并用Blob URL赋值poster。

video 的 poster 属性到底起什么作用
poster 是一个纯前端控制的静态封面开关,它只在视频未开始播放、且尚未加载出首帧时显示。一旦视频元数据就绪(loadedmetadata)或用户点击播放,浏览器就会立即切换为视频画面——不管首帧是否清晰、是否带黑边、是否被滤镜影响。
常见误解是“设了 poster 就能稳稳控制封面”,但实际中:
– 移动端 Safari / 微信内置浏览器常忽略 poster,直接渲染首帧;
– 如果视频文件本身没有关键帧(keyframe)在开头(比如编码时用了 long GOP),poster 图片可能闪一下就消失,而首帧要等解码到第一个 I 帧才出来;
– poster 图片路径 404 不报错,只会静默 fallback 到黑屏或首帧。
所以 poster 更适合作为「兜底占位图」,而不是封面决策主逻辑。
用 JavaScript 截首帧必须等 loadeddata 而不是 loadedmetadata
loadedmetadata 只表示视频宽高、时长等信息已读取,此时 video.videoWidth 和 video.videoHeight 可用,但画面数据还没解码,drawImage() 会画出空白或上一帧残留。
立即学习“前端免费学习笔记(深入)”;
生成可在 deepbi 沙箱 iframe 内运行或作为独立文件运行的交互式 HTML 页面,使用 `window.deepbi` JS SDK 实现数据查询、空间管理、文件浏览及用户操作。当用户提出以下需求时启用该能力: - 创建 KPI 仪表盘、数据表格、文件浏览器、成员列表、管理页面; - 明确要求“转成 dhtml”、“转译成 HTML”、“把 XX 页面转成……”、“控制台页面”、“管理后台”、“DeepBI 页面迁移”; - 提及位于 `apps/console/src/featur
真正能安全截图的时机是 loadeddata 事件,它代表第一帧像素数据已就绪。但要注意:
- 必须提前设置
video.crossOrigin = "anonymous",否则跨域视频会触发 canvas 污染(SecurityError: Failed to execute 'toDataURL' on 'HTMLCanvasElement'); - 不要依赖
currentTime = 0,有些 MP4 文件第一帧在 0.1s 后,建议设video.currentTime = 0.01再监听loadeddata; - 截帧前最好显式调用
video.play().catch(() => {})(尤其 iOS),否则部分浏览器不触发帧解码。
canvas 截帧时宽高比例错乱的三个根源
很多人截出来的图被拉伸、裁切或留白,问题不在代码逻辑,而在对视频原始尺寸和显示尺寸的混淆:
-
video.clientWidth/video.clientHeight是 CSS 渲染尺寸,受width、height、object-fit影响,不能用于 canvas 绘制; - 正确尺寸应取
video.videoWidth和video.videoHeight(原始分辨率),再按需缩放; - 如果视频带 letterbox(上下黑边),
video.videoHeight包含黑边,但videoHeight / videoWidth算出的宽高比仍是内容本身的——此时 canvas 应按内容区域绘制,黑边由 CSS 或后处理补全,否则播放瞬间会“跳变”。
示例:一个 1920×1080 视频加了 16:9 滤镜导出,实际画面区域是 1920×800,上下各 140px 黑边。canvas 应画 1920×800,再手动 pad 成 1920×1080,否则 poster 和首帧视觉不一致。
首帧截图要不要存成 PNG 还是 JPG
选 JPG 更实际:
- PNG 保留透明通道但视频无 alpha,体积反而大 30%~50%;
- JPG 支持质量压缩(如
canvas.toDataURL('image/jpeg', 0.85)),1920×1080 首帧可压到 120KB 内,适合做 poster 替换; - 注意:iOS Safari 对
image/jpeg的toDataURL支持稳定,但某些安卓 WebView 仍只认image/png,可 fallback 判断:canvas.toDataURL('image/jpeg')返回"data:,"就换 PNG。
真正容易被忽略的是:截图后若直接赋给 video.poster,必须用 blob URL 或 base64,不能用 canvas 的 dataURL 直接塞——因为 poster 不支持 data URL 协议(部分浏览器静默失败)。正确做法是转成 Blob 再用 URL.createObjectURL()。


















