FabricJs: Store and load canvas state reactively

Viewed 609

Using Meteor and React, I am building an application that has a fabric canvas in the middle, a 'components panel' on the left (a library of objects to add to the canvas), and a 'layers panel' on the right (similar to Photoshop, where each object on the canvas is represented as a layer with specific controls like color, opacity etc.).

So far I have managed to display the Layers Panel by using a Meteor session variable that contains an array for the objects on the canvas.

enter image description here

So each time I add, remove, adjust or make any functional change to any object on the canvas, I manually make the adjustment for the object within the session variable array as well.

This has worked well so far but I now want to implement grouping functionality. While it is easy to do this with fabric.js alone, I am having trouble representing the exact state of the groups on the layers panel. It needs to be displayed in a tree format where nested groups and items are within/under other objects.

I also still need to have the layers panel sortable as well.

Using fabric, we can group a set of objects, group another set of objects, and then group the two sets of objects together. This gets quite complicated and is quickly becoming a nightmare to maintain using the method I described above.

So now I am thinking, it would be much easier to just have the layers panel look at the object data directly from the canvas (reactively). This way, I don't need to maintain a second array and manually match every change that occurs. I could then use something like React Sortable Tree.

So in an attempt to make this happen, I am trying to store a copy of canvas.getObjects() into a Meteor session variable after any change occurs on the canvas.

canvas.on({
    'after:render': function() {
       var canvasObjects = [];
       var objects = canvas.getObjects();
       objects.forEach(object=>{
         canvasObjects.push(object);
       });
       Session.set('canvasObjects', canvasObjects);
    }
});

I am getting the following error though...

TypeError: callback is not a function
    at modules.js?hash=d9804d9fae07a146610fc3c3c610d3e79bf33006:261816
    at onLoaded (modules.js?hash=d9804d9fae07a146610fc3c3c610d3e79bf33006:244313)
    at modules.js?hash=d9804d9fae07a146610fc3c3c610d3e79bf33006:244337
    at modules.js?hash=d9804d9fae07a146610fc3c3c610d3e79bf33006:257179
    at onLoaded (modules.js?hash=d9804d9fae07a146610fc3c3c610d3e79bf33006:244357)
    at modules.js?hash=d9804d9fae07a146610fc3c3c610d3e79bf33006:244379
    at Array.forEach (<anonymous>)
    at Object.enlivenPatterns (modules.js?hash=d9804d9fae07a146610fc3c3c610d3e79bf33006:244370)
    at Function.fabric.Object._fromObject (modules.js?hash=d9804d9fae07a146610fc3c3c610d3e79bf33006:257171)
    at Function.fabric.Path.fromObject (modules.js?hash=d9804d9fae07a146610fc3c3c610d3e79bf33006:261457)

If I replace this line...

Session.set('canvasObjects', canvasObjects);

with this line...

console.log(canvasObjects);

I see the array printed in the console.

Why is it able to log the array but unable to store it into the session variable?

Is this even a good idea? I feel like it will be bad for performance. Would there be any other way to keep the canvas state and the layers panel in sync with all the properties that I want (visibility, opacity, fill, lock, index, group hierarchy etc.).

Thanks in advance.

0 Answers
Related