Konva – Layer redraw detection

I’ve seen a few questions asking how to know when a layer gets redrawn. But first I’m going to tell you why you don’t generally need to do it:

  • Firstly Konva redraws a layer when it detects that this is necessary. You should not sweat over when this will happen – sit back and let Konva do the heavy work.
  • If you add code to run when the layer is refreshed then DO NOT make any changes to any elements on the canvas that will cause a re-draw. Why – because you will cause an infinite loop as Konva detects something changed and does a redraw, then your code is called and changes the canvas, rinse-and-repeat. Dead browser window. Yuk.

Mostly people who look for layer redraw solutions are misunderstanding which commands are asynchronous. In general this is new folks loading images, and occasionally folks who should know better trying to save the canvas as an image. Lets just cover those two off quickly.

Loading images is async

Loading images is async because when you ask JavaScript to load an image, it asks the browser to go get it. Javascript has no idea of how long that will take so it carries on processing the next lines of code. Javascript has the wonderful async processing model for this eventuality – what that means is that when the image finally arrives at the browser it can run some more code which you should have provided as the ‘call back function’ along with the image load request.

The new folks don’t get that time delay issue and assume that the image is loaded immediately with a small delay before the next line of code runs. If you imagine having that mind set for a moment, you would be frustratedly thinking that there’s a bug and thinking through a way to know when the stage is finally drawn – maybe you could use that as a way of knowing all the images are ‘in’ ?

No – here’s a classic loading images 101 for Konva:

// declare a JavaScript image object to go get the image...
const imageObj = new Image();
// tell the image object about the callback code we want run when the image is loaded
imageObj.onload = function () {
   // this code runs when the image finishes loading - we don't know when that 
   // will happen, but as long as it does this code will run.
   
   // Make a Konva.Image object to put on the stage
    var yoda = new Konva.Image({
      x: 50,
      y: 50,
      image: imageObj,
      width: 106,
      height: 118,
    });

    // add the Konva.Image shape to the layer
    layer.add(yoda);
};
// set the image object loading the required image from the server...
imageObj.src = '/assets/yoda.jpg';

// this line will be executed immediately - no waiting for the image to load.
//...the rest of the code is here...;

The comments in the code make the point. If you came looking for a way to know the images are all loaded then you can modify the code in the onLoad callback function to set some global variable and trigger some custom function to count the load completions and respond accordingly. You don’t need to know when a layer is redrawn!

Exporting images

The export functions are asynchronous. The process is a hand-off request to the browser, and the browser vendors initially had different views on whether the process should be synchronous or async. As time passed, Chrome was left as the odd one that treated it as synchronous, and then finally they got into line and made it async. When Konva was originally coded and documented it seems to have been assumed to have been synchronous, but if you look in the Konva code in GitHub you can see that the commands have an optional callback function.

Conclusion – the docs are wrong, the implied synchronous-ness is incorrect, use a callback function like you do when loading images described above.

Sermon over.

So how can you detect when there’s a redraw ? Answer – stick a very simple custom shape on the canvas. The magic here is that a custom shape has to have a sceneFunc() function. This function is called by the Konva engine any time the layer the shape sits on is to be redrawn – and so it gets a kick every time the layer is refreshed. If you are doing something when the layer gets redrawn then you might want to ensure that your shape is at the top of the z-order (so drawn last because it is on-top of all other shapes in the layer) and also make a short timeout to allow the bits & bytes of the canvas to be fully assembled. Yes – there’s still some async-ness happening here because we only know when the Konva layer is processing the re-draw, not when the browser actually completes the underlying process.

Do bear in mind that whatever code you trigger off from the sceneFunc(), keep it small and quick, because for example, Konva will call a redraw on every step of a dragmove, and that adds up quickly.

Other than this, there’s no callback from the HTML5 canvas element itself to mark the redraw event completion. Konva is a wrapper for the HTML5 canvas so it can’t offer any ‘extra’ low-level capabilities that are not availble from the canvas element.

And that’s it. But as I said at the start, you should never need this!

const stage = new Konva.Stage({
        container: 'container',
        width: window.innerWidth,
        height: window.innerHeight,
      }), 

      layer = new Konva.Layer(),

      rect = new Konva.Rect({
        x: 100,
        y: 100,
        width: 100,
        height: 100,
        fill: 'red',
        draggable: true
      }),

      counter = {events: 0},

      myShape = new Konva.Shape({
        x: 0,
        y: 0,
        listening: false,
        sceneFunc : function(context, shape){
          console.log('layer was re-drawn ! ' + counter.events++ ); 
        },
        hitFunc: function(context){
          return false;
        }
      });

layer.add(rect, myShape);
stage.add(layer);

See the code here for a working version.

Summary

You don’t need to know when the layer is redrawn. if you think you do and the issue relates to image loading or image saving then you are highly likely to be going down the wrong road.

If you find that you really, really, DO need to have code run when the layer is redrawn then let me know the use-case and how it went, I’ll update this article.

Thanks for reading.

VW Jan 2023

Headline image courtesy of Pamela Chougne at giffy

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 )

Twitter picture

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

Facebook photo

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

Connecting to %s

%d bloggers like this: