Setting the frame rate

Using the time to set up the frame rate of the animation

Principle: even if the mainloop is called 60 times per second, ignore some frames in order to reach the desired frame rate

It is also possible to set the frame rate using time based animation: we can set a global variable that corresponds to the desired frame rate and compare the elapsed time between two executions of the animation loop:

    • If the time elapsed is too short for the target frame rate: do nothing,
    • If the time elapsed exceeds the delay corresponding to the chosen frame rate: draw the frame and reset this time to zero.

Here is the an online example at JSBin.

Try to change the parameter value of the call to: 

  1. setFrameRateInFramesPerSecond(5); // try other values!

Screenshot of a jsbin example with an animated curve, we can set the framerate in this example...

Source code of the example:

  1. <!DOCTYPE html>
  2. <html lang=“en”>
  3.  <head>
  4.  <meta charset=utf-8 />
  5.  <title>Set framerate using a high resolution timer</title>
  6.  </head>
  7.  <body>
  8.  <p>This example measures and sums deltas of time between consecutive frames of animation. It includes a <code>setFrameRateInFramesPerSecond</code> function you can use to reduce the number of frames per second of the main animation.</p>
  9.  <canvas id=“myCanvas” width=“700” height=“350”>
  10.  </canvas>
  11.  
  12.    var canvas = document.querySelector(“#myCanvas”);
  13.    var ctx = canvas.getContext(“2d”);
  14.    var width = canvas.width, height = canvas.height;
  15.    var lastX = width * Math.random();
  16.    var lastY = height * Math.random();
  17.    var hue = 0;
  18.    // Michel Buffa: set the target frame rate. TRY TO CHANGE THIS VALUE AND SEE
  19.    // THE RESULT. Try 2 frames/s, 10 frames/s, 60 frames/s Normally there
  20.    // should be a limit of 60 frames/s in the browser’s implementations.
  21.    setFrameRateInFramesPerSecond(60);
  22.   // for time based animation. DelayInMS corresponds to the target framerate
  23.   var now, delta, delayInMS, totalTimeSinceLastRedraw = 0;
  24.   // High resolution timer
  25.   var then = performance.now();
  26.   // start the animation
  27.   requestAnimationFrame(mainloop);
  28.   function setFrameRateInFramesPerSecond(frameRate) {
  29.     delayInMs = 1000 / frameRate;
  30.   }
  31.   // each function that is going to be run as an animation should end by
  32.   // asking again for a new frame of animation
  33.   function mainloop(time) {
  34.     // Here we will only redraw something if the time we want between frames has
  35.     // elapsed
  36.     // Measure time with high resolution timer
  37.     now = time;
  38.     // How long between the current frame and the previous one?
  39.     delta = now  then;
  40.     // TRY TO UNCOMMENT THIS LINE AND LOOK AT THE CONSOLE
  41.     // console.log(“delay = ” + delayInMs + ” delta = ” + delta + ” total time = ” +
  42.     // totalTimeSinceLastRedraw);
  43.     // If the total time since the last redraw is > delay corresponding to the wanted
  44.     // framerate, then redraw, else add the delta time between the last call to line()
  45.     // by requestAnimFrame to the total time..
  46.     if (totalTimeSinceLastRedraw > delayInMs) {
  47.        // if the time between the last frame and now is > delay then we
  48.        // clear the canvas and redraw
  49.        ctx.save();
  50.        // Trick to make a blur effect: instead of clearing the canvas
  51.        // we draw a rectangle with a transparent color. Changing the 0.1
  52.        // for a smaller value will increase the blur…
  53.        ctx.fillStyle = “rgba(0,0,0,0.1)”;
  54.        ctx.fillRect(0, 0, width, height);
  55.        ctx.translate(width / 2, height / 2);
  56.        ctx.scale(0.9, 0.9);
  57.        ctx.translate(-width / 2, height / 2);
  58.        ctx.beginPath();
  59.        ctx.lineWidth = 5 + Math.random() * 10;
  60.        ctx.moveTo(lastX, lastY);
  61.        lastX = width * Math.random();
  62.        lastY = height * Math.random();
  63.        ctx.bezierCurveTo(width * Math.random(),
  64.                          height * Math.random(),
  65.                          width * Math.random(),
  66.                          height * Math.random(),
  67.                          lastX, lastY);
  68.        hue = hue + 10 * Math.random();
  69.        ctx.strokeStyle = “hsl(“ + hue + “, 50%, 50%)”;
  70.        ctx.shadowColor = “white”;
  71.        ctx.shadowBlur = 10;
  72.        ctx.stroke();
  73.        ctx.restore();
  74.        // reset the total time since last redraw
  75.        totalTimeSinceLastRedraw = 0;
  76.     } else {
  77.        // sum the total time since last redraw
  78.        totalTimeSinceLastRedraw += delta;
  79.     }
  80.     // Store time
  81.     then = now;
  82.     // request new frame
  83.     requestAnimationFrame(mainloop);
  84.   }
  85.  
  86.  </body>
  87. </html>

SAME TECHNIQUE WITH THE BOUNCING RECTANGLE. SEE HOW WE CAN set BOTH SPEED AND FRAME-RATE USING A HIGH-RESOLUTION TIME

Here is a modified version on JSBin of the example with the rectangle that also uses this techniqueIn this version, you can change both the speed in pixels/s and the frame rate.

Source code:

  1. <!DOCTYPE html>
  2. <html lang=“en”>
  3. <head>
  4. <meta charset=utf-8 />
  5. <title>Bouncing rectangle with high resolution timer and adjustable frame rate</title>
  6.  
  7.    var canvas, ctx;
  8.    var width, height;
  9.    var x, y, incX; // incX is the distance from the previously drawn rectangle
  10.                    // to the new one
  11.    var speedX; // speedX is the target speed of the rectangle in pixels/s
  12.    // for time based animation, DelayInMS corresponds to the target frame rate
  13.    var now, delta, delayInMS, totalTimeSinceLastRedraw=0;
  14.    // High resolution timer
  15.    var then = performance.now();
  16.    // Michel Buffa: set the target frame rate. TRY TO CHANGE THIS VALUE AND SEE
  17.    // THE RESULT. Try 2 frames/s, 10 frames/s, 60, 100 frames/s Normally there
  18.    // should be a limit of 60 frames/s in the browser’s implementations, but you can 
  19.    // try higher values
  20.    setFrameRateInFramesPerSecond(25);
  21.    function setFrameRateInFramesPerSecond(framerate) {
  22.      delayInMs = 1000 / framerate;
  23.    }
  24.    // Called after the DOM is ready (page loaded)
  25.    function init() {
  26.      // init the different variables
  27.      canvas = document.querySelector(“#mycanvas”);
  28.      ctx = canvas.getContext(‘2d’);
  29.      width = canvas.width;
  30.      height = canvas.height;
  31.      x=10; y = 10;
  32.      // Target speed in pixels/second, try with high values, 1000, 2000…
  33.      speedX = 2000;
  34.      // Start animation
  35.      requestAnimationFrame(animationLoop)
  36.    }
  37.    function animationLoop(time) {
  38.      // Measure time with high resolution timer
  39.      now = time;
  40.      // How long between the current frame and the previous one?
  41.      delta = now  then;
  42.      if(totalTimeSinceLastRedraw > delayInMs) {
  43.        // Compute the displacement in x (in pixels) in function of the time elapsed
  44.        // since the last draw and
  45.        // in function of the wanted speed. This time, instead of delta we
  46.        // use totalTimeSinceLastRedraw as we’re not always drawing at
  47.        // each execution of mainloop
  48.        incX = calcDistanceToMove(totalTimeSinceLastRedraw, speedX);
  49.        // an animation involves: 1) clear canvas and 2) draw shapes,
  50.        // 3) move shapes, 4) recall the loop with requestAnimationFrame
  51.        // clear canvas
  52.        ctx.clearRect(0, 0, width, height);
  53.        ctx.strokeRect(x, y, 10, 10);
  54.        // move rectangle
  55.        x += incX;
  56.       // check collision on left or right
  57.       if((x+10 >= width) || ( 0)) {
  58.         // cancel move + inverse speed
  59.         x -= incX;
  60.         speedX = speedX;
  61.       }
  62.       // reset the total time since last redraw
  63.       totalTimeSinceLastRedraw = delta;
  64.    } else {
  65.      // sum the total time since last redraw
  66.      totalTimeSinceLastRedraw += delta;
  67.    }
  68.    // Store time
  69.    then = now;
  70.    // animate. 
  71.    requestAnimationFrame(animationLoop);
  72.  }
  73.  var calcDistanceToMove = function(delta, speed) {
  74.     return (speed * delta) / 1000;
  75.  }
  76.  
  77. </head>
  78. <body onload=init();>
  79.  <canvas id=“mycanvas” width=“200” height=“50” style=border: 2px solid black></canvas>
  80. </body>
  81. </html>

could we Use setInterval?

It’s quite possible to use setInterval(function, interval) if you do not need an accurate scheduling.

To animate a monster at 60 fps but blinking his eyes once per second, you would use a mainloop with requestAnimationFrame and target a 60 fps animation, but you would also have a call to setInterval(changeEyeColor, 1000); and the changeEyeColor function will update a global variable, eyeColor, every second, which will be taken into account within the drawMonster function, called 60 times/s from the mainloop.

Leave a comment