1

I'm trying to cancel a requestAnimationFrame loop, but I can't do it because each time requestAnimationFrame is called, a new timer ID is returned, but I only have access to the return value of the first call to requestAnimationFrame.
Specifically, my code is like this, which I don't think is entirely uncommon:

function animate(elem) {

  var step = function (timestamp) {

    //Do some stuff here.
    if (progressedTime < totalTime) {
      return requestAnimationFrame(step); //This return value seems useless.
    }

  };

  return requestAnimationFrame(step);

}

//Elsewhere in the code, not in the global namespace.
var timerId = animate(elem);

//A second or two later, before the animation is over.
cancelAnimationFrame(timerId); //Doesn't work!

Because all subsequent calls to requestAnimationFrame are within the step function, I don't have access to the returned timer ID in the event that I want to call cancelAnimationFrame.

Looking at the way Mozilla (and apparently others do it), it looks like they declare a global variable in their code (myReq in the Mozilla code), and then assign the return value of each call to requestAnimationFrame to that variable so that it can be used any time for cancelAnimationFrame.

Is there any way to do this without declaring a global variable?
Thank you.

HartleySan
  • 7,404
  • 14
  • 66
  • 119

1 Answers1

6

It doesn't need to be a global variable; it just needs to have scope such that both animate and cancel can access it. I.e. you can encapsulate it. For example, something like this:

var Animation = function(elem) {
  var timerID;
  var step = function() {
    // ...
    timerID = requestAnimationFrame(step);
  };
  return {
    start: function() {
      timerID = requestAnimationFrame(step);
    }
    cancel: function() {
      cancelAnimationFrame(timerID);
    }
  };
})();

var animation = new Animation(elem);
animation.start();
animation.cancel();
timerID; // error, not global.

EDIT: You don't need to code it every time - that's why we are doing programming, after all, to abstract stuff that repeats so we don't need to do it ourselves. :)

var Animation = function(step) {
  var timerID;
  var innerStep = function(timestamp) {
    step(timestamp);
    timerID = requestAnimationFrame(innerStep);
  };
  return {
    start: function() {
      timerID = requestAnimationFrame(innerStep);
    }
    cancel: function() {
      cancelAnimationFrame(timerID);
    }
  };
})();
var animation1 = new Animation(function(timestamp) {
  // do something with elem1
});
var animation2 = new Animation(function(timestamp) {
  // do something with elem2
});
Amadan
  • 191,408
  • 23
  • 240
  • 301
  • You are right in your solution, but what is annoying with it is that I then have to code out something similar every time I want to use `requestAnimationFrame`. Of course, realistically, I don't use `requestAnimationFrame` that often (maybe 2-3 places in my code), but still, if I have to code something like that out each time I use it, I'd rather just declare a global / properly encapsulated variable, and just know to use that variable for the timer ID in all cases. Not ideal, but I'm not going to add a bunch of code to work around the bad design of `requestAnimationFrame`. Any other ideas? – HartleySan Jul 08 '15 at 02:20