Search code examples
javascriptkineticjs

Kineticjs array of generated layers is very slow


Using Kineticjs v 5.1.0.

I figured out how to setup multiple layers hoping that performance would be better with many canvas layers instead of multiple shapes in one layer per earlier todays question. But it's even slower then ever this way. I know that Kinetic creates two canvas elements for each layer but did not think it would be insanely slow to start up and interact with it via a mouseover.

It could be in the mouseover this.draw() I could not figure out just yet how to target the layer from the array.

var xx = 20;
var yy = 20;
var _layers = [];
var count = 0;

var stage = new Kinetic.Stage({
  container: 'container',
  width: 978,
  height: 900
});

for(var j = 0; j < 20; j++) {

  for(var n = 0; n < 20; n++) {


    var layer = new Kinetic.Layer();

    var rect = new Kinetic.Rect({
      x: xx,
      y: yy,
      width: 14,
      height: 14,
      fill: 'green',
      stroke: 'black',
      strokeWidth: 4
    });

    rect.on('mouseover', function() {
        this.fill("yellow");
        // layer.draw();
        // _layers[n].draw();
        // using this.draw() instead of layer was a total guess 
        this.draw();
    });
    rect.on('mouseout', function() {
        this.fill("blue");
        this.draw();
    });

    // add to array
    _layers.push(layer)

    // add the shape to the layer
    // use count instead of n
    _layers[count].add(rect);

    // add the layer to the stage
    stage.add(layer);

    count += 1;
    xx += 20;
  }
  // reset the xx and increase the grid row
  xx = 20;
  yy += 20;
}

Solution

  • Each layer is the same size as the stage so you have 20x20=400 layers all of which are 978x900. Each layer has 2 canvases so you have 800 large canvases. That's waaaay to many.

    Instead, put all your Kinetic.Rects on 1 layer.

    Here's an example with all your rects on 1 layer. IMHO, the performance is acceptable but not great:

    http://jsfiddle.net/m1erickson/z2hnLLte/

    That still leaves 400 'smart' objects that are listening for mousemoves. You're redrawing all 400 smart objects with each hit on any 1 rectangle because this.draw() will redraw all nodes on the layer.

    But if you still desire more performance you might reduce the number of 'smart' objects that are listening for mousemove and being redrawn. You can do this by creating 1 custom Kinetic.Shape instead of 400 individual Kinetic.Rects.

    • Create an array of objects defining each of your 400 rectangles

      var rects=[];
      rects.push({x:0,y:0,color:'green'});
      rects.push({x:20,y:0,color:'green'});
      ....
      
    • Refactor your code to use 1 custom Kinetic.Shape to draw all your rectangles based on rects[].

    • Listen for mousemove on the Kinetic.Shape.

    • Mathematically hit-test if a rectangle is under the mouse.

    • Recolor the rectangle under the mouse and redraw the Shape.