Konva – React & Vue reactivity considerations HTML Canvas libs

Somebody using React asked why a canvas shape that was set in the model to be non-reactive is being redrawn when some property of another shape was changed.

TLDR: The contents of the HTML5 canvas element are not individually addressable objects, so they cannot be made non-reactive in the same sense as an HTML Div can be.

React, Vue and many other libraries use an object model that represents the HTML page DOM. You make changes to the model and a complex engine decides when to update the visual HTML DOM. These engines are clever, refreshing ONLY the HTML element or component that changed.

But the HTML Canvas is different.

The HTML5 canvas is NOT like a page DOM. It is a flat 2-D surface full of dots. A shape is drawn on the canvas and immediately after the shape is drawn there is no sense of it being part of a DOM. It is non-addressable. It is not an object. It is simply a setting of colour on all the dots contained in the space the shape you draw overlaps. Konva and similar libs provide an object-like interface against the canvas, but the objectivity part is handled in the lib code and it does not change the concept of the canvas being a dumb 2-D surface with no object model.

So what’s your point?

Hmmm, glad you asked….to make a change on any part of the canvas, the entire canvas is re-drawn. This is impossible to demonstrate simply – but we can try. In most libs like React and Vue, you are able to override the automatic redraw of visual elements. We are going to set that on a shape, update its colour, then change another shape. What we would see if there were a canvas DOM like the HTML page DOM, would be only the shape we wanted to change would be affected.

Make a stage and add two rectangles. Give them both a blue fill. In your reactive model, set one of them to not auto-refresh. Add a button and make its action changing the fill setting of both shapes in your reactive lib’s model to red. If you run this demo against two HTML Div’s then only the live element will change color. If there is no Canvas DOM then both rectangles will change colour. Go ahead – what happened? Yup, they both changed.

Why?

Simply because you cannot generally target a specific ‘object’ on the canvas because of it being a dumb 2-D surface with no objectivity. To change any part of it requires a full redraw of all of it. What Konva and similar canvas wrapper libs do is give the appearance that the canvas has a DOM, but the provision of this DOM is in fact the main point for those libs to exist!

That ‘do not redraw’ setting in your reactive model works only against the HTML DOM. The canvas lib wrapper is always being updated with whatever properties you change.

By the way, at a more complex level, like in the graphic chips & OS, there is some smarts working to optimise performance so that perhaps off-screen shapes are not redrawn but that is not important to this discussion.

Summary

There is no such thing as reactivity for the shapes drawn onto an HTML5 canvas element. Konva and similar libs make the developers life much easier by providing a DOM-like approach to working with the canvas, but the nature of the canvas means that it is not possible to update shapes individually. In one sense it ‘feels’ like individual shapes CAN be updated, but this is not actually the case – the canvas is always redrawn in full at any and every redraw.

You might be concerned that this is a performance issue, however in the majority of cases it is not. If you are concerned about performance, the Konva docs have some advice here.

About Konva autoRedraw

I’m mentioning this because it came up in the Discord discussion that invoked this post and it was a bit of a side-show / distraction. As I mentioned, to make a change on any part of the canvas, the entire canvas is re-drawn. From v8 Konva included the Stage.autoRedraw property. What this does is look out for changes to its internal model that would affect the view on the canvas and invoke the redraw of the canvas when they happen. Before this feature we had to execute Stage.draw(), or Layer.draw() to get that to happen. There was also a batchDraw function that was more efficient. We used to get folks yelling about the canvas not changing when in fact they had not invoked these drawing commands after a change to their model. So the autoRedraw concept was created. It IS possible to switch this feature off but I urge you not to as the extra work for you, the developer, is a pain, and the autoRedraw process is optimised by being tied in to the animation frame loop process of the browser.

Thanks for reading.

VW March 2022.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: