javascriptfabricjsfabricjs2

How to programmatically free draw using Fabric js?


Building a multiplayer doodle using fabric js.

Trying to implement multiplayer doodle using fabric js, the idea is when U1 draws on canvas we push the points to RTDB and get those points on both the client and programmatically draw the stroke in both the clients.


Solution

  • You can save the canvas' data on path:created for example (or other event) using toJSON().
    Send it to the server and the other client will load it using loadFromJSON().

    Update (4.3.1) (Thanks to @user8555937)

    const pointer = canvas.getPointer(e);
    const options = {pointer, e:{}} // required for Fabric 4.3.1
    
    canvas2.freeDrawingBrush.onMouseDown(pointer, options);
    

    var canvas = new fabric.Canvas(document.getElementById('canvasId'))
    canvas.isDrawingMode = true;
    canvas.freeDrawingBrush.width = 5;
    canvas.freeDrawingBrush.color = '#00aeff';
    
    canvas.on('path:created', function(e) {
      e.path.set();
      canvas.renderAll();
      drawOnCanvas(canvas.toJSON());
    });
    
    var canvas2 = new fabric.Canvas(document.getElementById('canvasId2'));
    function drawOnCanvas(json) {
      canvas2.loadFromJSON(json);
    }
    #app {
      display: flex;  
    }
    
    canvas {
      border: 1px solid red;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.19/fabric.min.js"></script>
    
    <div id="app">
      <canvas id="canvasId" width="400" height="400"></canvas>
      <canvas id="canvasId2" width="400" height="400"></canvas>
    </div>

    Probably you can optimize it by sending only the diffs and so on, but this is the start of the path

    Sync on drawing (not only after path:created)

    The idea is to "capture" the "original" canvas' events and trigger them on the second one.
    So, you can send the pointer to the server and trigger the events in the other clients.

    var canvas = new fabric.Canvas(document.getElementById('canvasId'))
    canvas.isDrawingMode = true;
    canvas.freeDrawingBrush.width = 5;
    canvas.freeDrawingBrush.color = '#00aeff';
    
    let isDrawing = false;
    canvas.on('mouse:down', function({e}) {
      isDrawing = true;
      onMouseDown(e);
    }).on('mouse:up', function({e}) {
      isDrawing = false;
      onMouseUp(e);
    }).on('mouse:move', function({e}) {
      if (isDrawing) {
        const pointer = canvas.getPointer(e);
        drawRealTime(e, pointer);
      }
    });
    
    var canvas2 = new fabric.Canvas(document.getElementById('canvasId2'));
    canvas2.isDrawingMode = true;
    canvas2.freeDrawingBrush.width = 5;
    canvas2.freeDrawingBrush.color = '#00aeff';
    
    function onMouseDown(e) {
      const pointer = canvas.getPointer(e);
      canvas2.freeDrawingBrush.onMouseDown(pointer);
    }
    
    function onMouseUp(e) {
      const pointer = canvas.getPointer(e);
      canvas2.freeDrawingBrush.onMouseUp(pointer);
    }
    
    function drawRealTime(e, pointer) {
      canvas2.freeDrawingBrush.onMouseMove(pointer);
    }
    #app {
      display: flex;  
    }
    
    canvas {
      border: 1px solid red;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.19/fabric.min.js"></script>
    
    <div id="app">
      <canvas id="canvasId" width="400" height="400"></canvas>
      <canvas id="canvasId2" width="400" height="400"></canvas>
    </div>

    https://codepen.io/moshfeu/pen/ZEGQEBO?editors=0010