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.