开源技术小栈开源技术小栈
🌐 Home
⛄ 精选文章
🐜 数据库
Ⓜ️ 微服务
📺 Bilibili
🐳 Github
🎃 腾讯云社区
💫 博客园
  • 💫 Git
  • 🐜 Linux
  • 🕸️ Docker
  • 🐌 Jenkins
🌐 Home
⛄ 精选文章
🐜 数据库
Ⓜ️ 微服务
📺 Bilibili
🐳 Github
🎃 腾讯云社区
💫 博客园
  • 💫 Git
  • 🐜 Linux
  • 🕸️ Docker
  • 🐌 Jenkins
  • 0X01 2025

    • 🔶 大模型编程0X01 | 调用 API 完成内容分类
    • 🔶 PHP8.5 新增管道操作符
    • 🔶 Redis搭建高并发电商秒杀系统
    • 🔶 MQTT与PHP使用实时消息传递指南
    • 🔶 MQTT消息协议初学者指南
  • 0X01 2024

    • 🔷 SaaS多租户架构数据源动态切换解决方案
    • 🔶 HTTP分块Chunk传输让客户端响应更迅速数据
    • 🔷 PHP使用JSONSchema进行API模式验证和类型检查
    • 🔶 PHP日志管理神器Monolog
    • 🔷 使用PHP创建和管理自己的JWT令牌
    • 🔶 谁说PHP不能异步和并行运行?
    • 🔷 webman使用RabbitMQ消息中间件实现系统异步解耦实战教程
    • 🔶 基于Websocket和Canvas实现多人协作实时共享白板
    • 🔶 PHP高性能纯协程网络通信引擎Swow
    • 🔶 webman如何使用swoole事件驱动和协程?
    • 🔶 高性能PHP框架webman协程插件入门实战
    • 🔶 编译PHP 8.3并快速启动ThinkPHP 8.0框架项目
    • 🔶 【转载】漫谈Swoole协程与异步IO

概述

本文主要基于 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

协同画板实现效果

最近更新: 2024/7/11 11:39
贡献者: 万少波
Prev
🔷 webman使用RabbitMQ消息中间件实现系统异步解耦实战教程
Next
🔶 PHP高性能纯协程网络通信引擎Swow