Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Undo Redo for Fabric.js

I'm trying to add undo/redo functionality to my Fabric.js canvas. My idea is to have a counter which counts canvas modifications (right now it counts the addition of objects). I have a state array, which pushes the whole canvas as JSON to my array.

Then I simply want to recall the states with

canvas.loadFromJSON(state[state.length - 1 + ctr],

As the user clicks on undo, ctr will reduce by one and load the state out of the array; as the user clicks on redo, ctr will increase by one and load the state out of the array.

When I experience this with simple numbers, everything works fine. With the real fabric canvas, I get some troubles --> it doesnt really work. I think this relies on my event handler

canvas.on({
   'object:added': countmods
});

jsfiddle is here:

here is the working numbers only example (results see console): jsFiddle

like image 684
gco Avatar asked Mar 11 '14 22:03

gco


2 Answers

I answered this on my own.

See jsfiddle:

What I did:

if (savehistory === true) {
    myjson = JSON.stringify(canvas);
    state.push(myjson);
} // this will save the history of all modifications into the state array, if enabled

if (mods < state.length) {
    canvas.clear().renderAll();
    canvas.loadFromJSON(state[state.length - 1 - mods - 1]);
    canvas.renderAll();
    mods += 1;
} // this will execute the undo and increase a modifications variable so we know where we are currently. Vice versa works the redo function.

Would still need an improvement to handle both drawings and objects. But that should be simple.

like image 160
gco Avatar answered Oct 16 '22 01:10

gco


You can use something like diff-patch or tracking object version. First, you listen to all object changes: object:created, object:modified...., save first snapshot of canvas by saving canvas.toObject() in a variable; For the next time, run diffpatcher.diff(snapshot,canvas.toObject()), and save only the patch. To undo, you can use diffpatcher.reverse these patch. To redo, just use function diffpatcher.patch. With this way, you can save memory, but cost more CPU usage.

With fabricjs you can use Object#saveState() and handling object:added to save original state to array(for undoing task), listening to object:modified, object:removing(for redoing task). This way is more lightweight and quite easy to implement. moreIt'd better to limit your history length by using circle queue.

like image 5
o0omycomputero0o Avatar answered Oct 16 '22 01:10

o0omycomputero0o