52

I'm new to animation, but I have recently created an animation using setTimeout. The FPS was too low, so I found a solution to use requestAnimationFrame, described in this link.

So far, my code is:

//shim layer with setTimeout fallback
    window.requestAnimFrame = (function(){
        return  
            window.requestAnimationFrame       || 
            window.webkitRequestAnimationFrame || 
            window.mozRequestAnimationFrame    || 
            window.oRequestAnimationFrame      || 
            window.msRequestAnimationFrame     || 
            function(/* function */ callback){
                window.setTimeout(callback, 1000 / 60);
            };
    })();
    (function animloop(){
        //Get metrics
        var leftCurveEndX = finalLeft - initialLeft;
        var leftCurveEndY = finalTop + finalHeight - initialTop;
        var rightCurveEndX = finalLeft + finalWidth - initialLeft - initialWidth;
        var rightCurveEndY = leftCurveEndY;

        chopElement(0, 0, 0, 0, leftCurveEndX, leftCurveEndY, rightCurveEndX, rightCurveEndY);//Creates a new frame 
        requestAnimFrame(animloop);
    })();

This stops during the first frame. I have a callback function requestAnimFrame(animloop); in the chopElement function.

Also, is there a more thorough guide to using this API?

hopper
  • 13,060
  • 7
  • 49
  • 53
einstein
  • 13,389
  • 27
  • 80
  • 110

3 Answers3

83

Warning! This question is not about the best way to shim requestAnimFrame. If you are looking for that, move on to any other answer on this page.


You got tricked by automatic semicolon insertion. Try this:

window.requestAnimFrame = function(){
    return (
        window.requestAnimationFrame       || 
        window.webkitRequestAnimationFrame || 
        window.mozRequestAnimationFrame    || 
        window.oRequestAnimationFrame      || 
        window.msRequestAnimationFrame     || 
        function(/* function */ callback){
            window.setTimeout(callback, 1000 / 60);
        }
    );
}();

javascript automatically puts a semicolon behind your return statement. It does this because it is followed by a newline and the next line is a valid expression. In fact it gets translated to:

return;
window.requestAnimationFrame       || 
window.webkitRequestAnimationFrame || 
window.mozRequestAnimationFrame    || 
window.oRequestAnimationFrame      || 
window.msRequestAnimationFrame     || 
function(/* function */ callback){
    window.setTimeout(callback, 1000 / 60);
};

This code returns undefined and never executes the code behind the return statement. So window.requestAnimFrame is undefined. When you call it in animloop, the javascript produces an error and stops execution. You can solve the problem by enclosing the expression in parentheses.

May I recommend the Chrome developer tools or firebug to inspect javascript execution. With these tools you would have seen the error. You should go about debugging it as follows (I'm assuming Chrome):

  1. Execute the code (it produces unexpected results)
  2. Open the developer tools (right click -> Inspect Element) You will see a red x in the status bar at the right (this means there is an error in the execution)
  3. Open the console tab
  4. You will see
    Uncaught TypeError: Property 'requestAnimFrame' of object [object DOMWindow] is not a function
  5. Type in the console: window.requestAnimFrame and press enter, you will see it is undefined. By now you know that the problem is in fact unrelated to requestAnimationFrame and that you should concentrate on the first part of your code.
  6. Now it is a matter of narrowing down the code up to the point where it returns something. This is the difficult part and if you still don't find it at this point you might want to turn to the internet for more help.

Also, watch this video for some good practices in writing javascript, He also mentions the evil automatic semicolon insertion.

Jan
  • 8,011
  • 3
  • 38
  • 60
  • using this method with events such as `scroll` or `resize` in browsers who don't support `requestAnimationFrame` will fire the callback all the time, and not like intended, because the setTimeout will be fired constantly, creating new timers and running them one after the other. a throttle method is better suited to compensate. also, you do not need to write `window` before every global...it's redundant. – vsync Mar 17 '14 at 20:12
  • writing "window" before globals can make it clear to other developers reading the code that those may be built-ins, and are not functions provided in the local scope somehow. Given how complex scope can be in modern js apps with bower/webpack/etc. these days, that's reasonable. Doesn't hurt anything. – Kyle Baker Jan 12 '21 at 12:38
8
 /*
  Provides requestAnimationFrame in a cross browser way.
  http://paulirish.com/2011/requestanimationframe-for-smart-animating/
 */

if (!window.requestAnimationFrame) {

    window.requestAnimationFrame = (function() {

        return window.webkitRequestAnimationFrame ||
            window.mozRequestAnimationFrame || // comment out if FF4 is slow (it caps framerate at ~30fps: https://bugzilla.mozilla.org/show_bug.cgi?id=630127)
        window.oRequestAnimationFrame ||
            window.msRequestAnimationFrame ||
            function( /* function FrameRequestCallback */ callback, /* DOMElement Element */ element) {

                window.setTimeout(callback, 1000 / 60);

        };

    })();

}

animate();

function animate() {
    requestAnimationFrame(animate);
    draw();
}

function draw() {
    // Put your code here
}

Have a look at the below jsfiddle example; It illustrates clearly what I mean;

http://jsfiddle.net/XQpzU/4358/light/

Hope this helps!

Gokhan Tank
  • 3,786
  • 1
  • 22
  • 19
  • 1
    Your call inside animate() is using the wrong function name. Paul uses requestAnimFrame in his example indeed, but you are shimming requestAnimationFrame (which I like better btw). Easy to fix. – Micros Sep 12 '13 at 13:45
  • Thanks for the comment, Micros! I've edited it. I mostly prefer to use long-naming functions like you like to. I think that habit came to me from using obj-c language :) – Gokhan Tank Sep 25 '13 at 03:58
0

"Smart throttling so the event won't get fired more times than the screen can repaint the change:

var requestFrame = window.requestAnimationFrame ||
                   window.webkitRequestAnimationFrame ||
                   // polyfill - throttle fall-back for unsupported browsers
                   (function() {
                       var throttle = false,
                           FPS = 1000 / 60; // 60fps (in ms)
       
                       return function(CB) {
                         if( throttle ) return;
                         throttle = true;
                         setTimeout(function(){ throttle = false }, FPS);
                         CB(); // do your thing
                       }
                    })();

/////////////////////////////
// use case:

function doSomething() {
  console.log('fired');
}

window.onscroll = function() {
  requestFrame(doSomething);
};
html, body{ height:300%; }
body::before{ content:'scroll here'; position:fixed; font:2em Arial; }
vsync
  • 118,978
  • 58
  • 307
  • 400
  • I don't know but the FPS maybe should be 1000/60 as 60fps – Ampersanda Feb 26 '18 at 13:56
  • Sweet, Hey vsync do you know or something about easing the scroll page with also requestAnimationFrame? – Ampersanda Feb 28 '18 at 05:42
  • @Ampersanda - easing in what way? when the user uses the mousewheel to manually scroll? – vsync Feb 28 '18 at 09:45
  • is it ok to be running `requestAnimationFrame` nonstop (i.e. at 60 fps indefinitely)? isnt it costly to be running it all the time, even if its at only 60fps? – oldboy Nov 18 '20 at 08:08
  • 1
    @oldboy - it's is super-okay and **absolutely *not*** costly. – vsync Nov 18 '20 at 08:11
  • so like initiating `requestAnimationFrame` in the global scope and then looping it infinitely, even if its not updating styles, is not going to cause problems, say, on mobile devices? sorry if this question seems similar to the last one. i have yet to familiarize myself with `requestAnimationFrame` and am kinda baffled because it instinctively seems costly – oldboy Nov 18 '20 at 08:13
  • What does a scope of a function has to do with performance? no performance lost, ever, why are you worrying so much over nothing? just test it and see. – vsync Nov 18 '20 at 08:16
  • it just seems more efficient to use a killswitch--specifically for smooth scrolling, but does this then break the smooth scrolling effect because it essentially sends separate or independent "tasks" of updating the scroll position via LERP to separate rendering cycles?? i am simply utterly confused right now. i have been trying to implement smooth scrolling for over a day. although i have a working version, i was hoping to improve upon it. but ofc to improve it i need to understand exactly whats going on first :( – oldboy Nov 18 '20 at 08:24