无感知录屏

402 阅读4分钟

网页录屏的实现

要在 JavaScript 中实现屏幕录制,可以使用 navigator.mediaDevices.getDisplayMedia() 方法来获取屏幕的媒体流。然后,可使用 MediaRecorder 对象将媒体流录制为视频文件。 但该方法会在浏览器弹出一个授权窗口,让用户选择要分享的内容,这不可实现“无感知”。

image.png

前置知识点

canvas.captureStream(60) 是一个用于将  元素中的内容以视频流的形式捕获出来的方法。参数 60 表示视频的帧率为 60 帧每秒。这个方法可以用于创建一个实时的视频流,可以被用于 WebRTC 连接、录制视频等场景中。

new MediaRecorder  是Web API提供的一个用来录制音频和视频的接口。操作流程通常是这样的:首先获取音频/视频数据流,然后使用 new MediaRecorder 对象来录制这些数据流。然后,你可以监听 dataavailable 事件来获取录制的数据块,最后将这些数据块拼接在一起,保存为一个音频或视频文件。

页面

Canvas视频录制
<main>
    <div class="buttons">
        <button class="start-btn">开始录制</button>
        <button class="stop-btn">结束录制</button>
    </div>
    <div id="box">
        <section class="content">
            <h2>TODO LIST</h2>
            <div class="background-div">
                <button class="background-btn">切换背景颜色</button>
            </div>
            <div id="todo-form">
                <input type="text" class="input-field" placeholder="输入待办事项">
                <button type="submit" class="submit-btn">提交</button>
            </div>
            <div class="list"></div>
        </section>
    </div>
    <img src="转存失败,建议直接上传图片文件 " alt="转存失败,建议直接上传图片文件" class="hidden">
</main>

<script src="<https://cdn.bootcss.com/html2canvas/0.5.0-beta4/html2canvas.min.js>" defer></script>
<script src="canvas.js" defer></script>

js部分

`

document.addEventListener('DOMContentLoaded', () => {
    document.querySelector('.background-btn').addEventListener('click', () => {
        document.querySelector('.content').style.background = colors[col];
        col = (col + 1) % colors.length;
        canvasFunction();
    });

    document.querySelector(".input-field").addEventListener("input", (event) => {
        canvasFunction();
    });

    document.querySelector('.submit-btn').addEventListener('click', () => {
        const text = document.querySelector('.input-field').value;
        document.querySelector('.input-field').value = '';
        const div = document.createElement('div');
        div.setAttribute('class', 'item');
        div.innerText = text;
        document.querySelector('.list').appendChild(div);
        canvasFunction();
    });

    const box = document.getElementById('box');
    const boxBoundingClientRect = box.getBoundingClientRect();
    const w = boxBoundingClientRect.width;
    const h = boxBoundingClientRect.height;
    const canvas = document.createElement('canvas');
    canvas.setAttribute('id', 'canvas');
    canvas.setAttribute('width', w);
    canvas.setAttribute('height', h);
    canvas.style.display = 'none';
    box.appendChild(canvas);

    const img = document.querySelector('img');
    const ctx = canvas.getContext("2d");
    const allChunks = [];
    const stream = canvas.captureStream(60); // 60 FPS recording   1秒60帧

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

    // recorder.ondataavailable 是一个事件处理程序,用于监听录音或录制视频时,每次有新的数据块可用时触发的事件。
    // 当录制媒体时,MediaRecorder 类会定期将数据存储为一个 Blob 对象并触发该事件。
    // 你可以通过监听这个事件来获取这些数据块,并在录制结束后将它们合并成一个完整的音频或视频文件。

    recorder.ondataavailable = (e) => {
        console.log('监听流', e)
        allChunks.push(e.data);
    };

    // 开始
    document.querySelector('.start-btn').addEventListener('click', () => {
        if (MediaRecorder.isTypeSupported('video/webm;codecs=vp9')) {
            canvasFunction();
            console.log("开始");
            // recorder.start 是 MediaRecorder 对象的方法,用于开始录制音频或视频。
            // 当你调用这个方法时,MediaRecorder 对象会开始接收来自音频或视频输入的数据,
            // 并且在接收到数据后触发 dataavailable 事件。
            recorder.start(10);
        }
    });


    // 结束
    document.querySelector('.stop-btn').addEventListener('click', () => {
        console.log("结束录制");
        canvasFunction();
        setTimeout(() => {
            console.log("保存");
            recorder.stop();
            const fullBlob = new Blob(allChunks);
            const videoUrl = window.URL.createObjectURL(fullBlob);
            const video = document.createElement('video');
            video.controls = true;
            video.src = videoUrl;
            video.muted = true;
            video.autoplay = true;
            document.body.appendChild(video);
        }, 1000);
    });

    const canvasFunction = () => {
        console.log("html2canvas ", html2canvas)
        html2canvas(box).then(canvas => {
            const imgStr = canvas.toDataURL("image/png");
            img.src = imgStr;
            img.onload = function () {
                ctx.drawImage(img, 0, 0, w, h);
            }
        });
    }
});
`

弊端

使用html2canvas可能会对浏览器的性能造成一定的影响,特别是在处理大量或复杂的DOM元素时。这是因为html2canvas会遍历DOM树,并对每个元素进行渲染和截图处理。因此,如果页面包含大量复杂的内容,或者需要截取大尺寸的区域,可能会导致内存占用增加和CPU负载升高。

为了减少性能消耗,可以尝试以下方法:

  • 仅对需要截取的局部进行截图,而不是整个页面
  • 在进行截图时,尽量减少页面上的复杂动画和变化
  • 使用合适的配置选项,比如限制最大宽度和高度,或者关闭抗锯齿等选项

此外,在使用html2canvas时,最好在测试阶段对性能进行评估,确保在实际应用中不会对用户体验造成负面影响。

借助canvas去画网页内容,具体可参考 html2canvas ,实现方式为持续对用户访问的页面进行截图并上传到服务器。为了视频流畅,一秒中我们需要25张图,一张图300KB,也就是60秒产生的图片为440M,这么大的网络开销明显吃不消。

好用的插件

rrweb