在canvas中绘制视频

4,120 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第1天,点击查看活动详情

本文章知识:
1.在canvas中显示video的当前帧画面

开始动手实现功能

先创建一个html文件,和准备要用到的视频文件

  <body>
    <video
      id="myVideo"
      width="360"
      height="202"
      controls
      autoplay
      src="../video//1.smoking.mp4"
    ></video>
    <canvas width="360" height="202" id="myCanvas"></canvas>

    <script>
      let videoEle = document.getElementById('myVideo')
      let canvasEle = document.getElementById('myCanvas')
      let cav = canvasEle.getContext('2d')

      videoEle.addEventListener('play', loop, false)

      function loop() {
        cav.drawImage(videoEle,0,0, canvasEle.width, canvasEle.height)
      }
    </script>
  </body>

这样我们就创建好了一个 306px * 202px 的 video 和 canvas 标签啦,紧跟着,我们的javascript获取了video和canvas标签并利用 getContext 获取到了画布的上下文,用 addEventListener 方法监听 video 的播放事件,当视频播发的时候,就把视频帧利用 drawImage() 画到canvas中。
看看效果如何:

image.png 嗯?为什么canvas中的画面和video的画面不一致呢,我们来分析一下上面写的代码是在那个节点绘制video到canvas中的,是在 video.play() 被触发的时候绘制了video,只绘制了一次,当前看到的canvas的画面其实是video的第一帧,这就是为什么video和canvas画面不一致的原因。

解决canvas画面和video画面不一致的问题

1.既然现在只绘制一次显示了一帧画面,那我就执行好多好多次就好啦。

function loop() {
   cav.drawImage(videoEle,0,0, canvasEle.width, canvasEle.height)
   cav.drawImage(videoEle,0,0, canvasEle.width, canvasEle.height)
   cav.drawImage(videoEle,0,0, canvasEle.width, canvasEle.height)
   cav.drawImage(videoEle,0,0, canvasEle.width, canvasEle.height)
   cav.drawImage(videoEle,0,0, canvasEle.width, canvasEle.height)
   ...
}

嗯,挺牛。

2.我们也可以用定时器来,设定间隔时间来定时绘制画面。

function loop() {
   setInterval(() => {
      cav.drawImage(videoEle, 0, 0, canvasEle.width, canvasEle.height)
    }, 0)
}

image.png 用了定时器画面看着已经同步啦!

3.还有个API叫 requestAnimationFrame(), 也可以用来实现这个需求。

function loop() {
   cav.drawImage(videoEle,0,0, canvasEle.width, canvasEle.height)
   requestAnimationFrame(loop);
}

上面几种方法有一个显而易见的缺点就是,当视频在停止播放的时候,还是会继续执行绘制,所以我们要在视频停止播放的时候也要停止画布的绘制。

function loop() {
  cav.drawImage(videoEle, 0, 0, canvasEle.width, canvasEle.height)
  if(!videoEle.paused) {
    requestAnimationFrame(loop)
    return
  }
}

paused返回true暂停 false播放 注意:因网络原因造成的缓冲状态仍然是false。

至此,功能就简单的实现了。如果你阅读本文章觉得有不妥的地方可以评论留言给我。