Javascript - Can't Adjust FrameRate - requestanimationframe
Asked Answered
Q

7

9

I start the loop

function gameLoop(){
   update();
   draw();
   requestAnimFrame(gameLoop);
}

var requestAnimFrame =  window.requestAnimationFrame ||
                    window.webkitRequestAnimationFrame ||
                    window.mozRequestAnimationFrame ||
                    window.oRequestAnimationFrame ||
                    window.msRequestAnimationFrame ||
                    function(callback) {
                        window.setTimeout(callback, 1000 / 1);
                    };
  1. I can't adjust the frame rate. It is always really fast. Why can't I change it to 1 frame a second. I want to do this just for testing purposes.
  2. Do I have to clear the canvas each time? It seems to work good without clearing it.

Thanks.

Here is a link to a fiddle for the complete code: complete code

Thanks

Quacksalver answered 25/9, 2013 at 8:41 Comment(0)
P
8

rAF is locked to monitor's sync, typically 60 Hz, so we can't adjust the FPS for it in itself (browser may reduce FPS when tab is inactive or on batteries).

Also, what you are trying to change is the fallback for the poly-fill; that is: if rAF is not supported in the browser it will instead use setTimeout. However, most browsers nowadays do support rAF (even un-prefixed) so the setTimeout will never be used.

You can do two things:

  • Replace rAF in your loop by using setTimeout directly (when testing)

Example:

var FPS = 1;

function testLoop() {

    ... ordinary code

    setTimeout(testLoop, 1000/FPS);
}
  • Throttle rAF by using a counter:

Example:

var framesToSkip = 60,
    counter = 0;

function loop() {

    if (counter < framesToSkip) {
        counter++;
        requestAnimationFrame(loop);
        return;
    }

    /// do regular stuff

    counter = 0;
    requestAnimationFrame(loop);
}

MODIFIED FIDDLE HERE

There are most likely better ways to implement throttling, but I am trying to just show the basic principle. This will still run at full speed, 60 FPS, but your code will do minimal of operations and only when the counter has reached its count will it execute the main code.

You do not need to clear the canvas each time if what you draw next will cover previously drawn content, or if you want to keep it of course. You can also clear a portion to optimize further, if needed.

Prophesy answered 25/9, 2013 at 8:57 Comment(0)
S
6

A bit late to the party, but here's how to get the benefit of RAF while also controlling frames/second.

Note: requestAnimationFrame now has a better way of doing things than by using the code pattern in my original 3 year old original answer ... see my update below for the new and improved way.

[Update: requestAnimationFrame now has a better way of throttling]

The new version of requestAnimationFrame now automatically sends in a current timestamp that you can use to throttle your code execution.

Here is example code to execute your code every 1000ms:

var nextTime=0;
var delay=1000;

function gameLoop(currentTime){
    if(currentTime<nextTime){requestAnimationFrame(gameLoop); return;}
    nextTime=currentTime+delay;
    // do stuff every 1000ms
    requestAnimationFrame(looper);
}

}

Scherman answered 25/9, 2013 at 15:25 Comment(5)
Sounds very logical and clever.Anastrophe
shouldn't you swap setTimeout and requestAnimationFrame somehow? In your example, update and draw are called at the setTimeout event, still async while the gameloop() body is in sync (but does nothing than setting the timeout)Bloomy
@DanielAlder. This code is a couple years old and now requestAnimationFrame automatically sends in a timestamp so the setTimeout is no longer required. Use the timestamp to throttle the animation.Scherman
Sure but your code is wrong anyway. It was in 2013 and it is now.Bloomy
@DanielAlder Well, this code is not "wrong" and it does indeed work...it's just been superceded by better capabilities. I've updated my answer to reflect the new capabilities :-)Scherman
M
2

You should look at this article which gives a proper treatment of the subject. http://creativejs.com/resources/requestanimationframe/

var fps = 15;
function draw() {
    setTimeout(function() {
        requestAnimFrame(draw);
        // Drawing code goes here
    }, 1000 / fps);
}

Here is the code I think you want, but in the original article it said used requestAnimationFrame, but here I am using requestAnimFrame. I think maybe it changed and you're supposed to use requestAnimFrame now. requestAnimationFrame did not work for me while requestAnimFrame did.

Mesolithic answered 21/7, 2014 at 4:43 Comment(0)
C
1

The way browsers and javascript work makes it difficult to set up a fixed frame rate. Say you want to do something every one second, like updating and drawing. One way of doing that could be to call window.setTimeout() with a setting of one second. But the problem is that this is not that reliable, even if you configure a callback every second you can't be sure all callbacks will be in time. A high processor load, for example, could make the callbacks arrive much later than they should. And even if the callbacks would be on time, you have no control of when the actual drawing to the screen will happen.

A better way of handling it is to accept the fact that you can't get a very precise timing of your calls, and instead, whenever you get a call, you calculate how much time has passed and act according to that. This means you'll let the system decide the frame rate, and you just take care of updating your animation or game depending on how much time that has passed.

requestAnimationFrame is a newer functionality supported by most browsers by now that is especially useful for games. It will be called every time the browser is ready to draw, which is good. Then you will know that the updates and drawing you are doing will happen right before the actual frame is drawn to screen.

Here's an example on how you could update your gameLoop to take the time difference into account.

var lastTimestamp = +new Date;

function gameLoop(timestamp) {
  var now = +new Date;
  var dt = now - lastTimestamp;

  // dt is the amount of time in ms that has passed since last call.
  // update takes this time difference (in seconds) and can then perform its
  // updates based on time passed.
  update(dt / 1000);
  draw();
  lastTimestamp = now;
  requestAnimationFrame(gameLoop);
}
Casto answered 25/9, 2013 at 9:12 Comment(0)
I
0

requestAnimationFrame will run with the maximum achievable frame rate (up to 60 fps). This is because it will always give you the next animation frame.

The parameter you adjusted is only for the polyfill, which will be active if your browser has no implementation of requestAnimationFrame.

If you want to try painting at one second for testing purposes, try setInterval instead.

Inwards answered 25/9, 2013 at 8:58 Comment(0)
P
0

That's how requestAnimationFrame works. If you want a specific framerate, use setTimeout only.

Usually you would take a parameter, which is the current time. Compare it to the last frame's time to find out how far along the animation should move.

Photometer answered 25/9, 2013 at 8:58 Comment(0)
A
0

Quite handy js library if you need to control Framrate in javascript https://github.com/aaronGoshine/Javascript-OnEnterFrame-Event-Manager/blob/master/index.html

Airt answered 8/12, 2013 at 22:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.