简体   繁体   中英

Simulating free drawing of path objects in fabric.js

I'm trying to simulate the free drawing of a path object inside a fabric.js canvas.

I have a "recording" of the steps inside a javascript object such as:

var dataset = [{
  "type": "move",
  "value": [
    [30, 257, "2022-03-22T10:16:56.882Z"],
    [58, 271, "2022-03-22T10:16:56.898Z"],
    [79, 281, "2022-03-22T10:16:56.914Z"],
    [91, 282, "2022-03-22T10:16:56.931Z"],
    [95, 282, "2022-03-22T10:16:56.948Z"],
    [96, 281, "2022-03-22T10:16:56.963Z"],
    [97, 281, "2022-03-22T10:16:56.981Z"]
  ]
},
{
  "type": "draw",
  "value": [
    [97, 281, "2022-03-22T10:16:57.046Z"],
    [98, 281, "2022-03-22T10:16:57.070Z"],
    [103, 279, "2022-03-22T10:16:57.083Z"],
    [106, 278, "2022-03-22T10:16:57.098Z"],
    [114, 274, "2022-03-22T10:16:57.114Z"],
    [133, 268, "2022-03-22T10:16:57.130Z"]
  ]
}]

value is an array of array containing the coordinates (x, y) on the canvas, and the timestamp. It is the result of a recording of the user interactions on the canvas.

I managed to recreate a moving cursor ( "type": "move" ) but I have troubles recreating the drawing process ( "type": "draw" ) as it involves three mouse events ('mousedown', 'mousemove' and 'mouseup').

For now, I'm using two nested asynchronous functions to navigate inside the object and move the cursor around the canvas.

var time;

autoDrawing(dataset, function() {
   console.log('finished');
});

async function autoDrawing(dataset, callback) {
   time = new Date(dataset[0].time);
   for (let i = 0; i < dataset.length; ++i) {
       await draw(dataset[i]);
   }
   callback();
}

function draw(data) {
    return new Promise(resolve => {
        if (data.type === 'move') {
            console.log('moving');
            moveCursor(data.value, false, function() { resolve('') });
        } else if (data.type === 'draw') {
            console.log('drawing');
            moveCursor(data.value, true, function() { resolve('') });
        } else {
            resolve('');
        }
   });
}

async function moveCursor(moves, draw, callback) {
     // The canvas object is stored inside the DOM element, in the fabric property.
     // This is because I have different canvas for different layers.
     let activecanvas = document.getElementsByClassName('canvas-container active')[0].firstChild.fabric;
     if (draw) {
          activecanvas.fire('mouse:down', {
              x: moves[0][0],
              y: moves[0][1]
          });
          activecanvas.renderAll();
     }
     for (j = 0; j < moves.length; j++) {
          await movement(moves[j], draw);
     }
     if (draw) {
          activecanvas.fire('mouse:up', {
              x: moves[moves.length - 1][0],
              y: moves[moves.length - 1][1]
          });
          activecanvas.renderAll();
     }
     callback();

     function movement(m, draw) {
         return new Promise(function(resolve, reject) {
              let newtime = new Date(m[2]);
              if (draw) {
                   activecanvas.fire('mouse:move', {
                        x: m[0],
                        y: m[1]
                   });
                   activecanvas.renderAll();
              }
              // mousecursor is a circle that substitutes the mouse cursor on an other canvas.
              mousecursor.set({
                   top: m[1],
                   left: m[0]
              }).setCoords().canvas.renderAll();
              // waitMap is just a setTimeout with the duration/callback position inverted
              waitMap((newtime - time) / 1000, function() {
                   time = newtime;
                   resolve('')
              })
         })
     }
}

I'm using the fire method of a fabric.js canvas but I didn't find any examples, so maybe I'm not using it the right way.

I managed to make it work by using javascript mouse events. I fire a mousedown event on the upper fabric.js canvas, then I fire multiple mousemove events and finally a mouseup event. To sum up:

let activecanvas = document.getElementsByClassName('canvas-container active')[0].getElementsByClassName('upper-canvas')[0];
if (draw) { simulateMouseEvent(activecanvas, 'mousedown', moves[0][0], moves[0][1]); }
for (j = 0; j < moves.length; j++) {
     await movement(moves[j], draw);
}
if (draw) { simulateMouseEvent(activecanvas, 'mouseup', moves[moves.length - 1][0], moves[moves.length - 1][1]); }

function movement(m, draw) {
      return new Promise(function(resolve, reject) {
             let newtime = new Date(m[2]);
             if (draw) { simulateMouseEvent(activecanvas, 'mousemove', m[0], m[1]); }
             mousecursor.set({
                  top: m[1],
                  left: m[0]
             }).setCoords().canvas.renderAll();
             waitMap((newtime - time) / 1000, function() {
                  time = newtime;
                  resolve('')
             })
      })
}

function simulateMouseEvent(e, eventName, x, y) {
    e.dispatchEvent(new MouseEvent(eventName, {
        view: window,
        bubbles: true,
        cancelable: true,
        clientX: x,
        clientY: y,
        button: 0
    }));
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM