views:

362

answers:

4

I have a page which has several <canvas> elements.

I am passing the canvas ID and an array of data to a function which then grabs the canvas info and passes the data onto a draw() function which in turn processes the given data and draws the results onto the canvas. So far, so good.

Example data arrays;

$(function() {
    setup($("#canvas-1"), [[110,110,100],
                          [180,180,50],
                          [220,280,80]]);

    setup($("#canvas-2"), [[110,110,100],
                          [180,180,50],
                          [220,280,80]]);
});

setup function;

function setup(canvas, data) {
    ctx = canvas[0].getContext('2d');
    var i = data.length;
    var dimensions = {
        w : canvas.innerWidth(),
        h : canvas.innerHeight()    
    };
    draw(dimensions, data, i);  
}

This works perfectly. draw() runs and each canvas is populated.

However - I need to animate the canvas. As soon as I replace line 8 of the above example;

draw(dimensions, data, i);  

with

setInterval( function() { draw(dimensions, data, i); }, 33 );

It stops working and only draws the last canvas (with the others remaining blank).

I'm new to both javascript and canvas so sorry if this is an obvious one, still feeling my way around. Guidance in the right direction much appreciated! Thanks.

A: 

I think you should be able to make it work if you try the following :

setInterval( 'draw(dimensions, data, i);', 33 );

Hope this helps :)

CogentasDeveloper
Avoid all forms of `eval`, including implicit evals, whenever possible. And it's nearly always possible. I also don't think it would work, separate from eval.
T.J. Crowder
This would fail anyway, because you're using `eval` outside of the current scope, so the variables wouldn't be available.
Andy E
+2  A: 

The problem has to do with how closures work. Closures don't receive a copy of the data, they receive an active reference to it. So when the function is run later, it references a live copy of i and such -- which have moved on since you set up the call.

You can fix it like this:

drawLater(dimensions, data, i);

...with this defined elsewhere:

function drawLater(dimensions, data, i) {

    setInterval(function() { draw(dimensions, data, i); }, 33 );
}

That works because the closure holds a reference to the arguments to drawLater, rather than to the variables in your loop.

Separately: Shouldn't you be passing the canvas or its ID into that somewhere?

T.J. Crowder
Thanks - Really I need to award 3 accepted answers here, because everyone helped :)
Alex
+2  A: 

How does draw() know which canvas to use? My guess from your code is that you're using the global variable ctx which will get overwritten with every call to setup(). So you need to change draw() and add the canvas to use as the first parameter.

Aaron Digulla
+1  A: 

JavaScript variables are function scoped, not block scoped. Also you need to declare ctx with var to make it local and then pass it to the draw function.

Jonas Elfström
Thanks, this was very useful to my understanding of what was actually going wrong. ctx being global was an obvious oversight that worked only to a point!
Alex