I'm currently trying to speed my web app for mobile devices a little bit up, but now I'm stuck at the most important part - caching. How is it possible to cache a entire layer right before the user starts to drag it and revert it back to usable Kinetic.Nodes when the drag-action has stopped?
At the moment I start caching on
stage.on('mousedown touchstart', function(){ // CACHING})
but the problem here is, that the user has to perform a second mousedown touchstart
event to "grab" the cached image, which, of course, starts a new caching.
In this case my questions would be:
How can I pass the mousedown touchstart
event to the cached image,
so the user can drag it with one fluent movement?
How can I speed up caching? (It takes 1-2 seconds for the cached image to appear. Is it useful to cache it in a setInterval
after every, lets say 4 secs, and use this precached image or causes that a too high performance drain?)
I highly appreciate any kind of suggestions regarding my problem or further tips&tricks to speed up things.
Based on this statement:
stage.on('mousedown touchstart', function(){ // CACHING})
I'm assuming that on mousedown touchstart
you call layer.toImage()
or stage.toImage()
and you want to drag the new image on that one click/tap.
You can invoke the drag event on the new generated image by using the .startDrag()
function: Kinetic.Shape#startDrag
You can then invoke .stopDrag()
on mouseup touchend
to stop the drag. Kinetic.Shape#stopDrag
Something like:
var image, ox, oy;
stage.on('mousedown touchstart', function(){
// CACHING
stage.toImage({
width: stage.getWidth(),
height: stage.getHeight(),
callback: function(img) {
var image = new Kinetic.Image({
image: img,
draggable: true
});
ox = image.getX();
oy = image.getY();
image.startDrag();
}
});
});
stage.on('mouseup touchend', function(){
image.stopDrag();
//Calculate dx, dy to update nodes.
var newX = image.getX();
var newY = image.getY();
var dx = newX-ox;
var dy = newY-oy;
var children = layer.getChildren();
for (var i=0; i<children.length; i++) {
children.setX(children.getX()+dx);
children.setY(children.getY()+dy);
}
image.hide(); //or remove() or destroy()
layer.draw();
});
Note you need to update your original nodes after dragging the cached layer.
Another Note I haven't tested the code but I believe you could do something along the lines of what I've got up there.
Small UPDATE: You also should probably hide()
the original layer while dragging the cached layer image! :) And then show()
it again when you hide the cached image layer.
Honestly I'm not sure how you would speed up that cache time unless you can predict when the user needs to click/tap the stage to move. I think your suggestion would cost more performance than it would save.
I'm guessing that the desktop caches the image faster than on your mobile? It might fall into just being a limitation of KineticJS performance on Mobile vs Desktop...
UPDATE
Okay, I have an idea for #2, it's not exactly a fix but it might work better for you.
Separate your stage mousedown
event with your touchstart
event. mousedown
will be the same but touchstart
we want to handle differently.
On stage touchstart
we want to drag the entire stage like normal, but meanwhile run the code for the original mousedown
to cache the layer.
When the cached image finishes loading (takes 1-2 seconds you say), use .stopDrag()
on the original stage and hide it. At this moment you want to store the current x and y values of the stage, so that you can still calculate dx,dy. Then immediately call .startDrag()
on the new cached image and continue on like we did for mousedown
.
How to know when the cached image finishes loading? I think that's what the toImage()
callback function
is for. If not, than hopefully a javascript onload
event will work to determine when the image finishes generating.
The end result will be that you'll get your slow choppy drag on the stage for touch events UNTIL the image is cached. Then we flip the switch and stop dragging the stage, start dragging the cached image, and then revert/update the stage on touchend
.
Hope that works as a semi-solution to your problem...
ANOTHER UPDATE
Okay here's another idea that actually might help your performance!
If your stage isn't modifying nodes very often, you can pre-cache the stage image so that it's already generated, and .hide()
it. Then when you need to drag it, you just need to set the x,y of the cached image to match the stage's current x,y and then .show()
the cached image. This will eliminate the time needed to wait/load the cached image when you first start dragging.
If you do happen to add a node or move a node or anything, after that cache the image. Hopefully this is manageable as we don't want to cache the image too often (drains performance). Again the cached image will be ready for your stage.drag
event beforehand.
The goal is to have the stage cached before you do mousedown touchstart
and start dragging. Hopefully this helps.