I want to start using Konva.js
library. It looks really great in my browser (chrome) on my PC and has awesome functionality. I created code to make some really easy animations (move and rotate). But when I tried to run my webpage on a mobile device or even in Safari browser, it started to be really laggy. Moreover, when I set four objects(Images) into a movement, the browser crashed.
I did some tests and realized that even draggable objects are laggy on mobile devices. (When I tried to move them, they had really slow and jerky movement).
Is there some way to optimize it. (I have tried the recommended batchDraw()
function, but it didn't help)? How can I make the movement fluent? If this is not currently possible, will there be some optimization possibilities in the future?
Here is my code... but obviously you have to run it on a mobile phone to see the effect. To activate the effect click(touch) on images.
http://bannerteam.tode.cz/konvaAnim/
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Konva animace</title>
<style>
body {
margin: 0;
padding: 0;
overflow: hidden;
background-color: #F0F0F0;
}
</style>
<script src="https://cdn.rawgit.com/konvajs/konva/2.0.3/konva.min.js"></script>
</head>
<body>
<div id="container"></div>
<script>
var width = window.innerWidth;
var height = window.innerHeight;
var lengthOfAnim = 60;
var pos = [{x: 50, y: 50, p: 'A'}, {x: 250, y: 50, p: 'B'}, {x: 450, y: 50, p: 'C'},
{x: 50, y: 250, p: 'D'}, {x: 250, y: 250, p: 'E'}, {x: 450, y: 250, p: 'F'},
{x: 50, y: 450, p: 'G'}, {x: 250, y: 450, p: 'I'}, {x: 450, y: 450, p: 'J'},
{x: 50, y: 650, p: 'K'}, {x: 250, y: 650, p: 'F'}];
var stage = new Konva.Stage({
container: 'container',
width: width,
height: height
});
var imageObj = [];
var img = new Image();
var doAnimations = function(i){
switch(i){
case 0:
imageObj[i].img.opacity(1 - imageObj[i].time/lengthOfAnim);
break;
case 1:
if(imageObj[i].time === lengthOfAnim -1)
imageObj[i].img.y(imageObj[i].img.y() + (lengthOfAnim-1)*2);
else
imageObj[i].img.y(imageObj[i].img.y() - 2);
break;
case 2:
imageObj[i].img.opacity(1 - imageObj[i].time/lengthOfAnim);
if(imageObj[i].time === lengthOfAnim -1)
imageObj[i].img.y(imageObj[i].img.y() + (lengthOfAnim-1)*2);
else
imageObj[i].img.y(imageObj[i].img.y() - 2);
break;
case 3:
var parent = imageObj[i].img.getParent();
parent.clipFunc(function(ctx) {
ctx.rect(imageObj[i].img.x() - 75, imageObj[i].img.y() - 75, 75*(1 - imageObj[i].time/lengthOfAnim), 150);
ctx.rect(imageObj[i].img.x() + 75 - 75*(1 - imageObj[i].time/lengthOfAnim) , imageObj[i].img.y() - 75, 75*(1 - imageObj[i].time/lengthOfAnim), 150);
});
break;
case 4:
var parent = imageObj[i].img.getParent();
parent.clipHeight(150*(1 - imageObj[i].time/lengthOfAnim));
break;
case 5:
imageObj[i].img.opacity(1 - imageObj[i].time/lengthOfAnim);
imageObj[i].img.rotation(90*(imageObj[i].time/lengthOfAnim));
break;
case 6:
imageObj[i].img.opacity(1 - imageObj[i].time/lengthOfAnim);
var pom = (1 - imageObj[i].time/lengthOfAnim);
imageObj[i].img.scale({x: pom, y: pom});
break;
case 7:
if(imageObj[i].time === lengthOfAnim -1)
imageObj[i].img.x(imageObj[i].img.x() - (lengthOfAnim-1)*2);
else
imageObj[i].img.x(imageObj[i].img.x() + 2);
imageObj[i].img.move({x: 0, y: 10*(Math.sin(6*Math.PI*(imageObj[i].time/lengthOfAnim)))});
break;
case 8:
imageObj[i].img.rotation(20*Math.sin(6*Math.PI*(imageObj[i].time/lengthOfAnim)));
break;
case 9:
imageObj[i].img.opacity(0.5 + Math.abs((imageObj[i].time/lengthOfAnim-0.5)));
break;
case 10:
imageObj[i].img.draggable(true);
break;
}
}
img.onload = function() {
for(let i = 0; i < pos.length; i++){
var layer = new Konva.Layer();
var yoda = new Konva.Image({
x: pos[i].x + 75,
y: pos[i].y + 75,
image: img,
width: 150,
height: 150,
offset: {
x: 75,
y: 75
}
});
imageObj.push({img: yoda, layer: layer, time: 0});
var charac = new Konva.Text({
x: pos[i].x + 50,
y: pos[i].y + 160,
text: pos[i].p,
fontSize: 30,
fontFamily: 'Calibri',
fill: 'black'
});
if(i === 3){
var group = new Konva.Group({clipFunc:function(ctx) {
ctx.rect(pos[i].x, pos[i].y, 150, 150)
},});
group.add(yoda);
layer.add(group);
}else if(i === 4){
var group = new Konva.Group({clip: {
x : pos[i].x,
y : pos[i].y,
width : 150,
height : 150
},});
group.add(yoda);
layer.add(group);
}else
layer.add(yoda);
layer.add(charac);
stage.add(layer);
yoda.on('click tap', function() {
if(imageObj[i].time === 0)
imageObj[i].time = lengthOfAnim;
});
}
setInterval(function(){
for(var i = 0; i < pos.length; i++){
if(imageObj[i].time > 0){
imageObj[i].time--;
doAnimations(i);
imageObj[i].layer.draw();
}
}
}, 40);
}
img.src = './castle.png';
</script>
<body>
</html>
There are a lot of ways to improve the performance.
You can read a lot of tips here: https://konvajs.github.io/docs/performance/All_Performance_Tips.html
Your stage looks simple, so performance should be very good on mobile.
Some tips:
setInterval
. Use requestAnimationFrame
. The animation will be much smootherlayer.batchDraw()