概述

本文主要基于 Websocket、Canvas、Webman、HTML5 CSS 等技术实现一个共享白板原型,支持在多个桌面浏览器之间共享一个无限大小、任意缩放的多人实时协作白板,并实时同步绘画、涂鸦。通过 Canvas 渲染画板、产生涂鸦数据,然后通过Websocket实现客户端与服务器的双向通信,并在多个设备之间实时同步涂鸦数据。

协同画板实现

使用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
/**
 * @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

协同画板实现效果

Last Updated:
贡献者: 万少波