You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于React TSX的Chrome扩展屏幕捕获功能交互实现问题

解决方案:Chrome扩展中React TS实现点击触发屏幕捕获并展示

一、核心前提配置(manifest.json)

首先确保你的manifest.json(Manifest V3)包含必要权限:

{
  "manifest_version": 3,
  "name": "你的扩展名称",
  "version": "1.0",
  "permissions": ["desktopCapture", "activeTab"],
  "background": {
    "service_worker": "background.js"
  },
  // 如果表单是通过content script注入网页:
  "content_scripts": [
    {
      "matches": ["<all_urls>"], // 替换为你需要注入的网站
      "js": ["dist/content.js"] // React编译后的content script文件
    }
  ],
  // 如果表单在扩展popup中:
  "action": {
    "default_popup": "dist/index.html"
  }
}

二、分场景实现逻辑

场景1:表单通过content script注入到网页

由于content script无法直接调用chrome.desktopCapture,需要通过消息传递让后台帮忙获取捕获权限,再在content script中处理截图逻辑。

1. 后台service worker(background.js)
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
  if (request.action === 'getCaptureStreamId') {
    // 弹出选择窗口让用户选屏幕/窗口
    chrome.desktopCapture.chooseDesktopMedia(
      ['screen', 'window'],
      sender.tab,
      (streamId) => {
        sendResponse(streamId ? { streamId } : { error: '用户取消了捕获' });
      }
    );
    // 标记为异步响应
    return true;
  }
});
2. React TS组件代码
import React, { useRef } from 'react';

const CaptureForm = () => {
  const screenshotImgRef = useRef<HTMLImgElement>(null);

  const handleCaptureClick = async () => {
    if (!window.chrome?.runtime) {
      console.error('当前环境不是Chrome扩展');
      return;
    }

    try {
      // 1. 向后台请求捕获权限,获取streamId
      const response = await chrome.runtime.sendMessage({ action: 'getCaptureStreamId' });
      if (response.error) throw new Error(response.error);
      const { streamId } = response;

      // 2. 通过streamId获取媒体流
      const mediaStream = await navigator.mediaDevices.getUserMedia({
        video: {
          mandatory: {
            chromeMediaSource: 'desktop',
            chromeMediaSourceId: streamId,
            maxWidth: window.screen.width,
            maxHeight: window.screen.height,
          },
        },
      });

      // 3. 捕获视频帧并转换为图片
      const videoTrack = mediaStream.getVideoTracks()[0];
      const imageCapture = new ImageCapture(videoTrack);
      const frameBitmap = await imageCapture.grabFrame();

      // 4. 将Bitmap转为Data URL用于展示
      const canvas = document.createElement('canvas');
      canvas.width = frameBitmap.width;
      canvas.height = frameBitmap.height;
      const ctx = canvas.getContext('2d');
      ctx?.drawImage(frameBitmap, 0, 0);
      const screenshotDataUrl = canvas.toDataURL('image/png');

      // 5. 停止媒体流,释放资源
      videoTrack.stop();
      mediaStream.getTracks().forEach(track => track.stop());

      // 6. 展示截图到指定区域
      if (screenshotImgRef.current) {
        screenshotImgRef.current.src = screenshotDataUrl;
      }
    } catch (err) {
      console.error('屏幕捕获失败:', err);
      alert(`捕获出错:${(err as Error).message}`);
    }
  };

  return (
    <div className="capture-form">
      <button onClick={handleCaptureClick} type="button">TAKE SCREEN SHOT</button>
      <div className="screenshot-display">
        <img ref={screenshotImgRef} alt="捕获的屏幕截图" />
      </div>
    </div>
  );
};

export default CaptureForm;

场景2:表单在扩展popup中

popup属于扩展内部上下文,可直接调用chrome.desktopCapture,无需后台中转,代码更简洁:

import React, { useRef } from 'react';

const PopupCaptureForm = () => {
  const screenshotImgRef = useRef<HTMLImgElement>(null);

  const handleCaptureClick = async () => {
    try {
      // 直接弹出捕获选择窗口
      const streamId = await new Promise<string | null>((resolve) => {
        chrome.desktopCapture.chooseDesktopMedia(
          ['screen', 'window'],
          null, // popup无需指定tab
          (id) => resolve(id)
        );
      });

      if (!streamId) throw new Error('用户取消了捕获');

      // 后续的流处理、截图转换、展示逻辑和场景1的步骤2-6完全一致
      const mediaStream = await navigator.mediaDevices.getUserMedia({
        video: {
          mandatory: {
            chromeMediaSource: 'desktop',
            chromeMediaSourceId: streamId,
            maxWidth: window.screen.width,
            maxHeight: window.screen.height,
          },
        },
      });

      const videoTrack = mediaStream.getVideoTracks()[0];
      const imageCapture = new ImageCapture(videoTrack);
      const frameBitmap = await imageCapture.grabFrame();

      const canvas = document.createElement('canvas');
      canvas.width = frameBitmap.width;
      canvas.height = frameBitmap.height;
      const ctx = canvas.getContext('2d');
      ctx?.drawImage(frameBitmap, 0, 0);
      const screenshotDataUrl = canvas.toDataURL('image/png');

      videoTrack.stop();
      mediaStream.getTracks().forEach(track => track.stop());

      if (screenshotImgRef.current) {
        screenshotImgRef.current.src = screenshotDataUrl;
      }
    } catch (err) {
      console.error('屏幕捕获失败:', err);
      alert(`捕获出错:${(err as Error).message}`);
    }
  };

  return (
    <div style={{ padding: '1rem', minWidth: '300px' }}>
      <button onClick={handleCaptureClick} type="button">TAKE SCREEN SHOT</button>
      <div style={{ marginTop: '1rem', border: '1px solid #ccc', padding: '0.5rem' }}>
        <img ref={screenshotImgRef} alt="捕获的截图" style={{ maxWidth: '100%' }} />
      </div>
    </div>
  );
};

export default PopupCaptureForm;

三、关键注意事项

  • 确保React编译后的文件正确注入到对应上下文(content script或popup)
  • 测试时需在Chrome扩展开发者模式下加载已解压的扩展包
  • ImageCapture API需要浏览器支持,Chrome已稳定支持
  • 捕获完成后务必停止媒体流,避免占用系统资源

内容的提问来源于stack exchange,提问作者dani_l_n

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 19:35:29