Animating HTML canvas elements

I am trying to animate elements on the <canvas> but setTimeout and setInterval are giving me problems. My data is in an Array. I get its length. For each node I generate an X and a Y, a cardinal direction and a color. Then my drawing function draws a circle for each one of those nodes.

My problems arise when I try and loop with a setInterval. My thinking was that I could establish all the X, Y values, then loop back and increment them each 10ms-100ms. But every time I use setInterval my child Arrays that hold the data for each circle get a length of 3 and the values are set to undefined.

I coded this once using various objects but couldn't get the animation to work. I learned about the issues with scoping setInterval() in objects. I recoded it without establishing a bunch of objects. still no luck.

$(document).ready(function(){
    getTweets();
});

CTX = $('#tweets')[0].getContext("2d");
WIDTH = $('#tweets').width();
HEIGHT = $('#tweets').height();
RADIUS = 10;
TWEETS = [];
CORDS = [];
DIRECTION = ['north','east','south','west'];

function getTweets(){
    $.getJSON("http://search.twitter.com/search.json?callback=?&q=scion&rpp=100",
    function(r){
        var numberOfTweets = r.results.length;
        while(numberOfTweets--){
            TWEETS.push(r.results[numberOfTweets].text);
        }
        plotXY();
        animateTweets()
    });
}

function animateTweets(){
    return setTimeout(plotXY(true),100);
}

function plotXY(animating){
    if(!animating){
        var numberOfTweets = TWEETS.length;
        while(numberOfTweets--){
            var topY = Math.ceil(Math.random()*(HEIGHT-20)),
                leftX = Math.ceil(Math.random()*(WIDTH-20)),
                cardinal = Math.floor(Math.random()*4),
                color = '#'+Math.floor(Math.random()*16777215).toString(16);

            var valCords = validateCords(leftX, topY);
            CORDS.push([valCords[0], valCords[1], cardinal, color]);
        }
        // console.log('animating false');
        // console.log(CORDS);
    }
    else{
        var numberOfTweets = TWEETS.length;
        while(numberOfTweets--){
            if(CORDS[numberOfTweets][2]=='north'){ //NORTH
                CORDS[numberOfTweets][1]+=2;
                CORDS[numberOfTweets][0]+=2;
            }
            if(CORDS[numberOfTweets][2]=='east'){ //EAST
                CORDS[numberOfTweets][1]+=2;
                CORDS[numberOfTweets][0]-=2;
            }
            if(CORDS[numberOfTweets][2]=='south'){ //SOUTH
                CORDS[numberOfTweets][1]-=2;
                CORDS[numberOfTweets][0]-=2;
            }
            if(CORDS[numberOfTweets][2]=='west'){ //WEST
                CORDS[numberOfTweets][1]-=2;
                CORDS[numberOfTweets][0]+=2;
            }

            var valCords = validateCords(leftX, topY, numberOfTweets);

            CORDS.push([valCords[0],valCords[1],cardinal]);
            CORDS.shift();          
        }
        // console.log('animating true');
        // console.log(CORDS);
    }
    drawCircles();
}


function drawCircles(){
    console.log('drawing');
    var numOfCords = CORDS.length;
    clear();

    while(numOfCords--){
        CTX.fillStyle = CORDS[numOfCords][3];
        CTX.beginPath();
        CTX.arc(CORDS[numOfCords][0], CORDS[numOfCords][1], RADIUS, 0, Math.PI*2, true);
        CTX.closePath();
        CTX.fill();
    }
}

Answers


I don't know if this is the only problem in your code, but one significant problem is the way you are using setTimeout():

setTimeout(plotXY(true), 100); // doesn't work

That says to call the plotXY() function and then pass whatever it returns as the first parameter to setTimeout.

Instead, the first parameter to setTimeout() should be a function expression or reference to a function like this:

setTimeout(plotXY, 100);      // works, but doesn't pass parameter to plotXY()

Note that plotXY does not have parentheses for this purpose. But of course that leaves you with another problem: you want plotXY() to be called with the parameter true. That is easily solved by wrapping the call to plotXY() in another function and passing that other function to setTimeout():

setTimeout(function(){ plotXY(true); }, 100);     // works

That creates an anonymous function and passes that function to setTimeout().

You probably want to call setTimeout() again at the end of your plotXY() function, or use setInterval(), otherwise your animation will only have one step. Or you can do something like the following, which shows both another way to solve the parameter passing problem and a way to keep calling the function with setTimeout():

function plotXYProxy() {
   plotXY(true);
   setTimeout(plotXYProxy, 100);
}
plotXYProxy();

Of course you can add some conditional processing within plotXYProxy() to decide whether to set each timeout, e.g., you could have plotXY() return a boolean as to whether the animation has finished and call setTimeout() or not based on that boolean. Or whatever suits.

Everything I just said about the parameters of setTimeout() also applies to setInterval().


Need Your Help

How do I download documents from AtTask?

c# api attask

I'm working on a continuing API project. The current issue at hand is to be able to download my data from the AtTask server in precisely the folder structure they exist in on the AtTask servers. I'...

About UNIX Resources Network

Original, collect and organize Developers related documents, information and materials, contains jQuery, Html, CSS, MySQL, .NET, ASP.NET, SQL, objective-c, iPhone, Ruby on Rails, C, SQL Server, Ruby, Arrays, Regex, ASP.NET MVC, WPF, XML, Ajax, DataBase, and so on.