In Web applications, there are many ways to realize animation effects, such as setTimeout timer in Javascript, Transition and animation in CSS3, and canvas in HTML5. Html5 also provides an API for requesting animations, requestAnimationFrame (requestAnimationFrame).

I. Related concepts

To understand the principles behind requestAnimationFrame, we first need to look at a few concepts related to it:

1. The page is visible

When the page is minimized or switched to a background TAB, the page is not visible, and the browser fires a VisiBilityChange event and sets the Document. hidden property to true; When you switch to the display state, the page is visible, which also fires a visiBilityChange event and sets the Document. hidden property to false.

2. The animation frame requests a list of callback functions

Each Document has a list of animated frame request callback functions, which can be thought of as a collection of

tuples. Where handlerId is an integer that uniquely identifies the tuple’s position in the list; Callback is a callback function.
,>

3. Screen refresh frequency

The rate at which an image is updated on the screen, or the number of times an image appears on the screen per second, is measured in Hertz (Hz). For a typical laptop, the frequency is about 60Hz, depending on screen resolution, screen size and graphics card.

4. Animation principle

According to the principle above, the image you see in front of you is refreshing at a rate of 60 times per second, so high that you don’t feel it refreshing. The essence of animation is to let the human eye see the visual effect of the change caused by the refresh of the image, and this change should be carried out in a coherent and smooth way. So how do you do that?

The screen with a refresh rate of 60Hz is refreshed every 16.7ms. Before each refresh, we move the position of the image to the left by one pixel, i.e. 1px. This way, each image on the screen will be 1px worse than the previous one, so you’ll see it move; Due to the visual stop effect of our human eyes, the impression of the current position of the image stays in the brain is not lost, and then the image is moved to the next position, so you can see the image in smooth movement, which is the visual effect of animation.

requestAnimationFrame

Asynchronously, the incoming function is called before the redraw.

HandlerId = requestAnimationFrame(callback)

(1) Pass in a callback function;

HandlerId is a browser-defined integer greater than 0 that uniquely identifies the position of the callback function in the list.

2. Browser execution process:

(1) First determine whether the document.hidden property is true, that is, the page will be executed only when it is visible;

(2) The browser clears the animation function of the previous round;

(3) This method returns the value of handlerId and the animation function callback,

into the animation frame request callback sequence;
,>

(4) The browser will iterate over the animation frame request callback function list and execute the corresponding animation function in turn according to the value of handlerId.

3. Cancel the animation function method:

cancelAnimationFrame(handlerId)Copy the code

SetTimeout and requestAnimationFrame

1.  setTimeout

After understanding the above concept, it is not difficult to find that setTimeout is actually to change the position of the image continuously by setting an interval time, so as to achieve the animation effect. However, animation realized by seTimeout will appear stutter and jitter on some low-end computers. There are two reasons for this phenomenon:

  • setTimeoutThe execution time is not fixed. In Javascript,setTimeout The task is placed in an asynchronous queue, and only after the main thread is finished does it check whether the task in the queue needs to be executed, soThe actual execution time of setTimeout is usually later than the specified time.
  • The refresh rate is affected by the screen resolution and screen size, so the refresh rate may vary from device to device, whilesetTimeoutYou can only set a fixed interval, which is not necessarily the same as the screen refresh time.

In both cases, the execution pace of setTimeout is inconsistent with the refresh pace of the screen, resulting in frame loss. So why does being out of step cause frame loss?

First of all, it is important to understand that setTimeout only changes the image properties in memory, and this change will not be updated to the screen until the next time the screen is refreshed. If the two are out of step, it is possible to skip the action in one frame and update the image in the next. Assuming the screen refreshes every 16.7ms and setTimeout sets the image to move 1px to the left every 10ms, the following drawing process will occur:

  • 0ms: Screen not refreshed, waiting,setTimeoutNot executed, waiting;
  • 10ms: Screen not refreshed, waiting,setTimeoutStart executing and set image properties left=1px;
  • Ms 16.7: The screen starts to refresh and the image on the screen moves to the left1px.setTimeout If no, continue waiting.
  • 20ms: Screen not refreshed, waiting,setTimeoutStart executing and set left=2px;
  • 30ms: Screen not refreshed, waiting,setTimeoutStart executing and set left=3px;
  • 33.4ms: The screen starts to refresh and the image on the screen moves to the left3px.setTimeoutIf no, continue waiting.

As can be seen from the drawing process above, the screen does not update the left=2px frame, and the image directly jumps from 1px to 3px, which is a frame loss phenomenon, and this phenomenon will cause animation lag.

2.  requestAnimationFrame

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.

The API call is simple, as follows:

var progress = 0;
// The callback function
function render() {  
  progress += 1; // Change the position of the image
  if (progress < 100) {  // Render recursively before the animation ends
    window.requestAnimationFrame(render); }}// Render first frame
window.requestAnimationFrame(render);Copy the code

In addition, requestAnimationFrame has the following two advantages:

  • The CPU energy savingUse:setTimeoutThe animation implemented when the page is hidden or minimized,setTimeout Animation tasks are still performed in the background, and since the page is not visible or available at this point, refreshing the animation is pointless and a waste of CPU resources. whilerequestAnimationFrameHowever, when the page processing is not active, the screen refresh task of the page is also suspended by the system, so follow the system steprequestAnimationFrameRendering also stops, and when the page is activated, the animation picks up where it left off, saving CPU overhead.
  • Function of the throttle: At high frequency events (resize.scrollIn order to prevent multiple function executions from occurring within a refresh interval, therequestAnimationFrameIn each refresh interval, the function can be executed only once, which not only ensures the smoothness, but also saves the cost of function execution. 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.

3. Downgrade gracefully

RequestAnimationFrame currently has compatibility issues and requires different prefixes for different browsers. Therefore, the requestAnimationFrame needs to be wrapped in a gracefully degraded manner, prioritizing advanced features, and then rolling back depending on the browser, until only setTimeout is available. The following code is a polyfill provided by someone on Github. Please refer to github requestAnimationFrame for details.

if (!Date.now)
    Date.now = function() { return new Date().getTime(); };
 
(function() {
    'use strict';
     
    var vendors = ['webkit'.'moz'];
    for (var i = 0; i < vendors.length && !window.requestAnimationFrame; ++i) {
        var vp = vendors[i];
        window.requestAnimationFrame = window[vp+'RequestAnimationFrame'];
        window.cancelAnimationFrame = (window[vp+'CancelAnimationFrame'] | |window[vp+'CancelRequestAnimationFrame']);
    }
    if (/iP(ad|hone|od).*OS 6/.test(window.navigator.userAgent) // iOS6 is buggy
        || !window.requestAnimationFrame || !window.cancelAnimationFrame) {
        var lastTime = 0;
        window.requestAnimationFrame = function(callback) {
            var now = Date.now();
            var nextTime = Math.max(lastTime + 16, now);
            return setTimeout(function() { callback(lastTime = nextTime); },
                              nextTime - now);
        };
        window.cancelAnimationFrame = clearTimeout;
    }
}());
Copy the code


(The article is purely for personal memo purposes, some quotes from the Internet and personal understanding. Welcome to reprint, please indicate the source. Please feel free to tip me if it will help you.