richie
richie

Reputation: 2478

requestAnimationFrame isn't canceled

I'm developing a game which is using the requestAnimationFrame. Here is the code for that.

Game.prototype.startLoop = function (arg) {

    var parent = this;

    (function tick() {

        parent.update();
        window.animationID = webkitRequestAnimationFrame(tick);

    }());

};

Game.prototype.stopLoop = function () {
    console.log('cancel the game now');
    window.webkitCancelRequestAnimationFrame(window.animationID);
};

The problem is that I get the message, cancel the game now, however the game continues to run because the animationFrame is somehow not canceled. I have tried webkitCancelAnimationFrame, cancelAnimationFrame, webkitCancelRequestAnimationFrame, and all the versions for requesting the animation frame in my browser: requestAnimationFrame, webkitRequestAnimationFrame. None of the combinations work.

The weird thing is that if I run window.webkitCancelRequestAnimationFrame(window.animationID) in the webkit inspector console, the game stops as expected. I added this code window.webkitCancelRequestAnimationFrame(window.animationID); console.log('button test') to the onclick attribute of a DOM button, and when I click it the game stops, but when I execute the JS theDOMButton.click() nothing happens, but I do get the message 'button test'.

Here is my user agent:

Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.95 Safari/537.36

Here is a jsfiddle:

http://jsfiddle.net/mJPMt/

After changing to this: http://pastebin.com/ewDXBT9C it works, but I think its weird since the request animation should stop without the need to check with a variable at every loop

Upvotes: 3

Views: 1044

Answers (2)

kalley
kalley

Reputation: 18462

You need to call update after setting the id

(function tick () {
    id = webkitRequestAnimationFrame(tick);
    update();
}());

Otherwise it's stopping the old one, but the new one is still getting set.

FIDDLE

To address the issue that Vincent Piel brings up, you could always do update like a promise. Here's a fiddle using this approach. It's similar to the one in his answer, except you don't need a scoped boolean.

function update() {
    var dfd = $.Deferred();
    x += 0.1;
    y += 0.1;

    if (x > 50) {
        dfd.reject();
        return;
    }

    draw();
    dfd.resolve();

    return dfd.promise();
}

(function tick () {
    update().done(function() {
        id = webkitRequestAnimationFrame(tick);
    });
}());

Upvotes: 4

GameAlchemist
GameAlchemist

Reputation: 19294

the behaviour you see is perfectly normal : what's happening is that you have the cancellation done in the update() method.
So what you are cancelling in fact is the previous rAF, and you launch another rAF whatever happened in the update.

(function tick() {

    parent.update(); // here we cancel the last window.animationID
    window.animationID = rAF(tick); // here we go again, we launch another rAF

}());

When you do it in the console, you will cancel the next rAF, hence the game stops.

So you need rather to use a boolean to have the loop to stop, and don't even save the animation id.

i don't know how you want to handle this boolean (closure / namespace / global variable) but it will look like :

var animCancelled = false;
(function tick() {

    parent.update(); 
    if (!animCancelled) rAF(tick); 

}());

Upvotes: 0

Related Questions