微信搜索公众号: 前端张小白
目前,在我们的项目中通常会使用各种各样的埋点和监控来收集页面访问的信息,例如点击埋点、PV埋点等,这些埋点数据能够反应绝大部分的用户行为,但是对于一些关注上下文的使用场景而言这些埋点是不够的 尤其是政务相关系统 ( 作为开发了很长一段周期政府项目的人来说 ) 如果我们能够能够还原用户的操作 进行问题复现 才能准确的定位问题 极大降低沟通成本
录制用户行为最容易想到的就是将屏幕操作通过视频的方式录制下来,目前浏览器本身已经提供了一套基于音视轨的实时数据流传输方案 WebRTC(Web Real-Time Communications),在我们的录屏使用场景主要关注以下几个 API:
- getDisplayMedia() - 提示用户给予使用媒体输入的许可从而获取屏幕的流;
- MediaRecorder() - 生成对指定的媒体流进行录制的 MediaRecorder 对象;
- ondataavailable - 当 MediaRecorder 将媒体数据传递到应用程序以供使用时将触发该事件;
整体录制流程如下:
- 调用
mediaDevices.getDisplayMedia()由用户授权选择屏幕进行录制,获取到数据流; - 生成一个
new MediaRecorder()对象录制获取的屏幕的数据流; - 在 MediaRecorder 对象上设置
ondataavailable监听事件用于获取录制的 Blob 数据。
import { useState, useRef, useEffect } from "react";
function App() {
const videoPlay = useRef();
const [myMediaRecorder, setMediaRecorder] = useState();
const [blobs, setBlobs] = useState([]);
const [playUrl, setPlayUrl] = useState("");
const [num, setNum] = useState(0);
/**
* 开始录制
*/
const startHandler = async () => {
const stream = await navigator.mediaDevices.getDisplayMedia();
const m = new MediaRecorder(stream, {
mimeType: "video/webm",
});
setMediaRecorder(m);
};
useEffect(() => {
myMediaRecorder &&
myMediaRecorder.addEventListener("dataavailable", (e) => {
const b = [...blobs];
b.push(e.data);
setBlobs(b);
});
myMediaRecorder?.start();
}, [myMediaRecorder]);
/**
* 结束录制
* */
const stopHandler = () => {
myMediaRecorder?.stop();
};
/**
* 播放录制
*/
const playHandler = async () => {
const res = await saveBlobUrl();
if (res && videoPlay.current && playUrl) {
setTimeout(() => {
videoPlay.current.src = playUrl;
videoPlay.current.play();
}, 1000);
}
};
/**
* 保存播放地址
*/
const saveBlobUrl = () => {
if (blobs.length === 0) return;
return new Promise((res, rej) => {
const b = new Blob(blobs, { type: "video/webm" });
const url = URL.createObjectURL(b);
setPlayUrl(url);
res(true);
});
};
/**
* 下载视频
*/
const downloadHandler = async () => {
await saveBlobUrl();
const a = document.createElement("a");
a.href = playUrl;
a.style.display = "none";
a.download = "record.webm";
a.click();
};
return (
<>
<video ref={videoPlay} className="v"></video>
<button onClick={startHandler}>开始录制</button>
<button onClick={stopHandler}>结束录制</button>
<button onClick={playHandler}>播放录制</button>
<button onClick={downloadHandler}>下载视频</button>
<br />
<br />
<br />
<h1>{num}</h1>
<button onClick={() => setNum(num + 1)}>+1</button>
</>
);
}