Javascript 两个人在同一张画布上画画

Javascript 两个人在同一张画布上画画,javascript,html,canvas,socket.io,real-time,Javascript,Html,Canvas,Socket.io,Real Time,我正在html5画布上制作一个实时绘画应用程序。当一个用户在画布上绘制时,一切都会正常进行,但当两个用户同时绘制时,一切都会变得一团糟,例如,如果一个用户更改颜色,所有客户端的颜色都会更改,线条从一个点开始绘制到另一个点。如何解决这个问题?谢谢,这是我的密码 var canvas = document.getElementById("myCanvas"); var context = canvas.getContext("2d"); canvas.width="600"; canvas.heig

我正在html5画布上制作一个实时绘画应用程序。当一个用户在画布上绘制时,一切都会正常进行,但当两个用户同时绘制时,一切都会变得一团糟,例如,如果一个用户更改颜色,所有客户端的颜色都会更改,线条从一个点开始绘制到另一个点。如何解决这个问题?谢谢,这是我的密码

var canvas = document.getElementById("myCanvas");
var context = canvas.getContext("2d");
canvas.width="600";
canvas.height="500";
var radius = 10;
var mouse = {x:0,y:0};
var drag = false;
var imageObj = new Image();
  imageObj.onload = function() {
    context.drawImage(imageObj, 20, 20);
 };
  imageObj.src = 'rhino4.png';
$scope.colorChange = function(color){
  Socket.emit("colorChange",color);
};
Socket.on("colorChange",function (color) {
  context.strokeStyle = color;
  context.fillStyle = color;
})
$scope.radiusChange = function(size) {
  Socket.emit("radiusChange",size);
}
Socket.on("radiusChange",function (size) {
  radius = size;
  context.lineWidth = radius*2;
})
context.lineWidth = radius*2;
var putPoint = function (mouse) {
  if(drag){
    context.lineTo(mouse.x,mouse.y)
    context.stroke();
    context.beginPath();
    context.arc(mouse.x,mouse.y,radius,0,Math.PI*2);
    context.fill();
    context.beginPath();
    context.moveTo(mouse.x,mouse.y);
    context.globalCompositeOperation='source-atop';
    context.drawImage(imageObj, 20, 20);
    context.globalCompositeOperation='source-over';
  }
}
Socket.on("putPoint",function (mouse) {
  putPoint(mouse);
});
var engage = function(mouse){
  console.log("in engage",mouse);
  drag = true;
  putPoint(mouse);
}
var disengage = function(){
  drag = false;
  context.beginPath();
}
var socketPutPoint = function(e){
  mouse.x = e.offsetX;
  mouse.y = e.offsetY;
  Socket.emit("putPoint",mouse);
}
Socket.on("engage",function (mouse) {
  console.log("engaging");
  engage(mouse);
});
var socketEngage = function (e) {
  mouse.x = e.offsetX;
  mouse.y = e.offsetY;
  console.log(mouse);
  Socket.emit("engage",mouse);
}
var socketDisengage = function (e) {
  mouse.x = e.offsetX;
  mouse.y = e.offsetY;
  console.log(mouse);
  Socket.emit("disengage",mouse);
}
Socket.on("disengage",function (mouse) {
  disengage();
})
canvas.addEventListener('mouseup',socketDisengage);
canvas.addEventListener('mouseleave',socketDisengage);
canvas.addEventListener('mousedown',socketEngage);
canvas.addEventListener('mousemove',socketPutPoint);

我曾想过在putpoint之后在colorChange方法中将颜色更改回原始颜色,但这似乎不起作用

您需要跟踪每个客户端的最后一点,然后再发布context.lineTomouse.x,y移动到客户端的最后一个点,该点也将应用于颜色,以便设置正确的客户端颜色

要获得理想,您可以尝试执行以下操作:

在mousedown处理程序socketEngage上添加原始代码

mouse.last_x=e.offsetX; mouse.last_y=e.offsetY

函数的开头

在mousemove处理程序socketPutPoint中

mouse.last_x=mouse.x; mouse.last_y=mouse.y

函数的开头

并在context.lineTomouse.x、mouse.y之前的putPoint上添加

如果mouse.last\u x&&mouse.last\u y context.moveTomouse.last\u x,mouse.last\u y


希望您可以进行其余的调整。

您需要跟踪每个客户端的最后一点,然后再发布上下文。lineTomouse.x,mouse.y移动到客户端的最后一点,该点也将应用于颜色,以便您可以设置正确的客户端颜色

要获得理想,您可以尝试执行以下操作:

在mousedown处理程序socketEngage上添加原始代码

mouse.last_x=e.offsetX; mouse.last_y=e.offsetY

函数的开头

在mousemove处理程序socketPutPoint中

mouse.last_x=mouse.x; mouse.last_y=mouse.y

函数的开头

并在context.lineTomouse.x、mouse.y之前的putPoint上添加

如果mouse.last\u x&&mouse.last\u y context.moveTomouse.last\u x,mouse.last\u y

希望您可以完成其余的调整。

一些白板提示:

以下所有代码都是伪代码

使用WebSocket进行通信。有几个流行的websocket库是和。当Websocket不受支持时,Websocket库通常具有回退方法

使用JSON序列化图形数据。JSON的好处在于它自动获取JavaScript对象/数组,并从中生成适合websocket传输的字符串。反之亦然:自动接收JSON字符串并将字符串重新水化为JavaScript对象/数组

var command = {
    client:'sam', 
    points:[{x:5,y:10},...],
    // optionally add styling (strokeStyle, linewidth, etc)
};

// serialize a command 
var jsonCommand = JSON.stringify(command);

// deserialize a command
var command = JSON.parse(jsonCommand);
这是非常重要的!要使所有图形保持原子状态,每个路径图形应完整,包括样式。不要启动context.beginPath并发出一系列context.lineTo

draw(command.points);

// ALWAYS issue complete drawing commands
// including styling (if any)
function draw(points);
    var ptsLength=points.length;
    context.beginPath;
    context.moveTo(points[0].x,points[0].y);
    for(var i=0;i<ptsLength;i++){
        var pt=points[i];
        context.lineTo(pt.x,pt.y);
    }
    context.stroke();
}
使用单独的循环向服务器发出本地绘图命令,并绘制传入的远程绘图命令:

// vars to schedule drawing from remote clients
// and sending local drawings to server
var nextDrawingTime, nextSendingTime;
var drawingTimeDelay=1000; // or some other delay, but don't be a burden!
var sendingTimeDelay=1000; // or some other delay, but don't be a burden!

// start the processing loop (it runs continuously non-stop)
requestAnimationFrame(process);

function process(time){

    // a simplification ...
    // don't interrupt if the local user is drawing
    if(isDown){ return; }

    // draw incoming strokes
    if(time>nextDrawingTime && receivedCommands.length>0){

        // set the next drawing time for remote draws
        nextDrawingTime=time+drawingTimeDelay;

        // draw all accumulated received commands
        for(var i=0;i<receivedCommands.length;i++){
            var c=receivedCommands[i];
            if(c.client!==myName){
                draw(c.points);
            }
        }
        receivedCommands.length=0;

    // emit outgoing strokes
    } else if(time>nextSendingTime && commands.length>0){

        // set the next emitting time for locally drawing paths
        nextSendingTime=time+sendingTimeDelay;

        // JSON.stringify
        var jsonPacket=JSON.stringify(commands);

        // reset the set of local drawing commands
        commands=[];

        // emit to server for broadcast to everyone

    }

    requestAnimationFrame(process);
}
让服务器执行一些重要任务:

如果您选择的websockets库没有自动包含时间戳,请为每个广播添加时间戳

将收到的所有图形命令保存到数据库中,因为出现问题,您可能需要不时地完全重新同步客户端

Mousemove每秒大约发射30次,因此会累积大量点数。为了减少数据传输的大小,考虑使用路径缩减算法来去除冗余点。一个好的算法是

一个好的白板应用程序还有很多,但这就是我现在所有的时间。。。祝你的项目好运!:-

一些白板提示:

以下所有代码都是伪代码

使用WebSocket进行通信。有几个流行的websocket库是和。当Websocket不受支持时,Websocket库通常具有回退方法

使用JSON序列化图形数据。JSON的好处在于它自动获取JavaScript对象/数组,并从中生成适合websocket传输的字符串。反之亦然:自动接收JSON字符串并将字符串重新水化为JavaScript对象/数组

var command = {
    client:'sam', 
    points:[{x:5,y:10},...],
    // optionally add styling (strokeStyle, linewidth, etc)
};

// serialize a command 
var jsonCommand = JSON.stringify(command);

// deserialize a command
var command = JSON.parse(jsonCommand);
这是非常重要的!要使所有图形保持原子状态,每个路径图形应完整,包括样式。不要启动context.beginPath并发出一系列context.lineTo

draw(command.points);

// ALWAYS issue complete drawing commands
// including styling (if any)
function draw(points);
    var ptsLength=points.length;
    context.beginPath;
    context.moveTo(points[0].x,points[0].y);
    for(var i=0;i<ptsLength;i++){
        var pt=points[i];
        context.lineTo(pt.x,pt.y);
    }
    context.stroke();
}
使用单独的循环向服务器发出本地绘图命令,并绘制传入的远程绘图命令:

// vars to schedule drawing from remote clients
// and sending local drawings to server
var nextDrawingTime, nextSendingTime;
var drawingTimeDelay=1000; // or some other delay, but don't be a burden!
var sendingTimeDelay=1000; // or some other delay, but don't be a burden!

// start the processing loop (it runs continuously non-stop)
requestAnimationFrame(process);

function process(time){

    // a simplification ...
    // don't interrupt if the local user is drawing
    if(isDown){ return; }

    // draw incoming strokes
    if(time>nextDrawingTime && receivedCommands.length>0){

        // set the next drawing time for remote draws
        nextDrawingTime=time+drawingTimeDelay;

        // draw all accumulated received commands
        for(var i=0;i<receivedCommands.length;i++){
            var c=receivedCommands[i];
            if(c.client!==myName){
                draw(c.points);
            }
        }
        receivedCommands.length=0;

    // emit outgoing strokes
    } else if(time>nextSendingTime && commands.length>0){

        // set the next emitting time for locally drawing paths
        nextSendingTime=time+sendingTimeDelay;

        // JSON.stringify
        var jsonPacket=JSON.stringify(commands);

        // reset the set of local drawing commands
        commands=[];

        // emit to server for broadcast to everyone

    }

    requestAnimationFrame(process);
}
让服务器执行一些重要任务:

如果您选择的websockets库没有自动包含时间戳,请为每个广播添加时间戳

将收到的所有图形命令保存到数据库中,因为出现问题,您可能需要不时地完全重新同步客户端

Mousemove每秒大约发射30次,因此会累积大量点数。减少数据量 传输大小,考虑使用路径缩减算法来去除冗余点。一个好的算法是


一个好的白板应用程序还有很多,但这就是我现在所有的时间。。。祝你的项目好运!:-

在发布context.lineTomouse.x,mouse.y之前,您需要跟踪每个客户端的最后一点,并将其移动到客户端的最后一点,该点也将应用于颜色,以便您可以设置正确的客户端颜色。了解颜色,但我对该点不太清楚,我是否必须在每次绘图后或在其他客户端启动mouseup事件后移动到旧点尝试在您的mousedown处理程序socketEngage上加上原始代码mouse.last_x=e.offsetX;mouse.last_y=e.offsetY;函数的开头,在mousemove处理程序socketPutPoint mouse.last_x=mouse.x;mouse.last_y=mouse.y;函数开头,在context.lineTomouse.x,mouse.y之前的putPoint上添加if mouse.last_x&&mouse.last_y context.moveTomouse.last_x,mouse.last_;要了解我在说什么=希望您可以进行其余的调整。感谢提示:我已固定了颜色,第一行几乎已固定,请写一个答案,以便我可以接受。您需要跟踪每个客户的最后一点,然后再发布上下文。lineTomouse.x,鼠标。y移动到客户端的最后一个点,该点也将应用于颜色,以便您可以设置正确的客户端颜色。了解颜色,但我对该点不太清楚,我是否必须在每次绘图后或在其他客户端启动mouseup事件后移动到旧点尝试在您的mousedown处理程序socketEngage上加上原始代码mouse.last_x=e.offsetX;mouse.last_y=e.offsetY;函数的开头,在mousemove处理程序socketPutPoint mouse.last_x=mouse.x;mouse.last_y=mouse.y;函数开头,在context.lineTomouse.x,mouse.y之前的putPoint上添加if mouse.last_x&&mouse.last_y context.moveTomouse.last_x,mouse.last_;要了解我在说什么=希望你能做其余的调整。感谢提示:我固定了颜色,第一行几乎固定了,请写一个答案,这样我可以接受在白板时保持所有图形原子化非常重要-每个路径图形都应完整,包括样式:.beginPath,.moveTo、.lineTo、.strokeStyle='something',stroke。不要让路径保持打开状态:因此不要设计一个套接字应用程序来接合和分离,这会导致绘图操作不完整。这意味着您应该等待用户拖动操作完成,然后再发出完整的绘图操作。hi@markE,如果我当时理解您的意思,我不应该在用户拖动鼠标时发出事件,那么我应该如何实时向所有其他用户显示用户所绘制的内容?请您解释一下,也许可以给出一个答案,谢谢:在白板上时,保持所有图形原子化非常重要-每个路径图形都应完整,包括样式:.beginPath、.moveTo、.lineTo、.strokeStyle='something',stroke。不要让路径保持打开状态:因此不要设计一个套接字应用程序来接合和分离,这会导致绘图操作不完整。这意味着您应该等待用户拖动操作完成,然后再发出完整的绘图操作。hi@markE,如果我当时理解您的意思,我不应该在用户拖动鼠标时发出事件,那么我应该如何实时向所有其他用户显示用户所绘制的内容?你能解释一下吗,也许可以回答一下,谢谢:这是我读过的关于白板的最好的解释。非常感谢!!:。这是我读过的关于白板的最好解释。非常感谢!!:。