You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.05 14:54:01