2016-06-07 2 views
0

Я делаю приложение для рисования в реальном времени в холсте 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); 

Я думал об изменении цвета обратно на оригинал в методе colorChange после putpoint, но это не похоже на работу

+0

Вам нужно будет отслеживать «последнюю точку» каждого клиента и до выдачи 'context.lineTo (mouse.x, mouse.y)' делать 'moveTo' в« последнюю точку »клиента (который также будет применяться к цвету, чтобы вы могли установить правильный цвет клиента). – Prusse

+0

понял о цвете, но я немного не понимаю о точке, мне нужно будет перейти к старым точкам после каждой ничьей или после того, как другой клиент уволил событие mouseup –

+0

Попробуйте сделать ваш обработчик 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) 'add' if (mouse.last_x && mouse.last_y). moveTo (mouse.last_x, mouse.last_y); 'чтобы понять, о чем я говорю =) Надеюсь, что вы сможете выполнить остальные настройки. – Prusse

ответ

2

Некоторые аудиторные доски намеки:

Все следующий код является псевдо-код!

  • Используйте веб-порты для связи. Несколько популярных библиотек websocket: SocketIO и SignalR. Библиотеки 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); 
    } 
    
  • Есть сервер сделать некоторые важные задачи:

    • Добавить временную метку для каждой трансляции, если ваш выбор библиотеки веб-сокетов не включает в себя временную метку.

    • Сохраните все полученные команды рисования (базы данных), потому что все пойдет не так, и вам, вероятно, придется полностью повторно синхронизировать клиентов время от времени.

  • Mousemove пожарует около 30 раз в секунду, поэтому будет накоплено большое количество очков. Чтобы уменьшить размер передачи данных, рассмотрите возможность использования алгоритма сокращения пути для удаления избыточных точек. Один хороший алгоритм - Douglas Peucker path simplification algorithm.

Есть еще много хорошего приложения для доски, но это все, что у меня есть сейчас ... Удачи вам в вашем проекте! :-)

+1

Это лучшее объяснение, которое я прочитал о белой посадке. Большое спасибо!! :). –

1

Вам нужно будет следить за «последней точки» каждого клиента и до выдачи context.lineTo(mouse.x,mouse.y) сделайте moveTo «последней точке» клиента (который также будет применяться к цвету, чтобы вы могли установить правильный цвет клиента).

Чтобы получить Ideia вы можете попробовать сделать:

  • на обработчик 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);

Надеюсь, что вы можете выполнить остальные корректировки.

+1

Когда выкладывание его ** очень важно, чтобы все рисунки были «атомарными» ** - каждый рисунок пути должен быть полным, включая стиль: '.beginPath',' .moveTo', '.lineTo',' .strokeStyle = 'something'', 'stroke'. ** Не оставляйте путь открытым: ** Поэтому не создавайте приложение сокета для 'ent' &' disengage' (что оставляет операцию рисования неполной). Это означает, что вы должны ** дождаться завершения операции перетаскивания пользователей ** перед началом полной операции рисования. – markE

+1

hi @markE, если я понял вас правильно, я не должен излучать события, пока пользователь перетаскивает мышь, тогда как я покажу, что пользователь нарисовал всем другим пользователям в реальном времени?Не могли бы вы немного объяснить, может быть, с ответом, Спасибо :) –

Смежные вопросы