💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
基于Websocket和Canvas实现多人协作实时共享白板 ## 概述 本文主要基于 Websocket、Canvas、Workerman、HTML5 CSS 等技术实现一个共享白板原型,支持在多个桌面浏览器之间共享一个无限大小、任意缩放的多人实时协作白板,并实时同步绘画、涂鸦。项目通过 Canvas 渲染画板、产生涂鸦数据。然后通过Websocket实现客户端与服务器的双向通信,并在多个设备之间实时同步涂鸦数据。 ### 画板协同 简单来说就是使用canvas开发一个可以多人共享的画板,都可以在上面作画画板,并且画面进行实时同步显示 ### 协同的方式 相当于创建一个房间,像微信的面对面建群一样,加入房间的用户之间可以进行消息通讯,其中一个客户端发布消息,其他的客户都会被分发消息,而达到的一种消息同步的效果 ## 协同画板实现 使用workerman作为Websocket消息订阅发布服务器 ### 安装think-template composer安装 ``` composer require topthink/think-template ``` 修改配置`config/view.php`为 ``` <?php use support\view\ThinkPHP; return [ 'handler' => ThinkPHP::class, ]; ``` ### HTTP服务 新建`app/controller/DemoController.php`控制器 ```php <?php /** * @desc Demo.php 描述信息 * @author Tinywan(ShaoBo Wan) * @date 2024/6/16 20:14 */ declare(strict_types=1); namespace app\controller; use support\Request; use support\Response; class DemoController { /** * @desc 实战构建多人互动画板 * @param Request $request * @return Response * @author Tinywan(ShaoBo Wan) */ public function canvas(Request $request): Response { return view('demo/canvas'); } } ``` 文件`app/view/demo/canvas.html`如下 ``` <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>开源技术小栈 WebSocket+Canvas: 实战构建多人互动画板</title> </head> <body> <canvas id="canvas" height="600" width="600" style="border:1px solid #000000;">你的浏览器不支持canvas,请升级浏览器</canvas> <script src="/static/js/index.js"></script> </body> </html> ``` `/static/js/index.js` 文件 ``` var el = document.getElementById('canvas'); el.width = document.body.clientWidth; el.height = document.body.clientHeight; var ctx = el.getContext('2d'); var isDrawing; var point = {}; ctx.strokeStyle = '#fffff'; var ws = new WebSocket('ws://192.168.13.168:8788'); console.log(ws) // 当连接成功后,再进行绘画操作,以免数据丢失 ws.onopen = function () { el.onmousedown = function (e) { isDrawing = true; ctx.moveTo(e.clientX,e.clientY); sendPoint(e,1); } el.onmousemove = function (e) { if(isDrawing){ ctx.lineTo(e.clientX,e.clientY); ctx.stroke(); sendPoint(e,2); } } el.onmouseup = function (e) { isDrawing = false; } } ws.onmessage = function (e) { var data = JSON.parse(e.data); if(data.type == 1){ ctx.moveTo(data.x,data.y); }else if(data.type == 2){ ctx.lineTo(data.x,data.y); ctx.stroke(); } } function sendPoint(e,type) { point = { type:type, x:e.clientX, y:e.clientY, } ws.send(JSON.stringify(point)); } ``` ### Websocket服务 这里通过webman自定义进程实现。在webman中你可以像workerman那样自定义监听或者进程。 新建 `process/CanvasWebsocket.php` ``` <?php /** * @desc CanvasWebsocket.php * @author Tinywan(ShaoBo Wan) * @date 2024/6/18 9:18 */ declare(strict_types=1); namespace process; use Workerman\Connection\TcpConnection; class CanvasWebsocket { public function onConnect(TcpConnection $connection) { echo "onConnect\n"; } public function onWebSocketConnect(TcpConnection $connection, $http_buffer) { echo "onWebSocketConnect\n"; } public function onMessage(TcpConnection $connection, $data) { foreach ($connection->worker->connections as $_connection) { if($connection != $_connection){ $_connection->send($data); } } } public function onClose(TcpConnection $connection) { echo "onClose\n"; } } ``` 在`config/process.php`中添加如下配置 ``` return [ // ... 其它进程配置省略 ... // canvas_websocket为进程名称 'canvas_websocket' => [ // 这里指定进程类,就是上面定义的Pusher类 'handler' => \process\CanvasWebsocket::class, 'listen' => 'websocket://0.0.0.0:8788', 'count' => 1, ], ]; ``` 启动webman ![](https://img.kancloud.cn/93/b3/93b35be4acf74000f6cc72cd2a0bd570_1221x200.png) ## 协同画板实现效果 ![](https://img.kancloud.cn/ab/70/ab70f1b7927ff994c4b789db3369038a_1493x710.png) ![](https://img.kancloud.cn/0f/f1/0ff1b897cf25f92cb4d78c59b4b74677_1496x710.png)