【第2036期】如何将Canvas绘制过程转为视频

作者:月影

前言

视频就是一帧一帧的图片。前些天还想在canvas添加链接呢?今日早读文章由字节跳动@月影授权分享。

正文从这开始~~

如果我们用Canvas实现了一些动画效果,需要将它回放出来,很多人通常就是用录屏工具将屏幕内容录下来播放,很少有人知道,Canvas可以直接通过现代浏览器支持的 Media Streams API 来转成视频。

Canvas对象支持captureStream方法,这个方法会返回一个MediaStream对象。然后,我们可以通过这个对象创建一个MediaRecorder来录屏。

我们看一个简单的例子。

录制视频

首先我们写一个非常简单的Canvas动画效果,代码如下:

  1. const canvas = document.querySelector('canvas');

  2. const ctx = canvas.getContext('2d');

  3. const {width, height} = canvas;


  4. ctx.fillStyle = 'red';


  5. function draw(rotation = 0) {

  6. ctx.clearRect(0, 0, 1000, 1000);

  7. ctx.save();

  8. ctx.translate(width / 2, height / 2);

  9. ctx.rotate(rotation);

  10. ctx.translate(-width / 2, -height / 2);

  11. ctx.beginPath();

  12. ctx.rect(200, 200, 200, 200);

  13. ctx.fill();

  14. ctx.restore();

  15. }


  16. function update(t) {

  17. draw(t / 500);

  18. requestAnimationFrame(update);

  19. }

  20. update(0);

这个效果实现一个200宽高的矩形在画布中心旋转。


接下来,我们将这个效果录制下来,假设我们录制6秒的视频,首先我们要获取MediaStream对象:

  1. const stream = canvas.captureStream();

然后,我们创建一个MediaRecorder:

  1. const recorder = new MediaRecorder(stream, { mimeType: 'video/webm' });

接着我们可以注册ondataavailable事件,将数据记录下来:

  1. const data = [];

  2. recorder.ondataavailable = function (event) {

  3. if (event.data && event.data.size) {

  4. data.push(event.data);

  5. }

  6. };

在onstop事件里,我们通过Blob对象,将数据写入到页面上的video标签中。

  1. recorder.onstop = () => {

  2. const url = URL.createObjectURL(new Blob(data, { type: 'video/webm' }));

  3. document.querySelector("#videoContainer").style.display = "block";

  4. document.querySelector("video").src = url;

  5. };

最后,我们开始录制视频,并设定在6秒钟之后停止录制:

  1. recorder.start();


  2. setTimeout(() => {

  3. recorder.stop();

  4. }, 6000);

这样,就可以达到我们想要的录屏效果了。

完整代码:

  1. <div class="main">

  2. <canvas width="600" height="600"></canvas>

  3. <div id="videoContainer">

  4. <h2>视频</h2>

  5. <video width="300"

  6. height="300"

  7. controls="true"

  8. autoplay="true"

  9. id="video"

  10. ></video>

  11. </div>

  12. </div>

  1. html, body {

  2. width: 100%;

  3. height: 100%;

  4. }


  5. .main {

  6. display: flex;

  7. }


  8. #videoContainer {

  9. display: none;

  10. }

  1. const canvas = document.querySelector('canvas');

  2. const ctx = canvas.getContext('2d');

  3. const {width, height} = canvas;


  4. ctx.fillStyle = 'red';


  5. function draw(rotation = 0) {

  6. ctx.clearRect(0, 0, 1000, 1000);

  7. ctx.save();

  8. ctx.translate(width / 2, height / 2);

  9. ctx.rotate(rotation);

  10. ctx.translate(-width / 2, -height / 2);

  11. ctx.beginPath();

  12. ctx.rect(200, 200, 200, 200);

  13. ctx.fill();

  14. ctx.restore();

  15. }


  16. function update(t) {

  17. draw(t / 500);

  18. requestAnimationFrame(update);

  19. }

  20. update(0);


  21. const stream = canvas.captureStream();

  22. const recorder = new MediaRecorder(stream, { mimeType: 'video/webm' });


  23. const data = [];

  24. recorder.ondataavailable = function (event) {

  25. if (event.data && event.data.size) {

  26. data.push(event.data);

  27. }

  28. };


  29. recorder.onstop = () => {

  30. const url = URL.createObjectURL(new Blob(data, { type: 'video/webm' }));

  31. document.querySelector("#videoContainer").style.display = "block";

  32. document.querySelector("video").src = url;

  33. };


  34. recorder.start();


  35. setTimeout(() => {

  36. recorder.stop();

  37. }, 6000);

与音频结合

Canvas录制好的视频,我们还可以将它和音频结合,方法是通过ffmpeg的Web端来合成。

浏览器可以通过WebAssembly来集成ffmpeg,具体的项目在这里,有兴趣的同学可以研究下。ffmpeg的Web API用起来还比较复杂,奇舞团@李成银同学开发了一个非常好用的封装,项目地址在https://github.com/welefen/canvas2video,可以在这里看到具体的例子。

总结

在一些需要动态回放需求的场景里,我们可以用Canvas来实时创建视频然后回放,从而不必使用JS重新绘制,这是一种比较节省资源的方式。另外,Canvas的录制功能对于一些在线教学的场景也很有价值,录屏软件消耗资源较多,而在Web上直接录制Canvas,这可能是一种更简单便捷的方式。

关于本文 作者:@月影 原文:https://juejin.im/post/6859177958058229768

@月影曾分享过


【第1923期】如何用CSS绘制饼图


SpriteJS的3D渲染能力 Up, Up, Up!


【第725期】少写代码少填坑


欢迎自荐投稿,前端早读课等你来