如何在React项目的Excalidraw组件中实现实时协作功能?
实现Excalidraw组件的实时协作功能及显示协作按钮
要给你当前的Excalidraw组件加上实时协作功能和顶部协作按钮,需要从前端组件配置和后端协作服务两方面入手,以下是具体实现步骤:
1. 核心原理说明
@excalidraw/excalidraw包仅提供前端协作的UI和CRDT(无冲突复制数据类型)逻辑,完整的实时协作需要你搭建一个WebSocket服务器来处理客户端之间的操作同步——也就是把A用户的绘图操作广播给房间内的其他用户,同时同步协作成员状态。
2. 修改Excalidraw组件配置
更新你的excalidrawComponent.js,添加协作相关的props和WebSocket通信逻辑:
import { useState, useEffect, useRef } from "react"; export default function Excalidraw(props) { const [ExcalidrawComp, setExcalidrawComp] = useState(null); const [collaborators, setCollaborators] = useState([]); // 可改为用户输入或随机生成的房间ID,用于标识协作会话 const [roomId, setRoomId] = useState("demo-collab-room"); const wsConnection = useRef(null); // 懒加载Excalidraw组件 useEffect(() => { import("@excalidraw/excalidraw").then((mod) => setExcalidrawComp(mod.Excalidraw)); }, []); // 初始化WebSocket连接,处理协作数据同步 useEffect(() => { if (!ExcalidrawComp) return; // 替换成你的WebSocket服务地址,带上房间ID wsConnection.current = new WebSocket(`ws://your-collab-server-url/${roomId}`); wsConnection.current.onmessage = (event) => { const message = JSON.parse(event.data); switch (message.type) { case "collab-operation": // 同步其他用户的绘图操作到本地画布 ExcalidrawComp.updateScene(message.payload); break; case "collaborators-update": // 更新协作成员列表 setCollaborators(message.payload); break; } }; // 组件卸载时关闭连接 return () => wsConnection.current?.close(); }, [ExcalidrawComp, roomId]); // 本地操作触发时,发送到服务器广播给其他用户 const handleCollabUpdate = (update) => { if (wsConnection.current?.readyState === WebSocket.OPEN) { wsConnection.current.send(JSON.stringify({ type: "collab-operation", payload: update, roomId, user: props.currentUser || { name: "匿名用户", color: "#2196F3" } })); } }; return ( <div className="excalidraw-component"> {ExcalidrawComp && ( <ExcalidrawComp // 启用协作模式 isCollaborating={true} // 传递协作成员信息,画布上会显示对应光标 collaborators={collaborators} // 本地操作触发回调,用于同步到服务器 onCollabUpdate={handleCollabUpdate} // 显示顶部协作按钮 toolbarCustomizations={{ showCollaborationButton: true, onCollaborationButtonClick: () => { // 可自定义点击逻辑,比如弹出房间ID邀请框 alert(`当前协作房间ID:${roomId},分享给他人即可协作`); } }} // 设置当前用户信息,用于协作光标标识 currentUser={{ name: "你", color: "#FF5722" }} /> )} </div> ); }
3. 搭建WebSocket协作服务器
服务器需要实现以下核心功能:
- 支持房间创建/加入逻辑,根据
roomId分组客户端 - 接收客户端发送的绘图操作,广播给同房间的其他所有客户端
- 维护房间内的协作成员列表,当用户加入/离开时同步给所有成员
用Node.js + ws库快速搭建的核心逻辑示例:
const WebSocket = require('ws'); const wss = new WebSocket.Server({ port: 8080 }); // 存储房间和对应的客户端连接 const rooms = new Map(); wss.on('connection', (ws, req) => { // 从URL获取房间ID const roomId = req.url.slice(1); if (!rooms.has(roomId)) { rooms.set(roomId, new Set()); } const room = rooms.get(roomId); room.add(ws); // 发送当前房间成员列表(简化示例) const collaborators = Array.from(room).map((_, idx) => ({ name: `用户${idx+1}`, color: ['#FF5722', '#2196F3', '#4CAF50'][idx%3] })); broadcast(room, JSON.stringify({ type: "collaborators-update", payload: collaborators })); // 接收客户端操作并广播 ws.on('message', (data) => { broadcast(room, data.toString()); }); // 用户断开连接时更新成员列表 ws.on('close', () => { room.delete(ws); if (room.size === 0) { rooms.delete(roomId); } else { const updatedCollaborators = Array.from(room).map((_, idx) => ({ name: `用户${idx+1}`, color: ['#FF5722', '#2196F3', '#4CAF50'][idx%3] })); broadcast(room, JSON.stringify({ type: "collaborators-update", payload: updatedCollaborators })); } }); }); // 广播消息给房间内所有客户端 function broadcast(room, message) { room.forEach((client) => { if (client.readyState === WebSocket.OPEN) { client.send(message); } }); }
关键注意事项
- 确保WebSocket服务器配置跨域支持,否则前端会出现连接失败
- 如果不想自己搭建后端,可以用第三方实时数据库(如Firebase Realtime Database)替代WebSocket服务器,适配Excalidraw的CRDT同步逻辑
- 可以扩展房间ID的生成逻辑,比如随机生成唯一ID,或者让用户自定义
内容的提问来源于stack exchange,提问作者Sumit Kumar
相关产品推荐
相关产品推荐

