canvas实现将rrweb的录制转换为视频

首先来实现canvas画布内容转换视频,canvas生成视频主要需要captureStreammediaRecorder

captureStream

canvas中有一个captureStream方法可以将普通画布转换成一个实时视频捕获的画布,该方法返回一个MediaStream实例。
参数:
frameRate(可选):设置帧率
详细文档:https://developer.mozilla.org/zh-CN/docs/Web/API/HTMLCanvasElement/%E6%8D%95%E8%8E%B7%E6%B5%81

mediaRecorder

mediaRecorder是一个进行媒体录制的接口,在实例化时需要传入MediaStream对象和MIME 类型(视频类型如video/webm或者video/mp4)。
详细文档:https://developer.mozilla.org/zh-CN/docs/Web/API/MediaRecorder

通过这两者的结合使用就可以实现canvas转视频的效果了。

案例效果:
chrome-capture.gif

案例代码:

// 页面结构(html)
<button onclick="startRecorder()">录制</button>
<button onclick="stopRecorder()">停止</button>
<button onclick="drawImageDemo()">播放图片</button>
<canvas id="canvas" width="200" height="200" control></canvas>
<video controls></video>
  ···
// js脚本
  var data = []
  var canvas = document.getElementById('canvas')
  // 创建一个MediaStream
  const stream = canvas.captureStream()
  // 创建一个对指定的MediaStream进行录制的MediaRecorder
  const recorder = new MediaRecorder(stream, {mimeType: 'video/webm'})
  // 录像结束后的回调,写入数据
  recorder.ondataavailable = function (event) {
    if (event.data && event.data.size) {
      data.push(event.data)
    }
  }
  // 监听录制结束
  recorder.onstop = () => {
    const url = URL.createObjectURL(new Blob(data, { type: 'video/webm' }));
    document.querySelector("video").src = url;
  }
  // 开始录制
  function startRecorder () {
    recorder.start()
  }
  // 停止录制
  function stopRecorder () {
    recorder.stop()
  }

  // 绘制图片(播放5张图片)
  let drawIndex = 1
  function drawImageDemo () {
    var img = document.createElement('img')
    img.src = `./images/${drawIndex}.jpg`
    img.width = 300
    img.height = 300
    var dom = canvas.getContext('2d')
    img.onload = function () {
      dom.drawImage(img, 0, 0, canvas.width, canvas.height)
      setTimeout(() => {
        drawIndex++
        if (drawIndex > 5) {
          drawIndex = 1
          return
        }
        drawImageDemo()
      }, 1000)
    }
  }

现在已经实现了canvas的视频转换,接下来就是需要将rrweb的录制直接绘制到canvas上或者转换为图片再绘制到录制的canvas上。一般的页面结构绘制canvas的方法使用html2canvas实现,当然也可以通过svgforeignObject来实现。

  1. 通过html2canvas实现:
    首先使用html2canvas获取页面截屏的canvas
// dom:需要截取的页面节点,返回绘制好的canvas
    html2canvas(dom, {
      width: window.screen.availWidth,
      height: window.screen.availHeight,
      x: 0,
      y: window.pageYOffset,  // 截取开始的y轴坐标
      allowTaint: true,
      useCORS: true
    }).then(canvas1 => { 
        // 将获得canvas绘制到实时视频捕获的画布上。周期性调用html2canvas绘制
    });

html2canvas中可以配置返回的canvas绘制在指定的canvas上,但多次绘制时出现绘制的是同一张图片,所有采用将返回的canvas转成base64在绘制到录制的canvas上来实现视频转换。而在周期性频繁调用html2canvas方法时,当前页面最好不要操作,不然会出现卡顿。

文档:http://html2canvas.hertzen.com/

  1. 使用svg的foreignObject实现截屏(放弃):
    svgforeignObject中可以内嵌html结构,但在开发中发现foreignObject中需要内嵌样式,用外联样式时在转成base64后无法显示样式,且在内联样式中不能带有#不然转成img时会出现图片加载失败。

  2. 使用puppeteer来对rrweb录制进行截屏处理,再将截取的图片绘制到录制的canvas上来进行视频转换。
    截屏函数:

// hasT:rrweb录制的时间长度,sToL:1秒中几张图
const startPupp = (hasT) => {
  (async () => {
    const browser = await puppeteer.launch({
      headless: false
    })
    var sToL = 10
    for (let nowI = 0; nowI <= hasT * sToL; nowI++) {
      let page = await browser.newPage()
      let waitTime = nowI * (1000 / sToL)
      // rrweb录制的播放页面
      await page.goto('http://127.0.0.1:2000/rrwebVideo.html')
      await page.setViewport({
        width: 1920,
        height: 1080
      })
      // await page.click('#replayBtn')
      await page.waitForTimeout(waitTime) // 等待录制的播放时间
      await page.screenshot({
        path: `test${nowI}.png`  // 图片的存储位置
      })
      page.close()
    }
  })();
}

在接受到当前rrweb的录制数据后调用该函数,无痕浏览器会自动打开播放页面进行周期性截图。

rrweb的录屏播放页(用于puppeteer截屏的页面)

  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/rrweb-player@latest/dist/style.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/rrweb@latest/dist/rrweb.min.css"/>
  ...
  <script src="https://cdn.jsdelivr.net/npm/rrweb@latest/dist/rrweb.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/rrweb-player@latest/dist/index.js"></script>
  <script>
  // rrweb行为录制
    let events = [];
    function record () {
      rrweb.record({
        emit(event) {
          // 用任意方式存储 event
          events.push(event);
        },
      });
    }
    function replay () {
      new rrwebPlayer({
        target: document.getElementById('playback'), // 可以自定义 DOM 元素
        data: {
          events,
        }
      });
    }
  </script>

rrweb的github地址: https://github.com/rrweb-io/rrweb

需要注意的是由于采用的等待播放后截屏,导致如果录屏时间过长,周期性截屏的时间也会相对的变得很长。也可以采用rrwebPlayer中的goto方法跳转到对应的时间进行截屏可以缩短等待的时间,只是使用这个方法时多输入框切换的时候会出现切换前的输入框被清空的问题。

当截屏图片保存到对应文件夹后就可以使用canvas周期性绘制图片来转化视频了。

demo地址:https://github.com/erpang123/rrwebToVideo

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容