This question is about the architecture approach. How would someone go about creating the immutable state for a graphics editor that would eventually provide an "undo/redo" functionality, considering that the library for canvas manipulations already has its own state management.

I am not sure that I'd be doing the right thing at all. Maybe it's just stupid to take something what already has its own state management and try to abstract over it. Any opinion would be great! If anything, I'll create an example out of it once I'm done.


EaselJS (a canvas manipulation library) is pretty straightforward for use.

It stores its state in the object you can get from new createjs.Stage("canvasId"); and it's usually assigned to a variable called stage.

You create canvas objects with constructors such as let circle = new createjs.Shape();, which you can color or shape with methods like circle.graphics.beginFill("red").drawCircle(0, 0, 50);, then you add those to the stage with stage.addChild(circle);, and finally you have to manually call the stage.update();

The state and representation of those objects on canvas is directly mutable and the only way for changing positions of elements on canvas is to change their X and Y positions in the stage (state) object, and then call the stage.update();

For example, to create a drag'n'drop functionality, all you need is to attach a callback function to a custom event called "pressmove". That event exists on all stage objects. In the callback function you can get the current mouse position on canvas from a passed event reference - event.stageX and event.stageY, which you use to change the state with event.currentTarget.x = event.stageX and event.currentTarget.y = event.stageY, and of course, call the stage.update();. Just take a look at the code:

circle.on("pressmove", evt => { 
    evt.currentTarget.x = evt.stageX; 
    evt.currentTarget.y = evt.stageY; 
    stage.update();
})

Now, what I'm thinking is creating my own representation of the state in my immutable data store which would track the creation and removal of those objects. Basically I would mirror all the important data from the Stage object. To be even more precise, I'd mirror its property called children, which is an array that has all the objects rendered on the canvas and those objects can also have their children. The example is the container object, which is usually used to group text and image object and I'll definitely use those for tooltips over images, for functionalities like resize and rotate.

So I am thinking of storing every Stage change - the creation/removal of objects, start/end positions on drag, etc. Basically everything except the changes of element positions that occur during the events such as the dragging, resizing and rotating (I'd only store start and end positions), because it would make undo/redo complicated and would probably overload the store snapshots.

Is my thinking correct and would someone have any advices? Has anyone created or done anything similar to this?

  • 1
    please don't cross-post: stackoverflow.com/questions/44976245/… "Cross-posting is frowned upon as it leads to fragmented answers splattered all over the network..." – gnat Jul 10 '17 at 12:46
  • Oh, ok, sorry. I didn't think of that. The only thing that crossed my mind is that I had no answers on StackOverflow and that I recalled of this Stack site so I simply copied and posted it over here. Should I delete the question from StackOverflow? – flackjap Jul 11 '17 at 12:37
up vote 0 down vote accepted

This makes perfect sense, and is similar to the way undo/redo are typically implemented in a graphics editor. I also recommend to design your editor as a set of finite state machines, with super states capturing behavior common to sub-states. Try to limit the lifespan of sub states to the minimum; for example, while a drag operation occurs.

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.