• RequestIdleCallback?

What is requestAnimationFrame

Tell the browser window. RequestAnimationFrame () – you want to perform an animation, and required the browser until the next redraw calls the specified callback function to update the animation. This method takes as an argument a callback function that is executed before the browser’s next redraw

The above quote is from MDN

In plain English, the API can use the browser display frequency as the frequency of its animation actions. For example, the browser refreshes every 10ms, and the animation callback is called every 10ms. In this way, there is no overdrawing problem, and the animation does not drop frames, so that it is natural and smooth.

The basic grammar

window.requestAnimationFrame(callback);
Copy the code
  • callback

The function called to update the animation frame before the next redraw (the above callback function). The callback function is passed the DOMHighResTimeStamp parameter, which has the same return value as performing.now () and represents the time when requestAnimationFrame() started executing the callback function.

  • The return value

A long integer, the request ID, is the unique identifier in the callback list. It’s a non-zero value, that’s all. You can send this value to the window. The cancelAnimationFrame () to cancel the callback function.

The basic application

The use of setTimeout is exactly the same as setTimeout, except that the current interval follows the system’s drawing frequency and is fixed

// Call the system interval
const element = document.getElementById('some-element-you-want-to-animate');
let start;

function step(timestamp) {
  if (start === undefined)
    start = timestamp;
  const elapsed = timestamp - start;

  // Use 'math.min ()' here to make sure the element stops at exactly 200px.
  element.style.transform = 'translateX(' + Math.min(0.1 * elapsed, 200) + 'px)';

  if (elapsed < 2000) { // Stop animation after two seconds
    window.requestAnimationFrame(step); }}var timer1 = window.requestAnimationFrame(step);

// Cancel the callback function
cancelAnimationFrame(timer1);

Copy the code

Compatibility processing

Firefox, Chrome, IE10 and above, requestAnimationFrame support is good, but not compatible with IE9 and below

// Simple compatibility handling
window.requestAnimationFrame = (function() {
  return window.requestAnimationFrame ||
         window.webkitRequestAnimationFrame ||
         window.mozRequestAnimationFrame ||
         function(callback) {
          window.setTimeout(callback, 1000/60);
         }
})();
Copy the code

However, the problem is that not all devices have a drawing interval of 1000/60ms, and there is no cancel related method, so the following is a more fully compatible method

(function() {
  var lastTime = 0;
  var vendors = ['webkit'.'moz'];
  / / if the window requestAnimationFrame for undefined try prefix is compatible browser
  for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
    window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame'];
    window.cancelAnimationFrame = window[vendors[x] + 'CancelAnimationFrame'] | |// The name of this cancel method in WebKit has changed
                                  window[vendors[x] + 'CancelRequestAnimationFrame'];
  }
  // If they are still incompatible, use setTimeOut for compatibility
  if(!window.requestAnimationFrame) {
    window.requestAnimationFrame = function(callback, element) {
      var currTime = new Date().getTime();
      var timeToCall = Math.max(0.16.7 - (currTime - lastTime));
      var id = window.setTimeout(function() {
        callback(currTime + timeToCall);
      }, timeToCall);
      lastTime = currTime + timeToCall;
      returnid; }}if(!window.cancelAnimationFrame) {
    window.cancelAnimationFrame = function(id) {
      clearTimeout(id); }}}) ();Copy the code

The above code was designed by Opera browser technologist Erik Moller to make it more compatible with various browsers, but basically his code is to determine whether to use a 4ms or 16ms delay to best match 60fps.

advantages

  • Make animation more smooth, prevent animation frame loss

RequestAnimationFrame brings together all DOM operations in each frame in a single redraw or reflow, and the redraw or reflow interval closely tracks the browser refresh rate

  • Resource saving (Cpu, memory, etc.)
    1. RequestAnimationFrame will not be redrawn or reflow in hidden or invisible elements, which of course means less CPU, GPU, and memory usage
    2. RequestAnimationFrame is a browser-specific API for animation that optimizes method calls at runtime and pauses animation automatically if the page is not active, saving CPU overhead

Pay attention to the point

::: warning

If you want to continue to update before next time to redraw the browser next frame animation, so the callback function itself must call window again. RequestAnimationFrame ()

: : :

  • You should call this method when you are ready to update the animation. This will cause the browser to call the animation function you passed into the method (that is, your callback) before the next redraw. Callbacks are typically executed 60 times per second, but in most browsers that follow W3C recommendations, the number of callbacks usually matches the number of browser screen refreshes. To improve performance and battery life, in most browsers requestAnimationFrame() is paused while running in background tabs or hidden to improve performance and battery life.

  • The callback function is passed the DOMHighResTimeStamp parameter, which indicates when the callback function currently sorted by requestAnimationFrame() is fired. Multiple callback functions in the same frame will each receive the same timestamp, even if some time has elapsed during the calculation of the workload for the previous callback function. The timestamp is a decimal number in milliseconds with a minimum accuracy of 1ms(1000μs).

RequestAnimationFrame relates to setInterval and setTimeout

  • Because setTimeout and setInterval are asynchronous apis, they must wait for the synchronous task to execute and for the microtask to complete before executing the current callback function.

  • There’s a problem here, there’s no way to pinpoint time, even if you write it as 16, there’s no way to pinpoint time to 16. The time interval is not guaranteed.

  • The biggest advantage of requestAnimationFrame over setTimeout is that the system decides when to execute the callback function.

If the system draw rate is 60Hz, then the callback function is executed every 16.7ms. If the draw frequency is 75 hz, then the interval becomes 1000/75=13.3ms, that is, its time interval follows the system draw frequency.

The performance comparison

  • Write an example to look at the performance metrics of various implementations

<div id="test" style="width: 0px; height: 12px; line-height: 12px; margin-bottom: 5px; background: rgb(185, 236, 243);"></div>Current Progress:<span id="progress">0%</span>
<button id="btn">open</button>
<script>
const btn = document.getElementById('btn');
// Use requestAnimationFrame implementation
btn.onclick = function() {
  var timer = requestAnimationFrame(function fn() {
    if (parseInt(test.style.width) < 300) {
      test.style.width = parseInt(test.style.width) + 3 + 'px';
      progress.innerHTML = parseInt(test.style.width) / 3 + The '%';
      timer = requestAnimationFrame(fn);
    } else{ cancelAnimationFrame(timer); }}); }// Use setInterval
// btn.onclick = function() {
// var timer = setInterval(function () {
// if (parseInt(test.style.width) < 300) {
// test.style.width = parseInt(test.style.width) + 3 + 'px';
// progress.innerHTML = parseInt(test.style.width) / 3 + '%';
// } else {
// clearInterval(timer);
/ /}
/ /}, 17);
// }

// Use setTimeout
//btn.onclick = function() {
// var timer = setTimeout(function fn() {
// if (parseInt(test.style.width) < 300) {
// test.style.width = parseInt(test.style.width) + 3 + 'px';
// progress.innerHTML = parseInt(test.style.width) / 3 + '%';
// timer = setTimeout(fn, 17);
// } else {
// clearTimeout(timer);
/ /}
/ /}, 17);
// }
</script>
Copy the code
  • Use setTimeout and setInterval frame rates

  • Use the frame rate of requestAnimationFrame

Dig into requestAnimationFrame and Event Loop

Event Loop is an internal browser mechanism for coordinating events, user interactions, scripting, rendering, and networking.

  • There are also several Event loops in the browser:
    • window event loop
    • worker event loop
    • worklet event loop

The main thing we’re talking about here is the Window Event loop. This is the Event Loop controlled by the main thread of a browser render process.

Basic process of Event Loop

  1. The convention must include a runnable task in the selected Task Queue (taskQueue). If there is no such task Queue, go to the microTasks step below.
  2. Make the oldestTask (oldestTask) in the taskQueue the first executable task, and then delete it from the taskQueue.
  3. Set the oldestTask above to the task running in the Event loop.
  4. Perform oldestTask.
  5. Set the running task in the Event loop to NULL.
  6. Perform microTasks checkpoints (that is, execute tasks in the MicroTasks queue).
  7. Set hasARenderingOpportunity to false.
  8. Update render.
  9. If the current queue is window Event Loop, the Task Queues have no tasks, the MicroTask Queue is empty, and the render opportunity variable hasARenderingOpportunity is false, Run the Idle Period (requestIdleCallback) command.
  10. Go back to step 1.

Basically, an Event loop is simply a queue that keeps finding out if there are executable tasks in Task Queues, pushing them to the Call Stack for execution if they exist, and updating the render when appropriate.

Figure 3 (source) shows a clear flow of the Event Loop running on the main thread of the browser:

In the specification above, the process of rendering is after the execution of the MicroTasks queue, and then take a closer look at the process of rendering.

Update rendering process

  1. Iterating through all the documents in the current browsing context, each document must be processed in the order found in the list.
  2. Rendering Opportunities: Delete all docs and cancel Rendering if there is no Rendering opportunity in the current browsing context (it is up to the browser to determine whether Rendering opportunities exist, depending on hardware refresh rate constraints, page performance, and whether the page is in the background).
  3. If the current document is not empty, set hasARenderingOpportunity to true.
  4. Unnecessary Rendering: If the browser considers that the rendering of the updated document’s browsing context will not be visible and the document’s Animation Frame Callbacks are empty, cancel rendering. Finally see the requestAnimationFrame
  5. Remove documents from Docs that the browser thinks are best to skip updating rendered for other reasons.
  6. If the browse context of a document is a top-level browse context, refresh the autofocus candidate for that document.
  7. To handle the resize event, pass in a performing.now () timestamp.
  8. Handle the Scroll event, passing in a performance.now() timestamp.
  9. Process the media query, passing in a performing.now () timestamp.
  10. Run the CSS animation, passing in a perform.now () timestamp.
  11. To handle a full-screen event, pass in a performing.now () timestamp.
  12. Execute the requestAnimationFrame callback, passing in a performing.now () timestamp.
  13. A performance.now() timestamp is passed in by executing the intersectionObserver callback.
  14. Draw each document.
  15. Update the UI and render.

The process is basically as shown in the figure below

At this point, the timing of the requestAnimationFrame callback is clear, and it will be called before Style/Layout/Paint.

The browser has an issue with Rendering opportunity, which is when the browser decides whether or not to render based on the current viewing context. It tries to be as efficient as possible, only Rendering when necessary, and will not render if there are no interface changes. As stated in the specification, due to the limitation of hardware refresh frequency, page performance and whether the page has a background and other factors, it is possible to find that the rendering time is not reached after executing the setTimeout task, so the setTimeout callback is performed several times before rendering

Application scenario of requestAnimationFrame

  • Big data rendering

In the rendering process of big data, such as the rendering of tables, if some performance strategies are not processed, THE UI will be frozen and the user experience will be very poor. In one scenario, inserting 100,000 records returned in the background into a table would cause the page to stall for about 5 seconds if DOM elements were generated in a loop at once. At this point, we can use requestAnimationFrame for step-by-step rendering to determine the best time interval for smooth page loading.

var total = 100000;
var size = 100;
var count = total / size;
var done = 0;
var ul = document.getElementById('list');

function addItems() {
    var li = null;
    var fg = document.createDocumentFragment();

    for (var i = 0; i < size; i++) {
        li = document.createElement('li');
        li.innerText = 'item ' + (done * size + i);
        fg.appendChild(li);
    }

    ul.appendChild(fg);
    done++;

    if(done < count) { requestAnimationFrame(addItems); }}; requestAnimationFrame(addItems);Copy the code
  • Realization of animation

Css3 implementation has greatly improved performance and smoothness, but also has great limitations, such as not all attributes can participate in the animation, the animation process can not be fully controlled, animation slow effect is too small, etc.

On the contrary, setTimeout and setInterval can achieve more controllable own frame animation, but due to the difference in refresh time and timer time, frame drop phenomenon will occur. The shorter timer time is set, the more serious frame drop time will be, and the performance sacrifice is very serious

However, the advent of requestAnimationFrame gives us a better alternative to these two commonly used scenarios

Knowledge of screen drawing 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 normal laptop, this frequency is about 60Hz, which can be viewed and set by right-clicking on the desktop > screen resolution > advanced Settings > monitor. This value depends on the screen resolution, screen size, and graphics card. In principle, it can be set to a comfortable value for the eyes.

There are two kinds of monitors commonly used in the market, namely, CRT and LCD. CRT is a Cathode Ray Tube and LCD is Liquid Crystal Display. CRT is a display that uses a cathode ray tube. The graphic images on the screen are composed of fluorescent points that emit light due to the impact of an electron beam. As the phosphor in the CRT has a short luminous time after being hit by an electron beam, the electron beam must continuously hit the phosphor to make it continue to emit light. The number of times the electron beam hits the phosphor per second is the screen rendering frequency.

For LCDS, however, there is no problem with drawing frequency, because each pixel in the LCD continues to glow until the non-glowing voltage changes and is sent to the controller, so the LCD does not have the flicker caused by the electron beam hitting the phosphor.

As a result, the computer screen is constantly updating the image 60 times per second, even when you’re looking at it and doing nothing. 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.

  • Principles of CSS Animation

According to the principle above, what you’re seeing is an image being drawn 60 times per second, so high that you can’t feel it being drawn. The essence of animation is to let the human eye see the visual effect of the changes caused by the drawing of the image, and this change should be carried out in a coherent and smooth way. So how do you do that?

A 60Hz screen is drawn every 16.7ms. If you move the position of the elements to the left one pixel, i.e., 1px, before each screen is drawn, you will see the image moving by 1px each time the screen is drawn. And because of the visual stop effect, the image stays in the brain in the current position, and then the image is moved to the next position, so you see the effect that the image is moving smoothly. This is the visual animation.

The author established a full stack large front-end communication group, like to discuss hot technology, please add wechat: Mokinzhao

  • Front-end technology summary: full stack big front-end column

reference

RequestAnimationFrame understanding and practice

RequestAnimationFrame execution mechanism exploration