Node.js实时截屏实现方案

发布于:2025-09-15 ⋅ 阅读:(24) ⋅ 点赞:(0)

前端:

<!DOCTYPE html>
<html lang="zh">
	<head>
		<meta charset="UTF-8" />
		<meta name="viewport" content="width=device-width, initial-scale=1.0" />
		<title>实时截屏</title>
		<style>
			html,body {
				margin: 0;
				overflow: hidden;
				width: 100%;
				height: 100%;
			}

			img {
				width: 100%;
				height: 100%;
			}
		</style>
	</head>
	<body>
		<img id="screen" alt="Screen Feed" />

		<script>
			const img = document.getElementById('screen');
			const ws = new WebSocket('ws://localhost:8080'); // 改成服务器 IP 如果跨机

			ws.onmessage = (event) => {
				// event.data 是 base64 图片
				const img = document.getElementById('screen');
				img.src = event.data;

				// 计算合适的宽度和高度
				const containerWidth = document.body.clientWidth; // 容器宽度
				const aspectRatio = img.naturalWidth / img.naturalHeight; // 原始宽高比

				if (img.naturalWidth > containerWidth) {
					img.style.width = `${containerWidth}px`;
					img.style.height = `${containerWidth / aspectRatio}px`;
				} else {
					img.style.width = `${img.naturalWidth}px`;
					img.style.height = `${img.naturalHeight}px`;
				}

				img.src = event.data;
			};

			ws.onopen = () => {
				console.log('Connected to screen capture server');
			};

			ws.onclose = () => {
				console.log('Disconnected');
				img.src = '';
			};

			ws.onerror = (err) => {
				console.error('WebSocket error:', err);
			};
		</script>
	</body>
</html>

服务端:

注意:
robotjs:用于截屏(仅支持桌面环境:Windows/macOS/Linux)
canvas:Node.js 中处理图像(需安装 node-canvas,可能需要系统依赖)
Ubuntu:

sudo apt-get install build-essential libcairo2-dev libpango1.0-dev libjpeg-dev libgif-dev librsvg2-dev

macOS:

brew install pkg-config cairo pango libpng jpeg giflib librsvg

Windows: 推荐使用 windows-build-tools(Node 14+ 可能不需要)

初始化项目

npm init -y
npm install ws robotjs canvas
// server.js
const WebSocket = require('ws');
const robot = require('robotjs');
const { createCanvas } = require('canvas');

// 创建 WebSocket 服务器
const wss = new WebSocket.Server({ port: 8080 });
console.log('WebSocket server running on ws://localhost:8080');

// 获取屏幕尺寸
const screen = robot.getScreenSize();
let width = screen.width;
let height = screen.height;
width = 1920;
height = 1080;
// 创建与屏幕大小相同的 Canvas
const canvas = createCanvas(width, height);
const ctx = canvas.getContext('2d');

wss.on('connection', (ws) => {
  console.log('Client connected');

  // 每 100ms 截屏一次(约 10 FPS)
  const interval = setInterval(() => {
    try {
      // 1. 使用 robotjs 截取屏幕
      const img = robot.screen.capture(0, 0, width, height);
      if (!img) {
        console.error('Failed to capture screen: no image data');
        return;
      }

      // 2. 创建 ImageData 并填充像素
      const imageData = ctx.createImageData(width, height);
      const pixelBuffer = img.image; // 原始像素数据(BGR 格式)

      for (let y = 0; y < height; y++) {
        for (let x = 0; x < width; x++) {
          const idx = (y * width + x) * 4;       // ImageData 索引(RGBA)
          const pixelIdx = (y * width + x) * 4;  // robotjs 像素索引(BGR + 未使用)

          // robotjs 返回的是 BGR + 未使用字节,顺序为 [B, G, R, ?]
          imageData.data[idx]     = pixelBuffer[pixelIdx + 2]; // R
          imageData.data[idx + 1] = pixelBuffer[pixelIdx + 1]; // G
          imageData.data[idx + 2] = pixelBuffer[pixelIdx];     // B
          imageData.data[idx + 3] = 255;                       // A(不透明)
        }
      }

      // 3. 将 ImageData 绘制到 Canvas
      ctx.putImageData(imageData, 0, 0);

      // 4. 转为 JPEG Base64(压缩,质量 50%)
      const dataUrl = canvas.toDataURL('image/jpeg', 1.0);

      // 5. 发送给客户端
      ws.send(dataUrl);

    } catch (err) {
      console.error('Capture error:', err.message || err);
    }
  }, 100); // 100ms = 10 FPS

  // 客户端断开时清理
  ws.on('close', () => {
    console.log('Client disconnected');
    clearInterval(interval);
  });

  // 处理错误
  ws.on('error', (err) => {
    console.error('WebSocket error:', err);
  });
});

// 错误处理
wss.on('error', (err) => {
  console.error('WebSocket Server error:', err);
  if (err.code === 'EADDRINUSE') {
    console.error('端口 8080 已被占用,请关闭其他程序或更换端口。');
  }
});

效果:
请添加图片描述