JavaScript中video视频画面复制到canvas的就绪触发事件问题
问题原因
你用到的loadeddata、canplay等事件触发时,仅代表浏览器已加载足够的视频数据,不代表首帧已经完成解码、可读取像素内容。此时调用drawImage读取视频内容,拿到的是未就绪的空白缓冲区,因此画布渲染为空。
解决方案
方案1:配合seeked事件主动定位首帧
主动将视频进度定位到首帧位置,seeked事件触发时代表目标帧已完成解码,可直接绘制,兼容性极佳:
<html> <head> <script> var video, image, width, height; function copy(src) { video = document.getElementById('video'); // 先监听元数据加载完成,拿到视频原始尺寸 video.addEventListener('loadedmetadata', () => { // 直接取视频原始分辨率,避免样式影响 width = video.videoWidth; height = video.videoHeight; image = document.getElementById('image'); image.width = width; image.height = height; // 监听seek完成事件,此时帧已解码就绪 video.addEventListener('seeked', draw, { once: true }); // 主动定位到0.1s位置,避免部分视频0位置无有效帧 video.currentTime = 0.1; }); video.src = src; } function draw() { let ctxt = image.getContext('2d'); ctxt.drawImage(video, 0, 0, width, height); } </script> </head> <body> <!-- 关闭自动播放,开启预加载元数据 --> <video id="video" autoplay="false" preload="metadata"></video> <canvas id="image"></canvas> </body> </html>
方案2:使用requestVideoFrameCallback API
现代浏览器支持的视频专属帧回调API,会在视频帧准备好渲染时触发,无需手动定位进度,在loadeddata事件触发后调用即可:
function finishCopy() { width = video.videoWidth; height = video.videoHeight; image = document.getElementById('image'); image.width = width; image.height = height; // 等待首帧就绪后执行绘制 video.requestVideoFrameCallback(draw); }
注意事项
- 不要使用
getComputedStyle读取视频宽高,直接使用video.videoWidth和video.videoHeight获取视频原始分辨率,避免样式缩放导致绘制尺寸错误 - 视频标签建议添加
preload="metadata"属性,保证浏览器会预加载视频元数据和首帧信息
内容的提问来源于stack exchange,提问作者russell
相关产品推荐
相关产品推荐

