首先来实现canvas画布内容转换视频,canvas生成视频主要需要captureStream
和mediaRecorder
。
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转视频的效果了。
案例代码:
// 页面结构(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
实现,当然也可以通过svg
的foreignObject
来实现。
- 通过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/
使用svg的foreignObject实现截屏(放弃):
在svg
的foreignObject
中可以内嵌html结构,但在开发中发现foreignObject
中需要内嵌样式,用外联样式时在转成base64后无法显示样式,且在内联样式中不能带有#
不然转成img时会出现图片加载失败。使用
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周期性绘制图片来转化视频了。