Beaut
Beaut

Reputation: 124

Optimizing performance in fabric.js

I'm writing a little program to make a interesting background texture. It works the way I want it to except for one thing, it is very slow/choppy on computer and on mobile it doesn't even show the animations. I'm wondering if there is a better way to achieve what I'm trying.

The choppiness becomes especially bad when the size of the circles is small or the screen is large (creating many objects).

Here is the fiddle: https://jsfiddle.net/cv6cgdja/12/

var canvas = new fabric.StaticCanvas('c')
var height = window.innerHeight
var width = window.innerWidth
var size = 35
var strokeWidth = 2
var strokeColor = '#c9db8e'
var fill = 'transparent'
var x = 0
var y = 0
var groups = []

canvas.setHeight(height)
canvas.setWidth(width)

while(y <= (height + (size*2))){
    while(x <= (width + (size*2))){
    var rotation = 0
    switch(Math.floor(Math.random()*4)){
        case 1: rotation = 90; break;
    }

    var first = new fabric.Circle({
      left: 0,
      top: 0,
      radius: size,
      startAngle: 0,
      endAngle: Math.PI/2,
      fill: fill,
      strokeWidth: strokeWidth,
      stroke: strokeColor
    });    
    var second = new fabric.Circle({
        left: size *2 ,
      top: size*2,
      radius: size,
      startAngle: Math.PI,
      endAngle: -Math.PI/2,
      fill: fill,
      strokeWidth: strokeWidth,
      stroke: strokeColor
    });

    var group = new fabric.Group([ first, second ], {
      left: x - size,
      top: y - size,
      originX: 'center',
      originY: 'center',
      angle : rotation
    });

    groups.push(group)
    x += size * 2
  }
  x = 0
    y += size * 2
}

function draw(){
    groups.forEach(function(g){
    canvas.add(g)
  })
}

draw()

setInterval(function(){
    groups.forEach(function(g){
    var rotation = g.get('angle')
    switch(Math.floor(Math.random()*32)){
        case 1: rotation = '+=90'; break;
        case 1: rotation = '-=90'; break;
    }
    g.animate('angle', rotation, {
      onChange: canvas.renderAll.bind(canvas),
      duration: 250,
      easing: fabric.util.ease.easeOut
    });
  })
}, 800)

Upvotes: 0

Views: 2331

Answers (1)

AndreaBogazzi
AndreaBogazzi

Reputation: 14731

Here you go! First of all nice usage of startAngle, endAngle.

Second the main problem was triggering a renderAll for each animation. if you have 100 group the canvas/js cannot tolerate 100 animation in a short time frame ( 16ms ).

The solution in this case is to make .animate make the animation work ( so just changing the angle ) and in the onChange function do exacty nothing.

Then start one single animation, with the same duration, that will make on the onChange function the renderAll.

This problem would have been auto solved in the latest beta release if you would have used requestRenderAll that do not perform any operation if another render has been requested alredy, but i would suggest to take always the consciuous approach of what is going on, and fire renderAll just when necessary.

var canvas = new fabric.StaticCanvas('c')
var height = window.innerHeight
var width = window.innerWidth
var size = 50
var strokeWidth = 2
var strokeColor = '#c9db8e'
var fill = 'transparent'
var x = 0
var y = 0
var groups = []

canvas.setHeight(height)
canvas.setWidth(width)
while(y <= (height + (size*2))){
	while(x <= (width + (size*2))){
  	var rotation = 0
    switch(Math.floor(Math.random()*4)){
    	case 1: rotation = 90; break;
    }
  
  	var first = new fabric.Circle({
      left: 0,
      top: 0,
      radius: size,
      startAngle: 0,
      endAngle: Math.PI/2,
      fill: fill,
      strokeWidth: strokeWidth,
      stroke: strokeColor
    });    
    var second = new fabric.Circle({
  		left: size *2 ,
      top: size*2,
      radius: size,
      startAngle: Math.PI,
      endAngle: -Math.PI/2,
      fill: fill,
      strokeWidth: strokeWidth,
      stroke: strokeColor
    });
    
    var group = new fabric.Group([ first, second ], {
      left: x - size,
      top: y - size,
      originX: 'center',
      originY: 'center',
      angle : rotation
    });
		
    groups.push(group)
  	x += size * 2
  }
  x = 0
	y += size * 2
}

function draw(){
  canvas.renderOnAddRemove = false;
	groups.forEach(function(g){
  	canvas.add(g)
  })
  canvas.renderOnAddRemove = true;
  canvas.renderAll();
}

draw();

setInterval(function(){
    groups.forEach(function(g){
    var rotation = g.get('angle')
    switch(Math.floor(Math.random()*32)){
        case 1: rotation = '+=90'; break;
        case 0: rotation = '-=90'; break;
    }
    g.animate('angle', rotation, {
      onChange: () => {},
      duration: 250,
      easing: fabric.util.ease.easeOut
    });
  })
  groups[0].animate('nothing', 1000, {
    duration: 250,
    onChange: canvas.renderAll.bind(canvas)
  });
}, 500)
<script src="//cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.13/fabric.js"></script>
<canvas id='c'></canvas>
<div>
Hi there
</div>

Upvotes: 2

Related Questions