3

(Sorry if this is a duplicate, but I don't think it is)

Just so you know, I'm using Google Chrome 29.0.1547.66 m.

I have a KineticJS Project going on at the moment which builds a tiled "staggered" map 40 x 100 tiles. The map takes about 500ms to render which is fine. All 4000 tiles have been put onto one layer.

Once that has completed, I try to drag the stage but I am getting very poor performance as it tries to move all 4000 tiles at once. We are talking about 160ms of CPU time.

I have tried breaking each row up into its own separate layer as others suggested, but that made it even worse (620ms CPU time) as it had to move each layer separately.

I wouldn't say I'm very good at JavaScript but I can't see a way to improve the performance of the drag and I have done some descent research.

Maybe caching the entire layer or something could work?

The project so far has quite a lot of code, so I'm just going to show snippets:

//Stage Object
window.stage = new Kinetic.Stage({
    container: element,
    width: 800,
    height: 600,
    draggable: true
});

//Map Loop for create and position tiles
var tempLayer = map.addLayer(); //makes a new layer and adds it to stage etc.
for (var y = 0; y < height; y++) { //100 tiles high
    for (var x = width-1; x > -1; x--) { //40 tiles wide
        var tileImg = map._tiles[mapArray[x][y]-1]; //map._tiles is just an array of Kinetic.Image objects
        if (typeof(tileImg) != "undefined"){
            var tileClone = tileImg.clone();
            map.place(x, y, 0, tileClone, tempLayer); //places tile in world scope positions
        }
    }
}
tempLayer.draw();

If I can't work out how to improve the performance, there is no way anyone will be able to run this and the project is going to have to be binned, so all thoughts are appreciated! Thanks!

Community
  • 1
  • 1
WizzHead
  • 150
  • 9
  • Could you give us a working jsfiddle that reproduces the issue? – Tschallacka Sep 13 '13 at 12:20
  • Do you need to interact with the tiles (aside from moving them)? If not, you might consider a large background html image that you reposition with CSS (or several smaller background images). Although Kinetic is efficient, 4000 separate objects brings a good deal of programmatic overhead. – markE Sep 13 '13 at 17:56

1 Answers1

3

Have a look at this SO Question: How to cache a whole layer right before dragstart and revert it back on dragend?

The question and my answer describes a similar issue and I think the solution I came up with may help.

Basically what I was suggesting (although I haven't tried it completely so I don't know if it will work well):

  1. Cache the layer using toImage
  2. Drag the image on a different layer while hiding the original layer.
  3. Calculate dx and dy (the distance that you moved)
  4. Update the original layer with dx and dy
  5. Hide image layer, show shapes layer.

I managed to create a quick example JSFIDDLE to work with so check it out. One thing I noticed is that the stage size really affected the performance of the drag, even if it was just 1 rectangle instead of 4000. So, if the stage is really big, even with this image caching thing it didn't really help. But when I decrease the stage size it seems to help

UPDATE:

I found the reason for that "stretched/scaled" image when dragging. It's because I had the image size set statically like so:

var image = new Kinetic.Image({
    image: img,
    x: 0,
    y: 0,
    width: 2000,
    height: 5000
});

This caused the image to stretch since the image was larger than the stage. If we remove the width and height properties like so:

var image = new Kinetic.Image({
    image: img,
    x: 0,
    y: 0
});

You'll see that the image doesn't stretch anymore.

The other good news is that I reduced the stage dimensions by half (although the number of rectangles, area taken by rectangles and size of image remains the same) and the performance has improved greatly. Hopefully your stage dimension isn't as large (2000x5000) as I had it before right? Check the JSFIDDLE now and see it in action!

Community
  • 1
  • 1
projeqht
  • 3,160
  • 3
  • 18
  • 32
  • JSFIDDLE is still not completely working yet, I'll let you know if I get it but you can test it too if you want. – projeqht Sep 13 '13 at 15:17
  • I have had a look at it and I think your on to something there, but is it just me or is KineticJS only caching part of the layer that's in view? [This jsFiddle](http://jsfiddle.net/tPCGV/17/) will show you that the cached image is stretched over the original tile layer and if you go all the way down, you will see they end at the same place. – WizzHead Sep 13 '13 at 15:37
  • Nope I see that too now, isn't that weird? Not sure what's causing that haha – projeqht Sep 13 '13 at 15:39
  • James, I updated my answer above, it's looking pretty good now! Let me know how it goes or if you run into any other issues. – projeqht Sep 13 '13 at 19:21
  • Oh and I added random color from this [question](http://stackoverflow.com/questions/1484506/random-color-generator-in-javascript) so that it looks nicer to compare between Rect vs Image – projeqht Sep 13 '13 at 19:27
  • Sorry for not coming back to you sooner. Thanks so much for the work you put in. I was working on it at the same time as you and I have implemented my own version into my project but I was able to improved the CPU time. I'm marking this answer as correct as it's the correct idea. Thanks again! – WizzHead Sep 14 '13 at 10:16
  • Glad to hear it worked out man. Let us know if you come up with any other ways of improving the performance! – projeqht Sep 15 '13 at 07:36