Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

requestAnimationFrame is passing unexpected parameters in IE10

So I've been a good net citizen, using feature detection to see whether the browser supports requestAnimationFrame and only fall back to a setTimeout-based solution otherwise (something around the lines of Paul Irish's famous post).

var NOW = Date.now || function () { return new Date.getTime(); };
var reqAnimFrame =
        window.requestAnimationFrame ||
        window.webkitRequestAnimationFrame ||
        /*                        ... ||                     */
        function (callback) {
            setTimeout(function () { callback(NOW()); }, 1000 / 60);
        };

var previousTime = NOW();
function animStep(time) {
    var timePassed = time - previousTime;
    myCharacter.move(myCharacter.speed * timePassed);
    previousTime = time;
    reqAnimationFrame(animStep);
} 

// start the animation
reqAnimationFrame(animStep); 

This worked great everywhere until Internet Explorer 10 came along. In IE10, the time parameter passed doesn't seem to have anything to do with the current time, screwing up the calculation of timePassed.

What's going on?

like image 483
balpha Avatar asked Dec 18 '12 15:12

balpha


1 Answers

All (as far as I know) other browsers that implement requestAnimationFrame go by the specification in the (at the time of writing) current Working Draft:

Let time be [the redraw time] expressed as the number of milliseconds since 1970-01-01T00:00:00Z.

That's representing time precisely like your NOW() does.

IE10 however goes by the spec in the current Editor's Draft:

Let time be the result of invoking the now method of the Performance interface within this context.

Which essentially means the number of milliseconds since the browser loaded this page (it also means the the measurement is more precise, since performance.now returns fractional milliseconds).

And thus when you calculate timePassed for the first time in IE10, you are getting something like negative 43 years.

Fortunately, because the value passed to the requestAnimationFrame callback has the same unit in either case, just a different point of reference, you can easily adjust to this.

There are three possibilities:

  1. You could just throw away the very first animation step, using it only to set previousTime, but not doing anything else.
  2. You could ignore the passed parameter and use your NOW() (or performance.now) every time, thus always having the same same point of reference.
  3. Or you could change the start of the animation to this:

    // start the animation
    reqAnimationFrame(function (t) {
        previousTime = t - (NOW() - previousTime);
        animStep(t);
    );
    

    This will make the calculation (including the first one) of timePassed correct no matter which spec the browser follows. And since it changes only the very first invocation, you don't have any additional overhead in the long run either.

like image 134
balpha Avatar answered Sep 19 '22 16:09

balpha