+
27
-

回答

我们从零开始的、详细的、分步的教程,来创建一个基础的录屏插件。这个插件将能够:

点击浏览器工具栏的图标,弹出一个小窗口。

在小窗口中点击“开始录制”按钮,触发浏览器原生屏幕选择界面。

用户选择要录制的屏幕、窗口或标签页后,开始录制。

点击“停止录制”按钮,结束录制。

生成一个可下载的视频文件(通常是 .webm 格式)。

我们将使用 Manifest V3,这是当前 Chrome 扩展的最新标准。

第一步:创建项目结构

首先,在你的电脑上创建一个新的文件夹,比如 screen-recorder-extension。然后在这个文件夹里,创建以下文件和目录

800_auto

icons/: 你可以先用任意图片作为占位符,或者从网上找一些免费图标。这些图标会显示在浏览器工具栏和扩展管理页面。

第二步:编写 manifest.json (清单文件)

这是扩展的核心配置文件,它告诉 Chrome 关于这个插件的所有信息,包括名称、权限、脚本等。manifest.json

{
  "manifest_version": 3,
  "name": "简易录屏插件",
  "version": "1.0",
  "description": "一个简单的Chrome录屏插件,使用 getDisplayMedia API。",
  "permissions": [
    "storage" 
  ],
  "background": {
    "service_worker": "background.js"
  },
  "action": {
    "default_popup": "popup.html",
    "default_icon": {
      "16": "icons/icon16.png",
      "48": "icons/icon48.png",
      "128": "icons/icon128.png"
    }
  },
  "icons": {
    "16": "icons/icon16.png",
    "48": "icons/icon48.png",
    "128": "icons/icon128.png"
  }
}

关键点解释:

"manifest_version": 3: 声明我们使用 Manifest V3。

"permissions": ["storage"]: 我们请求 storage 权限,用来在不同脚本之间(比如 popup 和 background)同步录制状态。

"background": { "service_worker": "background.js" }: 指定 background.js 作为我们的 Service Worker。在 V3 中,后台脚本在需要时运行,而不是持续存在,这对于处理录制这种需要持久状态的任务至关重要。

"action": { ... }: 定义了用户在浏览器工具栏上看到和交互的内容。点击图标时,会弹出 popup.html。

第三步:创建用户界面 (popup.html 和 popup.js)

这是用户直接交互的部分。

popup.html

<!DOCTYPE html>
<html>
<head>
    <title>录屏插件</title>
    <style>
        body { width: 200px; font-family: sans-serif; text-align: center; }
        button { width: 120px; margin: 5px; padding: 10px; }
        #status { font-weight: bold; margin: 10px 0; }
        #downloadLink { display: none; }
    </style>
</head>
<body>
    <h3>简易录屏</h3>
    <p id="status">空闲</p>
    <button id="startBtn">开始录制</button>
    <button id="stopBtn" disabled>停止录制</button>
    <a id="downloadLink" download="recording.webm">下载视频</a>
</body>
<script src="popup.js"></script>
</html>

popup.js

这个脚本负责处理 popup.html 中的按钮点击事件,并与后台脚本 background.js 通信。

const startBtn = document.getElementById('startBtn');
const stopBtn = document.getElementById('stopBtn');
const status = document.getElementById('status');
const downloadLink = document.getElementById('downloadLink');

// 更新UI状态
function updateUI(isRecording, videoUrl = null) {
    startBtn.disabled = isRecording;
    stopBtn.disabled = !isRecording;
    status.textContent = isRecording ? '正在录制...' : '空闲';
    
    if (videoUrl) {
        downloadLink.href = videoUrl;
        downloadLink.style.display = 'block';
        status.textContent = '录制完成!';
    } else {
        downloadLink.style.display = 'none';
    }
}

// 监听开始按钮点击
startBtn.addEventListener('click', () => {
    // 向 background.js 发送消息,请求开始录制
    chrome.runtime.sendMessage({ type: 'START_RECORDING' }, (response) => {
        if (response && response.success) {
            updateUI(true);
        } else {
            console.error("无法开始录制:", response.error);
            status.textContent = '错误:无法开始';
        }
    });
});

// 监听停止按钮点击
stopBtn.addEventListener('click', () => {
    // 向 background.js 发送消息,请求停止录制
    chrome.runtime.sendMessage({ type: 'STOP_RECORDING' });
});

// 监听来自 background.js 的消息,例如录制已停止
chrome.runtime.onMessage.addListener((message) => {
    if (message.type === 'RECORDING_STOPPED') {
        updateUI(false, message.url);
    }
});

// 当 popup 打开时,检查当前录制状态
chrome.storage.local.get(['isRecording', 'videoUrl'], (result) => {
    updateUI(result.isRecording, result.videoUrl);
});

第四步:编写核心逻辑 (background.js)

这是插件的大脑,它在后台运行,处理实际的屏幕捕获和录制工作。background.js

let mediaRecorder;
let recordedChunks = [];
let stream;

// 监听来自 popup.js 的消息
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
    if (request.type === 'START_RECORDING') {
        startRecording().then(() => {
            sendResponse({ success: true });
        }).catch(err => {
            sendResponse({ success: false, error: err.message });
        });
        return true; // 表示我们将异步发送响应
    } else if (request.type === 'STOP_RECORDING') {
        stopRecording();
        sendResponse({ success: true });
    }
});

async function startRecording() {
    try {
        // 1. 获取屏幕捕捉的 MediaStream
        stream = await navigator.mediaDevices.getDisplayMedia({
            video: { mediaSource: 'screen' },
            audio: true // 如果需要录制系统或麦克风声音
        });

        // 2. 创建 MediaRecorder 实例
        mediaRecorder = new MediaRecorder(stream, { mimeType: 'video/webm' });
        
        // 3. 清空之前的数据块
        recordedChunks = [];

        // 4. 监听 dataavailable 事件,收集数据块
        mediaRecorder.ondataavailable = (event) => {
            if (event.data.size > 0) {
                recordedChunks.push(event.data);
            }
        };

        // 5. 监听 stop 事件,当录制停止时处理数据
        mediaRecorder.onstop = () => {
            const blob = new Blob(recordedChunks, { type: 'video/webm' });
            const url = URL.createObjectURL(blob);
            
            // 将录制状态和视频URL存入storage
            chrome.storage.local.set({ isRecording: false, videoUrl: url });
            
            // 向 popup 发送消息,通知录制已停止并附上URL
            chrome.runtime.sendMessage({ type: 'RECORDING_STOPPED', url: url });
            
            // 你也可以选择直接打开下载页面
            // chrome.tabs.create({ url: url });
        };
        
        // 6. 开始录制
        mediaRecorder.start();

        // 7. 更新状态
        await chrome.storage.local.set({ isRecording: true, videoUrl: null });

        // 当用户手动停止屏幕共享时(例如点击浏览器自带的停止共享按钮)
        stream.getVideoTracks()[0].onended = () => {
            stopRecording();
        };

    } catch (error) {
        console.error('Error starting recording:', error);
        await chrome.storage.local.set({ isRecording: false });
        throw error;
    }
}

function stopRecording() {
    if (mediaRecorder && mediaRecorder.state !== 'inactive') {
        mediaRecorder.stop();
    }
    // 停止所有轨道,这样浏览器上的 "正在共享屏幕" 提示才会消失
    if (stream) {
        stream.getTracks().forEach(track => track.stop());
    }
    // 清理
    stream = null;
    mediaRecorder = null;
}

关键 API 解释:

navigator.mediaDevices.getDisplayMedia(): 这是现代浏览器提供的标准 API,用于请求用户授权捕获屏幕、窗口或标签页。它会返回一个 MediaStream 对象。

MediaRecorder: 这个 API 接收一个 MediaStream,并将其录制成一个媒体文件。

mediaRecorder.ondataavailable: 当 MediaRecorder 收集到一块数据时,这个事件会被触发。我们将这些数据块(chunks)收集起来。

mediaRecorder.onstop: 当录制停止时,这个事件被触发。在这里,我们将所有收集到的数据块合并成一个 Blob 对象。

URL.createObjectURL(blob): 将 Blob 对象转换成一个临时的 URL,这个 URL 可以用于 <a> 标签的 href 属性,从而实现下载。

chrome.runtime.sendMessage: 用于在扩展的不同部分之间(如 popup 和 background)发送消息。

chrome.storage.local: 用于在扩展中存储少量数据,它可以在扩展的不同生命周期中保持存在。

第五步:加载和测试插件

打开 Chrome 浏览器,在地址栏输入 chrome://extensions 并回车。

在右上角,打开 “开发者模式” 的开关。

点击左上角的 “加载已解压的扩展程序” 按钮。

在弹出的文件选择器中,选择你创建的 screen-recorder-extension 整个文件夹。

如果一切顺利,你的插件图标就会出现在浏览器的工具栏上。

测试流程:

点击插件图标,弹出窗口。

点击“开始录制”。浏览器会弹出一个窗口,让你选择要录制的屏幕、应用窗口或标签页。

选择一个目标,点击“共享”。

此时,插件的 UI 会更新为“正在录制...”,并且浏览器的标签页或屏幕上会有正在共享的提示。

再次点击插件图标,然后点击“停止录制”。

录制停止,UI 更新,并出现一个“下载视频”的链接。

点击链接,即可下载你刚才录制的 webm 视频文件。

可能的改进和扩展方向

这个基础版本已经可以工作了,但还有很多可以优化的空间:

录制选项:在 popup.html 中添加选项,让用户选择是否录制麦克风声音、系统声音。

暂停和继续:使用 mediaRecorder.pause() 和 mediaRecorder.resume() 实现暂停/继续功能。

倒计时:开始录制前增加一个 3 秒倒计时。

更好的状态管理:使用 chrome.storage 更精细地管理状态,确保即使 popup 关闭,状态也能正确同步。

格式转换:录制的 webm 格式兼容性不如 mp4。你可以研究使用 ffmpeg.wasm 在浏览器端将 webm 转换为 mp4,但这会大大增加复杂性。

录制特定标签页:使用 chrome.tabCapture.capture() API 可以更精确地录制某个标签页的内容,且无需用户每次都选择。这需要额外的 "tabCapture" 权限。

网友回复

我知道答案,我要回答