簡體   English   中英

兩人在同一塊畫布上繪圖

[英]two people drawing on same canvas

我正在html5 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庫是SocketIOSignalR 不支持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庫沒有自動包含時間戳,則為每個廣播添加一個時間戳。

    • 保存所有收到的繪圖命令(數據庫),因為出問題了,您可能需要不時完全重新同步客戶端。

  • Mousemove每秒大約發射30次,因此會累積大量點。 為了減少數據傳輸的大小,請考慮使用路徑縮減算法來刪除冗余點。 一種很好的算法是Douglas Peucker路徑簡化算法

好的白板應用程序還有很多,但這就是我目前為止的所有時間。祝您項目順利! :-)

您需要跟蹤每個客戶端的“最后一點”,並在發出context.lineTo(mouse.x,mouse.y)之前將moveTo移至客戶端的“最后一點”(這也將應用於顏色),因此您可以設置正確的客戶顏色)。

要獲得想法,您可以嘗試執行以下操作:

  • 在您的mousedown處理程序( socketEngage )上進行操作(以及此處的原始代碼)

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

(功能開始)

  • 在您的mousemove處理程序( socketPutPoint )中

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

(功能開始)

  • putPoint之前context.lineTo(mouse.x,mouse.y)

    如果(mouse.last_x && mouse.last_y)context.moveTo(mouse.last_x,mouse.last_y);

希望您可以進行其余的調整。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM