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

如何在WebRTC+MERN视频聊天应用中实现协作白板同步?

协作白板同步解决方案

方案一:复用WebRTC DataChannel(推荐,无需额外服务)

既然你已经基于WebRTC实现了视频通话,直接用它的**数据通道(DataChannel)**传输绘图指令是最顺畅的选择,不用额外维护独立的WebSocket服务。

1. 初始化WebRTC数据通道

在管理WebRTC会话的Session.js中,创建并监听数据通道:

// Session.js
let dataChannel;

// 发起方创建数据通道
function createDataChannel(peerConnection) {
  dataChannel = peerConnection.createDataChannel('whiteboard');
  setupDataChannelListeners();
}

// 配置通道监听逻辑
function setupDataChannelListeners() {
  dataChannel.onopen = () => console.log('白板数据通道已建立');
  dataChannel.onmessage = (event) => {
    const drawData = JSON.parse(event.data);
    // 将远程绘图指令传给白板实例处理
    window.whiteboard.drawRemote(drawData);
  };
  dataChannel.onclose = () => console.log('白板数据通道已关闭');
}

// 接收方监听对方创建的通道
peerConnection.ondatachannel = (event) => {
  dataChannel = event.channel;
  setupDataChannelListeners();
};

// 导出发送方法,供白板调用
export function sendDrawData(drawData) {
  if (dataChannel && dataChannel.readyState === 'open') {
    dataChannel.send(JSON.stringify(drawData));
  }
}

2. 修改Whiteboard.js,捕获本地绘图并发送指令

在Whiteboard.js中,把每次本地绘制的动作(坐标、颜色、线宽等)打包成数据,调用上面的sendDrawData发送给对方:

// Whiteboard.js
import { sendDrawData } from './Session';

class Whiteboard {
  constructor(canvas) {
    this.canvas = canvas;
    this.ctx = canvas.getContext('2d');
    this.isDrawing = false;
    // 存储当前绘图的核心参数
    this.currentDrawData = {
      type: 'path',
      color: '#000',
      lineWidth: 2,
      points: []
    };
    this.setupEventListeners();
  }

  setupEventListeners() {
    this.canvas.addEventListener('mousedown', this.startDrawing.bind(this));
    this.canvas.addEventListener('mousemove', this.draw.bind(this));
    this.canvas.addEventListener('mouseup', this.endDrawing.bind(this));
  }

  startDrawing(e) {
    this.isDrawing = true;
    const rect = this.canvas.getBoundingClientRect();
    const x = e.clientX - rect.left;
    const y = e.clientY - rect.top;
    this.currentDrawData.points = [[x, y]];
    // 从Options.js获取当前设置的颜色、线宽
    this.currentDrawData.color = Options.getCurrentColor();
    this.currentDrawData.lineWidth = Options.getCurrentLineWidth();
  }

  draw(e) {
    if (!this.isDrawing) return;
    const rect = this.canvas.getBoundingClientRect();
    const x = e.clientX - rect.left;
    const y = e.clientY - rect.top;
    this.currentDrawData.points.push([x, y]);
    // 执行本地绘制
    this.drawLocal(this.currentDrawData);
  }

  endDrawing() {
    if (!this.isDrawing) return;
    this.isDrawing = false;
    // 将完整的绘图指令发送给对方
    sendDrawData(this.currentDrawData);
    this.currentDrawData = { ...this.currentDrawData, points: [] };
  }

  // 本地绘制逻辑(保留你原有的实现)
  drawLocal(data) {
    this.ctx.strokeStyle = data.color;
    this.ctx.lineWidth = data.lineWidth;
    this.ctx.beginPath();
    this.ctx.moveTo(...data.points[0]);
    for (let i = 1; i < data.points.length; i++) {
      this.ctx.lineTo(...data.points[i]);
    }
    this.ctx.stroke();
  }

  // 远程绘图重现:直接复用本地绘制逻辑
  drawRemote(data) {
    this.drawLocal(data);
  }
}

// 初始化白板实例,挂载到全局方便调用
window.whiteboard = new Whiteboard(document.getElementById('whiteboard-canvas'));

3. 验证WebRTC连接

确保你的PeerConnection已经成功建立(视频流正常传输),DataChannel会自动复用这个连接,无需额外配置。


方案二:修复WebSocket同步逻辑

如果坚持使用WebSocket,核心是确保客户端与服务器的消息收发逻辑正确:

1. 修正server.js的WebSocket处理

// server.js
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });

// 存储所有在线客户端
const clients = new Set();

wss.on('connection', (ws) => {
  clients.add(ws);
  console.log('新客户端接入');

  ws.on('message', (message) => {
    // 转发绘图指令给除发送方外的所有客户端
    clients.forEach((client) => {
      if (client !== ws && client.readyState === WebSocket.OPEN) {
        client.send(message);
      }
    });
  });

  ws.on('close', () => {
    clients.delete(ws);
    console.log('客户端断开连接');
  });
});

2. 客户端Whiteboard.js集成WebSocket

// Whiteboard.js
class Whiteboard {
  constructor(canvas) {
    this.canvas = canvas;
    this.ctx = canvas.getContext('2d');
    this.isDrawing = false;
    this.currentDrawData = { type: 'path', color: '#000', lineWidth: 2, points: [] };
    // 建立WebSocket连接
    this.ws = new WebSocket('ws://localhost:8080');
    this.setupWsListeners();
    this.setupEventListeners();
  }

  setupWsListeners() {
    this.ws.onopen = () => console.log('WebSocket连接已建立');
    this.ws.onmessage = (event) => {
      const drawData = JSON.parse(event.data);
      this.drawRemote(drawData);
    };
    this.ws.onclose = () => console.log('WebSocket连接已关闭');
  }

  // ... 保留原有startDrawing、draw、drawLocal方法 ...

  endDrawing() {
    if (!this.isDrawing) return;
    this.isDrawing = false;
    // 发送绘图指令到服务器
    this.ws.send(JSON.stringify(this.currentDrawData));
    this.currentDrawData = { ...this.currentDrawData, points: [] };
  }

  drawRemote(data) {
    this.drawLocal(data);
  }
}

关键注意事项

  • 精简传输数据:只传绘图的必要参数(类型、颜色、线宽、坐标点),不要传输canvas图片数据,避免性能损耗。
  • 同步初始状态:新用户加入时,可以将当前白板的绘图指令历史发送给对方,或者临时转base64图片同步初始内容。
  • 错误处理:添加DataChannel/WebSocket的异常捕获,比如连接断开时提示用户,或实现自动重连逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 19:44:56