前端录屏黑科技:几行 JS 代码实现网页录屏
在过去,实现网页录屏通常需要依赖复杂的浏览器插件、桌面应用或付费服务。这不仅增加了用户操作成本,也给开发者带来不小的开发压力。如今,借助现代 Web API,我们可以用原生 JavaScript 简单实现屏幕录制功能。
核心工具:MediaRecorder API
MediaRecorder API 简介
MediaRecorder 是 WebRTC 技术栈的一部分,它可以接收 MediaStream(音视频流),并将其编码成特定格式(如 WebM 或 MP4),输出为数据块(Blobs)。
媒体流来源广泛:
- 用户摄像头和麦克风:
navigator.mediaDevices.getUserMedia
- 用户屏幕、窗口或浏览器标签页:
navigator.mediaDevices.getDisplayMedia
<video>
或<audio>
元素- 动态生成的 Canvas 画布
本篇聚焦于 屏幕录制,通过 getDisplayMedia
获取屏幕流。
基础录屏三步走
1. 获取屏幕共享的媒体流
async function getScreenStream() {
try {
const stream = await navigator.mediaDevices.getDisplayMedia({
video: true, // 捕获屏幕视频
audio: true // 可选:捕获屏幕音频
});
return stream;
} catch (error) {
console.error("获取屏幕共享失败:", error);
alert("您取消了屏幕共享或发生错误。");
return null;
}
}
注意:
getDisplayMedia
必须由用户交互(如点击按钮)触发,否则浏览器会阻止调用。
2. 创建 MediaRecorder 实例并开始录制
let recordedChunks = [];
let mediaRecorder;
function startRecording(stream) {
mediaRecorder = new MediaRecorder(stream, { mimeType: 'video/webm' });
mediaRecorder.ondataavailable = (event) => {
if (event.data.size > 0) {
recordedChunks.push(event.data);
}
};
mediaRecorder.onstop = handleRecordingStop;
mediaRecorder.start();
console.log("录制已开始");
}
3. 停止录制并处理结果
function stopRecording() {
if (mediaRecorder && mediaRecorder.state !== 'inactive') {
mediaRecorder.stop();
}
}
function handleRecordingStop() {
const recordedBlob = new Blob(recordedChunks, { type: 'video/webm' });
const videoUrl = URL.createObjectURL(recordedBlob);
// 下载链接
const downloadLink = document.createElement('a');
downloadLink.href = videoUrl;
downloadLink.download = `recording-${new Date().toISOString()}.webm`;
downloadLink.textContent = '下载录屏';
document.body.appendChild(downloadLink);
// 视频预览
const previewVideo = document.createElement('video');
previewVideo.src = videoUrl;
previewVideo.controls = true;
document.body.appendChild(previewVideo);
// 清空数据块,为下一次录制做准备
recordedChunks = [];
}
通过按钮触发 startRecording
与 stopRecording
,即可实现一个完整的网页录屏功能。
MediaRecorder 的应用场景
- 用户反馈与 Bug 复现
- 在线教育与演示
- 屏幕录像或演示教程录制
注意事项与最佳实践
浏览器兼容性
- 支持 Chrome、Firefox、Edge、Safari 等现代浏览器
- 需检查兼容性,为不支持的浏览器提供降级方案
MIME 类型
- 不同浏览器支持不同格式
- 使用
MediaRecorder.isTypeSupported()
检查支持 video/webm
支持最广
用户授权
- 必须在用户明确操作后调用
getDisplayMedia
- 清晰告知权限用途
- 必须在用户明确操作后调用
资源管理
- 录制完成后使用
URL.revokeObjectURL(videoUrl)
释放内存 - 停止媒体流:
stream.getTracks().forEach(track => track.stop())
- 录制完成后使用
性能考量
- 长时间或高分辨率录制消耗大量内存和 CPU
- 上传场景可考虑分片上传,而不是一次完成
MediaRecorder API 的出现,让网页录屏变得前所未有的简单:几行 JavaScript 就能搞定完整功能,无需插件或第三方服务。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>网页录屏示例</title>
<style>
body { font-family: "Microsoft YaHei", sans-serif; padding: 20px; background: #f0f2f5; }
button { padding: 10px 20px; margin: 5px; font-size: 16px; cursor: pointer; }
video { display: block; margin-top: 20px; max-width: 100%; border: 1px solid #ccc; }
a { display: block; margin-top: 10px; font-size: 16px; color: #007bff; text-decoration: none; }
</style>
</head>
<body>
<h2>网页录屏示例</h2>
<button id="startBtn">开始录屏</button>
<button id="stopBtn" disabled>停止录屏</button>
<div id="previewContainer"></div>
<div id="downloadContainer"></div>
<script>
let mediaRecorder;
let recordedChunks = [];
let screenStream;
const startBtn = document.getElementById('startBtn');
const stopBtn = document.getElementById('stopBtn');
const previewContainer = document.getElementById('previewContainer');
const downloadContainer = document.getElementById('downloadContainer');
async function startRecording() {
try {
screenStream = await navigator.mediaDevices.getDisplayMedia({
video: true,
audio: true
});
mediaRecorder = new MediaRecorder(screenStream, { mimeType: 'video/webm' });
mediaRecorder.ondataavailable = (event) => {
if (event.data.size > 0) recordedChunks.push(event.data);
};
mediaRecorder.onstop = handleRecordingStop;
mediaRecorder.start();
console.log('录制已开始');
startBtn.disabled = true;
stopBtn.disabled = false;
} catch (err) {
console.error('获取屏幕失败:', err);
alert('获取屏幕失败或用户取消了操作。');
}
}
function stopRecording() {
if (mediaRecorder && mediaRecorder.state !== 'inactive') {
mediaRecorder.stop();
screenStream.getTracks().forEach(track => track.stop());
startBtn.disabled = false;
stopBtn.disabled = true;
}
}
function handleRecordingStop() {
const recordedBlob = new Blob(recordedChunks, { type: 'video/webm' });
const videoUrl = URL.createObjectURL(recordedBlob);
// 清空之前内容
previewContainer.innerHTML = '';
downloadContainer.innerHTML = '';
// 创建视频预览
const videoEl = document.createElement('video');
videoEl.src = videoUrl;
videoEl.controls = true;
previewContainer.appendChild(videoEl);
// 创建下载链接
const downloadLink = document.createElement('a');
downloadLink.href = videoUrl;
downloadLink.download = `recording-${new Date().toISOString()}.webm`;
downloadLink.textContent = '下载录屏';
downloadContainer.appendChild(downloadLink);
// 清空数据块
recordedChunks = [];
}
// 绑定按钮事件
startBtn.addEventListener('click', startRecording);
stopBtn.addEventListener('click', stopRecording);
</script>
</body>
</html>