uni-app 中 video.captureFrame 不支持,需用 canvas.drawImage 绕过,但存在黑图、跨域、平台兼容等问题,推荐优先使用系统封面或云服务截图。

uni-app 无法直接用 video.captureFrame 截图 —— 这个 API 在 iOS、Android 真机和 H5 全都不支持,强行调用会静默失败或报 undefined is not a function。必须绕过 video 原生方法,用 canvas.drawImage(video, ...) 模拟帧捕获,但这条路充满平台陷阱。
为什么 video.currentTime + drawImage 在真机上常截出黑图
根本原因是视频未真正解码就绘制:Android 部分厂商 WebView(如华为 X86)、iOS WKWebView 对 video.readyState 判断不敏感,canplay 事件触发时帧数据可能还没到位。
- 务必监听
loadeddata而非canplay,它表示第一帧已加载完成 - Android 真机建议加
setTimeout(() => ctx.drawImage(...), 100)延迟绘制 - iOS 必须设
video.muted = true且video.autoplay = true,否则静音策略会阻断首帧解码 - 不要用
video.currentTime = 0,部分 MP4 封装里第 0 秒是空白;改用0.1或1
跨域视频(OSS/CDN 直链)在 H5 上 canvas 污染怎么办
H5 端遇到 Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported 就是跨域污染,crossOrigin="anonymous" 单独加没用,服务端必须返回 Access-Control-Allow-Origin: * 头,且不能带 cookie。
- 本地视频(
uni.chooseVideo返回的tempFilePath)完全不受限,可直接 draw - 远程视频若无法控制服务端 CORS 头,H5 端只能放弃前端截图,改用后端生成(如 OSS 的
?x-oss-process=video/snapshot,t_1000,f_jpg) - App 端(iOS/Android)不走浏览器同源策略,
crossOrigin属性可忽略,但需确保 video src 是合法 http(s) 或本地 file 协议
uni-app 里拿不到 document,canvas 怎么创建
Vue 逻辑层无法访问视图层 DOM,document.createElement('canvas') 在 .vue 文件里永远返回 null。必须用 renderjs —— 它运行在视图层,能直接操作真实 DOM。
- 在 template 中加
- 新建
xxx.renderjs.js文件,导出handleVideoChange方法,在里面用document.getElementById拿到元素,再创建video和canvas - 不要在 renderjs 里传大段 base64,用
ownerInstance.callMethod('onPosterReady', base64)回传给 Vue 实例 - 每次处理完立刻
video.remove()、canvas.remove(),避免内存泄漏
有更省事的替代方案吗
有,但要看视频来源:
- 用户本地录制/选择的视频:优先用
uni.chooseMedia,它的tempFiles[0].thumbTempFilePath直接返回系统生成的封面图,全端可用,无需 canvas - 后端返回的视频链接:先查是否支持云服务截图参数(如阿里云 OSS 的
?x-oss-process=video/snapshot),比前端 JS 截图快 10 倍且无兼容问题 - 需要精确到毫秒级帧定位(比如切第 2.37 秒):只能走 canvas 路线,且必须在 renderjs 中实现,否则 iOS 和部分 Android 会失败
canvas 截帧不是“写几行代码就能跑”,它本质是把浏览器视频解码管线的不确定性暴露给了前端。最稳的路径是:能用系统封面就用系统封面,能用云服务就用云服务,只有当这两条路都被堵死时,才进 renderjs + canvas 的深水区 —— 并且要为每个平台单独验证黑图、延迟、跨域问题。











