There are many ways to implement animation in web applications.

  1. Js implemented using setTimeout
  2. The CSS is implemented using transition and animation
  3. Html5 uses canvas for implementation. Besides, we will introduce requestAnimationFrame today, which literally translates to requestAnimationFrame.

Screen refresh rate

Refresh rate is the update rate on the screen, or the number of images on the screen per second, measured in Hertz (Hz). A common computer frequency is 60Hz. The monitor is constantly updating the image on the screen 60 times a second. Why don’t you feel this change? That is because people’s eyes have visual retention effect, that is, the impression of the previous picture in the brain has not disappeared, followed by the next picture followed by the middle of the 16.7ms interval (1000/60≈16.7), so you will mistakenly think that the image on the screen is still. The screen is right. If the refresh rate is 1 refresh per second, the image on the screen will flicker severely, which can easily cause eye strain, soreness and dizziness.

requestAnimationFrame

Window. RequestAnimationFrame () method tells the browser you want to perform the animation and request the browser before the next redraw call the specified function to update the animation. This method takes as an argument a callback function that is called before the browser redraws.

The biggest advantage of requestAnimationFrame over setTimeout is that the system decides when to execute the callback function. To be more specific, if the screen refresh rate is 60Hz, the callback function is executed every 16.7ms, if the refresh rate is 75 hz, the interval becomes 1000/75=13.3ms, in other words, the requestAnimationFrame moves at the pace of the system refresh. It ensures that the callback function is executed only once in each screen refresh interval, thus avoiding frame loss and animation stuttering.

Let’s implement the progress bar using requestAnimationFrame:

<div class="box"></div>
<button class="btn">run</button>
Copy the code
.box {
  background: hotpink;
  width: 0;
  height: 20px;
  line-height: 20px;
  border-radius: 10px;
  color: #fff;
  box-shadow: 0 0 10px hotpink;
  text-align: center;
}

.btn {
  background: skyblue;
  padding: 10px;
  margin-top: 20px;
  outline: 0;
  color: #fff;
  border-radius: 10px;
}
Copy the code
let timer
window.onload = (a)= > {
  const btn = document.querySelector('.btn')
  const box = document.querySelector('.box')
  const width = 500
  function callback () {
    if (parseInt(box.style.width) < width) {
      box.style.width = parseInt(box.style.width) + 5 + 'px'
      box.innerHTML = parseInt(box.style.width) / (width / 100) + The '%'
      timer = requestAnimationFrame(callback)
    } else {
      cancelAnimationFrame(timer)
    }
  }
  btn.onclick = (a)= > {
    box.style.width = '0'
    cancelAnimationFrame(timer)
    timer = requestAnimationFrame(callback)
  }
}
Copy the code

In addition, requestAnimationFrame has the following two advantages:

  • CPU energy saving: When using setTimeout to achieve animation, when the page is hidden or minimized, setTimeout is still performing animation tasks in the background, because the page is not visible or unavailable at this time, refreshing animation is meaningless, it is a complete waste of CPU resources. RequestAnimationFrame is completely different. When the page is not active, the screen refresh task of the page is also suspended, so the requestAnimationFrame that follows the pace of the system will also stop rendering. When the page is active, The animation picks up where it left off, effectively saving CPU overhead.

  • Function throttling: In high frequency events (resize, Scroll, etc.), to prevent multiple function executions in a refresh interval, requestAnimationFrame is used to ensure that functions are executed only once in each refresh interval. This ensures smooth performance and saves function execution costs. It does not make sense if the function is executed more than once in a refresh interval, because the monitor is refreshed every 16.7ms and multiple draws do not show up on the screen.

compatibility

RequestAnimationFrame has some compatibility issues and will probably not be maintained in the future, so it can be polyfilled.

; (function (window) {
  var lastTime = 0;
  var prefixes = 'webkit moz ms o'.split(' '); // Each browser prefix

  var requestAnimationFrame = window.requestAnimationFrame;
  var cancelAnimationFrame = window.cancelAnimationFrame;

  var prefix;
  // Get the implementations of requestAnimationFrame and cancelAnimationFrame in the current browser by iterating through the browser prefixes
  for( var i = 0; i < prefixes.length; i++ ) {
    if ( requestAnimationFrame && cancelAnimationFrame ) {
      break;
    }
    prefix = prefixes[i];
    requestAnimationFrame = requestAnimationFrame || window[ prefix + 'RequestAnimationFrame' ];
    cancelAnimationFrame  = cancelAnimationFrame  || window[ prefix + 'CancelAnimationFrame'] | |window[ prefix + 'CancelRequestAnimationFrame' ];
  }

  // If the current browser does not support requestAnimationFrame and cancelAnimationFrame, setTimeout will be used
  if(! requestAnimationFrame || ! cancelAnimationFrame ) { requestAnimationFrame =function( callback ) {
      var currTime = new Date().getTime();
      // To get setTimteout as close to 60 frames per second as possible
      var timeToCall = Math.max( 0.16 - ( currTime - lastTime ) ); 
      var id = window.setTimeout( function() {
      callback( currTime + timeToCall );
      }, timeToCall );
      lastTime = currTime + timeToCall;
      return id;
    };
    
    cancelAnimationFrame = function( id ) {
      window.clearTimeout( id );
    };
  }
  // Get a compatible API for all browsers
  window.requestAnimationFrame = requestAnimationFrame; 
  window.cancelAnimationFrame = cancelAnimationFrame; }) (window)
Copy the code

Blog address gitbook booklet