Reputation: 4327
I'm using animate
method to animate 100 objects, but in my case, the performance is so slow, how do I fix it?
my demo code: https://jsfiddle.net/cs6jqj2w/
Upvotes: 2
Views: 1991
Reputation: 3106
Sounds like you've got it working now. Just thought I would add that the docs on http://fabricjs.com/fabric-intro-part-2 also mention that you might want to use requestAnimationFrame
instead of the onChange
callback when animating lots of objects:
The reason animate doesn't automatically re-render canvas after each change is due to performance. After all, we can have hundreds or thousands animating objects on canvas, and it wouldn't be good if every one of them tried to re-render screen. In the case of many objects, you could use something like requestAnimationFrame (or other timer-based) loop to render canvas continuosly on its own, without calling renderAll for each object. But most of the time, you'll probably need to explicitly specify canvas.renderAll as "onChange" callback.
I had similar problems to yours, and this advice worked quite well.
Upvotes: 0
Reputation: 3706
Please take a look at fabricJS demo
Also, I modified little bit that demo using your function for generating random numbers and created 100 shpaes in this fiddle
(function() {
var canvas = this.__canvas = new fabric.Canvas('c');
fabric.Object.prototype.transparentCorners = false;
var Cross = fabric.util.createClass(fabric.Object, {
objectCaching: false,
initialize: function(options) {
this.callSuper('initialize', options);
this.animDirection = 'up';
this.width = 100;
this.height = 100;
this.w1 = this.h2 = 100;
this.h1 = this.w2 = 30;
},
animateWidthHeight: function() {
var interval = 2;
if (this.h2 >= 30 && this.h2 <= 100) {
var actualInterval = (this.animDirection === 'up' ? interval : -interval);
this.h2 += actualInterval;
this.w1 += actualInterval;
}
if (this.h2 >= 100) {
this.animDirection = 'down';
this.h2 -= interval;
this.w1 -= interval;
}
if (this.h2 <= 30) {
this.animDirection = 'up';
this.h2 += interval;
this.w1 += interval;
}
},
_render: function(ctx) {
ctx.fillRect(-this.w1 / 2, -this.h1 / 2, this.w1, this.h1);
ctx.fillRect(-this.w2 / 2, -this.h2 / 2, this.w2, this.h2);
}
});
for (var i = 0; i < 100; i++){
canvas.add(
new Cross({ top: getRandomInt(0,500), left: getRandomInt(0,500)})
);
}
setTimeout(function animate() {
canvas.forEachObject(function(obj){ obj.animateWidthHeight(); obj.dirty = true; });
canvas.renderAll();
setTimeout(animate, 10);
}, 10);
})();
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
UPDATE: Your animation didn't work because you had to much rendering of the canvas.
You have to generate 99 items without rendering and last one with the rendering. Also, last item has to me with the maximum duration for the animation in order to complete animation for all shapes.
var fabric = window.fabric
var canvas = new fabric.StaticCanvas('c')
function createItem(canvas) {
var item = new fabric.Circle({
left: -100,
top: getRandomInt(0, 500),
opacity: Math.random().toFixed(2),
radius: getRandomInt(10, 50),
})
item.keepGoing = true
canvas.add(item)
// itemTopAnim(canvas, item, getNextTop(item.top))
// itemLeftAnim(canvas, item)
return item;
}
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
function getNextTop(top) {
if (top < (canvas.height / 2)) {
return top + getRandomInt(50, 200)
}
return top - getRandomInt(50, 200)
}
function itemTopAnim(canvas, item, top) {
item.animate('top', top, {
duration: getRandomInt(1, 3) * 1000,
// onChange: canvas.renderAll.bind(canvas),
easing: fabric.util.ease.easeInOutCubic,
onComplete: function() {
item.keepGoing && itemTopAnim(canvas, item, getNextTop(item.top))
}
})
}
function itemTopAnimLast(canvas, item, top) {
item.animate('top', top, {
duration: 3 * 1000,
onChange: canvas.renderAll.bind(canvas),
easing: fabric.util.ease.easeInOutCubic,
onComplete: function() {
item.keepGoing && itemTopAnim(canvas, item, getNextTop(item.top))
}
})
}
function itemLeftAnim(canvas, item) {
item.animate('left', canvas.width - item.radius, {
duration: getRandomInt(5, 10) * 1000,
//onChange: canvas.renderAll.bind(canvas),
onComplete: function() {
item.keepGoing = false
}
})
}
function itemLeftAnimLast(canvas, item) {
item.animate('left', canvas.width - item.radius, {
duration: 10 * 1000,
onChange: canvas.renderAll.bind(canvas),
onComplete: function() {
item.keepGoing = true
}
})
}
for (var i = 0; i < 100; i++) {
var item = createItem(canvas);
if (i == 99){
itemLeftAnimLast(canvas, item)
itemTopAnimLast(canvas, item, getNextTop(item.top))
} else {
itemLeftAnim(canvas, item)
itemTopAnim(canvas, item, getNextTop(item.top))
}
}
Check this updated fiddle
Hopefully, it gives you more idea how it works right now.
Upvotes: 2