首先來實現(xiàn)canvas畫布內(nèi)容轉(zhuǎn)換視頻,canvas生成視頻主要需要captureStream和mediaRecorder。
captureStream
canvas中有一個captureStream方法可以將普通畫布轉(zhuǎn)換成一個實時視頻捕獲的畫布,該方法返回一個MediaStream實例。
參數(shù):
frameRate(可選):設(shè)置幀率
詳細文檔: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
通過這兩者的結(jié)合使用就可以實現(xiàn)canvas轉(zhuǎn)視頻的效果了。

案例代碼:
// 頁面結(jié)構(gòu)(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')
// 創(chuàng)建一個MediaStream
const stream = canvas.captureStream()
// 創(chuàng)建一個對指定的MediaStream進行錄制的MediaRecorder
const recorder = new MediaRecorder(stream, {mimeType: 'video/webm'})
// 錄像結(jié)束后的回調(diào),寫入數(shù)據(jù)
recorder.ondataavailable = function (event) {
if (event.data && event.data.size) {
data.push(event.data)
}
}
// 監(jiān)聽錄制結(jié)束
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)
}
}
現(xiàn)在已經(jīng)實現(xiàn)了canvas的視頻轉(zhuǎn)換,接下來就是需要將rrweb的錄制直接繪制到canvas上或者轉(zhuǎn)換為圖片再繪制到錄制的canvas上。一般的頁面結(jié)構(gòu)繪制canvas的方法使用html2canvas實現(xiàn),當然也可以通過svg的foreignObject來實現(xiàn)。
- 通過html2canvas實現(xiàn):
首先使用html2canvas獲取頁面截屏的canvas:
// dom:需要截取的頁面節(jié)點,返回繪制好的canvas
html2canvas(dom, {
width: window.screen.availWidth,
height: window.screen.availHeight,
x: 0,
y: window.pageYOffset, // 截取開始的y軸坐標
allowTaint: true,
useCORS: true
}).then(canvas1 => {
// 將獲得canvas繪制到實時視頻捕獲的畫布上。周期性調(diào)用html2canvas繪制
});
html2canvas中可以配置返回的canvas繪制在指定的canvas上,但多次繪制時出現(xiàn)繪制的是同一張圖片,所有采用將返回的canvas轉(zhuǎn)成base64在繪制到錄制的canvas上來實現(xiàn)視頻轉(zhuǎn)換。而在周期性頻繁調(diào)用html2canvas方法時,當前頁面最好不要操作,不然會出現(xiàn)卡頓。
文檔:http://html2canvas.hertzen.com/
使用svg的foreignObject實現(xiàn)截屏(放棄):
在svg的foreignObject中可以內(nèi)嵌html結(jié)構(gòu),但在開發(fā)中發(fā)現(xiàn)foreignObject中需要內(nèi)嵌樣式,用外聯(lián)樣式時在轉(zhuǎn)成base64后無法顯示樣式,且在內(nèi)聯(lián)樣式中不能帶有#不然轉(zhuǎn)成img時會出現(xiàn)圖片加載失敗。使用
puppeteer來對rrweb錄制進行截屏處理,再將截取的圖片繪制到錄制的canvas上來進行視頻轉(zhuǎn)換。
截屏函數(shù):
// 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的錄制數(shù)據(jù)后調(diào)用該函數(shù),無痕瀏覽器會自動打開播放頁面進行周期性截圖。
rrweb的錄屏播放頁(用于puppeteer截屏的頁面)
<link rel="stylesheet" >
<link rel="stylesheet" />
...
<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
需要注意的是由于采用的等待播放后截屏,導(dǎo)致如果錄屏?xí)r間過長,周期性截屏的時間也會相對的變得很長。也可以采用
rrwebPlayer中的goto方法跳轉(zhuǎn)到對應(yīng)的時間進行截屏可以縮短等待的時間,只是使用這個方法時多輸入框切換的時候會出現(xiàn)切換前的輸入框被清空的問題。
當截屏圖片保存到對應(yīng)文件夾后就可以使用canvas周期性繪制圖片來轉(zhuǎn)化視頻了。