我正在 html5 canvas 中制作一个实时绘画应用程序。当单个用户在 Canvas 上绘图时,一切都会很好,但是当两个用户同时绘图时,一切都会变得困惑,例如,如果其中一个用户更改了颜色,则所有客户端的颜色都会更改,并且线条开始从一个点绘制到另一个点。 。如何解决这个问题?谢谢,这是我的代码。
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 方法中的颜色更改回原始颜色,但这似乎不起作用
最佳答案
一些白板提示:
以下所有代码均为伪代码!
使用 websocket 进行通信。几个流行的 websocket 库是 SocketIO和 SignalR 。当不支持 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(); }
不要让路径保持开放:因此不要设计套接字应用程序来发送部分绘图点(这会使绘图操作不完整)。这意味着您应该等待用户拖动操作完成,然后再发出完整的绘图操作。
var isDown=false; var commands=[]; var points; var lastX,lastY; // on mousedown ... // reinitialize the accumulated points array // with the mousedown point function handleMouseDown(e){ // tell the browser we're handling this event e.preventDefault(); e.stopPropagation(); // get mouse position lastX=parseInt(e.clientX-offsetX); lastY=parseInt(e.clientY-offsetY); // reset the accumulated points array // add the point to the accumulated points array points=[ {x:lastX, y:lastY} ]; // set the isDown flag isDown=true; } // on mousemove ... // add the current mouse position to the accumulated points array function handleMouseMove(e){ if(!isDown){return;} // tell the browser we're handling this event e.preventDefault(); e.stopPropagation(); // get mouse position mouseX=parseInt(e.clientX-offsetX); mouseY=parseInt(e.clientY-offsetY); // draw the newest local path segment // so the local user can see while they're drawing context.beginPath(); context.moveTo(lastX,lastY); context.lineTo(mouseX,mouseY); context.stroke(); // save the last x,y lastX=mouseX; lastY=mouseY; // add the point to the accumulated points array points=[ {x:mouseX, y:mouseY} ]; } // on mouseup ... // end the current draw operation // and add the points array to the commands array function handleMouseOut(e){ // tell the browser we're handling this event e.preventDefault(); e.stopPropagation(); // clear the isDown flag isDown=false; // add the current set of points // to the accumulated commands array commands.push({ client:myName, stroke:myCurrentStrokeColor, points:points }); }
使用单独的循环将本地绘图命令发送到服务器并绘制传入的远程绘图命令:
// 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 库不会自动包含时间戳,请为每个广播添加时间戳。
保存所有收到的绘图命令(数据库),因为出现问题,您可能必须不时完全重新同步客户端。
鼠标移动每秒发射约 30 次,因此会积累大量积分。为了减少数据传输大小,可以考虑使用路径缩减算法来删除冗余点。一种很好的算法是 Douglas Peucker path simplification algorithm .
一个好的白板应用程序还有很多东西,但这就是我现在所拥有的时间......祝你的项目好运! :-)
关于javascript - 两个人在同一张 Canvas 上作画,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37683190/