serv-bot 22
serv-bot 22

Reputation: 1298

Why is this canvas animation running choppy?

I think I'm reloading the image too many times or there's an error in my loops, but this is my first canvas project, so I'm not sure how to handle calls and methods. Anybody mind giving me a hand or explaining a bit?

BTW; I need for of these with different patterns on the site, hence the vectors. I'm using three more of the animateC() function with different names and vector positions (they're identical otherwise) and different canvases(<canvas id="CSCanvas" width="2800" height="2000"></canvas>) inside the 'wave' div. Is this the right way to go about that?

The JSDfiddle is right this way.

and here's the script..

$(document).ready(function(){

var offset = 0;
var offset2= 0;
var delta = 1;
var delta2 = .5;
var wavesCanvas = [ 'CCanvas', 'CSCanvas', 'SCanvas', 'SSCanvas' ];
var wavesIMG = ['wt1', 'shadow', 'wt2', 'shadow'];  

function animateC() {   
    var wavevar=0;
    var imgname=wavesIMG[wavevar];
    var canvasname=wavesCanvas[wavevar];
    var Ccanvas = document.getElementById(canvasname);
    var Ccontext = Ccanvas.getContext('2d');

    var pattern = new Image();
    pattern.onload = function(){
        Ccontext.clearRect(0,0,Ccanvas.width,Ccanvas.height);
        Ccontext.save();
        Ccontext.translate(-offset, 0);
        Ccontext.beginPath();
        Ccontext.moveTo(0, Ccanvas.height);
        Ccontext.lineTo(0, 0);
        var waux=120;
        for(i=0;i<50;i++){
            Ccontext.quadraticCurveTo(5+(waux*i), 0, (10+(waux*i)), 6);
            Ccontext.quadraticCurveTo((60+(waux*i)), 56, (120+(waux*i)), 6);
        }
        Ccontext.lineTo(Ccanvas.width, Ccanvas.height);
        Ccontext.closePath();
        var fillP = Ccontext.createPattern(pattern,'repeat');
        Ccontext.fillStyle = fillP;
        Ccontext.fill();
    };
    pattern.src= './images/'+imgname+'.png';
    Ccontext.restore();
    offset += delta;
    if (offset > 120) offset=0;
    requestAnimationFrame(animateC);
}


window.requestAnimationFrame = (function(){
  return window.requestAnimationFrame       ||
         window.webkitRequestAnimationFrame ||
         window.mozRequestAnimationFrame    ||
         function( callback ){
            window.setTimeout(callback, 3000 / 60);
         };
})();

animateC();
});

..and here's the HTML..

<div id='wave'>
<canvas id="CCanvas" width="2800" height="2000"></canvas>
</div>

Upvotes: 1

Views: 2371

Answers (1)

user1693593
user1693593

Reputation:

Causes

It runs choppy for many reasons:

  • Images is set for load each time
  • Canvas IDs is looked-up for each time
  • 2D context is requested for each time
  • Pattern is created and set for each time
  • The canvases are huge (2800 x 2000 and you have four of them)

The whole code need to re-factored to pre-allocate images, patterns, canvases and contexts.

I would have to rewrite most of the code, but I have shown a little example here how you can pre-allocate some of the resources. It helps a little but due to the sizes and number of canvases it will maybe not be so noticeable.

On your pattern loader (there is only one in the fiddle) you need to start everything after the image is loaded:

pattern.onload = loadDone;
pattern.src = '...';

On the handler you can get canvases, context and allocate patterns and then call the animation loop:

var aCanvas = [];
var aCtx = [];
var aPattern = [];

function loadDone() {
    for(var i = 0; i < wavesCanvas.length; i++) {
        var Ccanvas = document.getElementById(wavesCanvas[i]);
        var Ccontext = Ccanvas.getContext('2d');
        var fillP = Ccontext.createPattern(pattern, 'repeat');
        aCanvas.push(Ccanvas);               
        aCtx.push(Ccontext);
        aPattern.push(fillP);
    }
    animateC();
}

Now in the animation loop you just get the already stored resources:

function animateC() {
    var wavevar = 0;
    var Ccanvas = aCanvas[wavevar];
    var Ccontext = aCtx[wavevar];
    ...

I updated the fiddle with these changes:
http://jsfiddle.net/AbdiasSoftware/6FRa9/3/

Suggestions

Using canvas layers is fine, but here due to them being so big, this will require a lot of memory. In this case the raw memory requirement is:

2800 x 2000 x 4 (RGBA) x 4 (Canvases) = 89 600 000 bytes, or ~84 mb.

That is 84 mb that need to be updated 60 times per second, a required bandwidth of 5 gb per second (in addition to other data in the browser). In theory anyways.

So for that reason it's better to draw everything in one canvas. You can do this by re.factoring like this:

function drawWave() { ... }

function animation() {

    ctx.translate(offset1x, offset1y);
    drawWave();
    ctx.translate(-offset1x, -offset1y); //reset

    ctx.translate(offset2x, offset2y);
    drawWave();
    ctx.translate(-offset2x, -offset2y); //reset

    // ...
}

Or better: just use the offset directly with the drawWave function as the x and y start point.

Upvotes: 3

Related Questions