【问题标题】:Copying from video to canvas in javascript在javascript中从视频复制到画布
【发布时间】:2021-11-03 05:50:33
【问题描述】:

我正在尝试加载视频并将其缩略图绘制到画布中。我遇到的问题是,当视频图像准备好复制时,我找不到触发的事件。当加载数据事件触发时,大小设置正确,但画布图像为空白。如果我取消注释计时器以延迟一秒钟,则画布被填充,但没有延迟它不是。是否有一些我忽略的事件,或者其他方式来告诉视频元素何时能够复制?我尝试了 load、canplay、canplaythrough、loadeddata、progress,但似乎都没有在正确的时间出现。

<html>
  <head>
    <script>
      var video, image, width, height;
      function copy(src) {
        video = document.getElementById('video');
        video.addEventListener('loadeddata', x => finishCopy()); 
        video.src = src;
      }
      function finishCopy() {
        width = document.defaultView.getComputedStyle(video).width.replace(/[^.0-]/g, '');
        height = document.defaultView.getComputedStyle(video).height.replace(/[^.0-]/g, '');
        image = document.getElementById('image');
        image.width = width;
        image.height = height;
        //setTimeout(draw, 1000);
        draw();
      }

      function draw() {
        let ctxt = image.getContext('2d');
        ctxt.drawImage(video, 0, 0, width, height);
      }

    </script>
  </head>
  <body >
    <video id="video" autostart="false" ></video>
    <canvas id="image"></canvas>
  </body>
</html>

【问题讨论】:

    标签: javascript html5-canvas dom-events html5-video


    【解决方案1】:

    虽然loadeddata 事件会在当前帧的数据加载后立即触发,但它并没有给出任何暗示这实际上意味着什么。

    然而,在实践中,这意味着在某些情况下,或者在某些浏览器中,它可能能够呈现第一帧 - 尽管您不能依赖它。

    解决方案是寻找一个事件,该事件稍后在加载/处理视频的过程中触发。

    这些事件发生的顺序如下:

    1. 加载开始
    2. 持续时间变化
    3. 加载元数据
    4. 加载数据
    5. 进展
    6. 可以玩

    因此,如果我们只使用canplay 事件而不是loadeddata,事情应该会按预期工作。

    这是一个例子:

    var video, image, width, height;
    
    function copy(src) {
      video = document.getElementById('video');
      video.addEventListener('canplay', x => finishCopy());
      video.src = src;
    }
    
    function finishCopy() {
      width = parseInt(document.defaultView.getComputedStyle(video).width);
      height = parseInt(document.defaultView.getComputedStyle(video).height);
      image = document.getElementById('image');
      image.width = width;
      image.height = height;
      draw();
    }
    
    function draw() {
      let ctxt = image.getContext('2d');
      ctxt.drawImage(video, 0, 0, width, height);
    }
    
    copy("https://www.w3schools.com/html/mov_bbb.mp4")
    <video id="video" autostart="false"></video>
    <canvas id="image"></canvas>

    编辑

    在使用不同浏览器进行一些测试后,我的解决方案似乎在 Chrome 中失败了。尽管如此,我还是找到了一种在 Chrome 中也可以使用的解决方法。

    您可以将一个相当未记录的参数附加到视频的 URL,让您可以指定视频的开始位置甚至范围 (#t=)。

    现在,如果您将起始位置设置为 0.000001 之类的较低值,您仍会看到视频的第一帧,并且可以在画布上进行绘制。

    但是我的测试也表明,如果您使用 canplaythrough 事件而不是 canplay 或 loadeddata,这仅适用于 FireFox 和 Chrome。

    这是修改后的示例:

    var video, image, width, height;
    
    function copy(src) {
      video = document.getElementById('video');
      video.addEventListener('canplaythrough', x => finishCopy());
      video.src = src;
    }
    
    function finishCopy() {
      width = parseInt(document.defaultView.getComputedStyle(video).width);
      height = parseInt(document.defaultView.getComputedStyle(video).height);
      image = document.getElementById('image');
      image.width = width;
      image.height = height;
      draw();
    }
    
    function draw() {
      let ctxt = image.getContext('2d');
      ctxt.drawImage(video, 0, 0, width, height);
    }
    
    copy("https://www.w3schools.com/html/mov_bbb.mp4#t=0.000001")
    <video id="video" autostart="false"></video>
    <canvas id="image"></canvas>

    【讨论】:

    • 感谢您的回复,但很抱歉,这对我不起作用。我之前尝试过“canplay”事件,现在还测试了您的示例代码。我检查了画布,然后添加了一个实心边框以确保我正在寻找正确的空间。画布大小是正确的,但它是空白的。我还在 firefox 和 safari 上试过它——它确实在 Safari 上工作,但在 Chrome 或 Firefox 上却不行。我认为它要么是特定于浏览器的,要么可能是时间问题。我笨拙的解决方法确实适用于所有 3。
    • 抱歉 russel - 我只是忘了在 Chrome 中测试它。不过,我找到了另一种解决方法并更新了我的答案。
    • 太棒了!非常感谢您的努力 - 不仅仅是为了解决方案,而是为了了解正在发生的事情。这将使支持其他浏览器变得更加容易。
    【解决方案2】:

    如果有人有更好的解决方案,我会很欢迎,但我确实找到了一种方法来做我想做的事,方法是加载元数据,进行搜索,然后在“搜索”事件侦听器中复制图像。我正在使用 Chrome,并且从我所读到的内容中怀疑这可能不是一个通用的解决方案,但由于目前我只支持 Chrome,这似乎可以满足我的需求。

    <html>
      <head>
        <script>
          var video, image, width, height;
          function copy(src) {
            video = document.getElementById('video');
            video.addEventListener('loadedmetadata', x => video.currentTime = 0);
            video.addEventListener('seeked', x => finishCopy());
            video.src = src;
          }
    
          function finishCopy() {
            width = document.defaultView.getComputedStyle(video).width.replace(/[^.0-9]/g, '');
            height = document.defaultView.getComputedStyle(video).height.replace(/[^.0-9]/g, '');
            image = document.getElementById('image');
            image.width = width;
            image.height = height;
            //setTimeout(draw, 1000);
            draw();
          }
    
          function draw() {
            let ctxt = image.getContext('2d');
            ctxt.drawImage(video, 0, 0, width, height);
          }
        </script>
      </head>
      <body >
        <button onclick="copy('asd.mp4')">Copy asd.mp4</button>
        <br />
        <video id="video" preload="metadata" autostart="false" ></video>
        <canvas id="image"></canvas>
      </body>
    </html>
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-12-03
      • 1970-01-01
      • 2012-08-28
      • 2015-01-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多