Timers have always been the core technology of JavaScript animation. The key to writing an animation loop is to know how long the delay time is. On the one hand, the loop interval must be short enough to make different animation effects appear smooth; on the other hand, the loop interval must be long enough to ensure that the browser has the ability to render the resulting changes.
The refresh rate of most computer monitors is 60Hz, which roughly equates to 60 redraws per second. Most browsers will limit redraw operations to no more than the display's redraw frequency, because even beyond that frequency the user experience will not be improved. Therefore, the optimal loop interval for the smoothest animation is 1000ms/60, which is approximately equal to 16.6ms.
The problem with setTimeout and setInterval is that they are not precise. Their internal operating mechanism determines that the time interval parameter actually just specifies the time to add the animation code to the browser UI thread queue to wait for execution. If other tasks have been added to the front of the queue, the animation code must wait until the previous task is completed before executing it.
requestAnimationFrame uses the system time interval to maintain the best drawing efficiency. It will not cause over-drawing and increase overhead because the interval is too short. It will also not cause the animation to be stuck and unsmooth because the interval is too long, allowing various web page animation effects to be achieved. There is a unified refresh mechanism to save system resources, improve system performance and improve visual effects.
FeaturesThe requestAnimationFrame method takes a callback as a parameter, and the callback function will be passed in a parameter, DOMHighResTimeStamp, indicating the time when the callback function currently sorted by requestAnimationFrame() is triggered. The return value is a request ID, which represents a unique identifier in the callback list. This value can be passed to window.cancelAnimationFrame() to cancel the callback function.
requestID = window.requestAnimationFrame(callback);
Using this API, certain codes can be executed during the next re-rendering to avoid triggering a large number of reflows in a short period of time.
For example, the callback function of the page scroll event (scroll) is very suitable for using this API, deferring the callback operation until the next re-rendering. However, it should be noted that requestAnimationFrame does not manage callback functions, that is, calling requestAnimationFrame with the same callback function multiple times before the callback is executed will cause the callback to be executed multiple times in the same frame. The simplest way is to use a throttling function to solve this problem, or you can find a way to have only one same callback function in the queue of requestAnimationFrame:
let scheduledAnimationFrame = false;document.body.onscroll = () => { if (scheduledAnimationFrame) return; scheduledAnimationFrame = true; window.requestAnimationFrame(() => { scheduledAnimationFrame = false; // do something });};
Of course, the best application scenario is still in frame animation, which can greatly optimize performance.
Interview questions How to render tens of thousands of pieces of data without getting stuck in the interfaceThis question examines how to render data without blocking the page. That is to say, you cannot render tens of thousands of items at once, but should render part of the DOM at once. Then you can refresh it every 16 ms through requestAnimationFrame.
<!DOCTYPE html><html lang=en><head> <meta charset=UTF-8> <meta name=viewport content=width=device-width, initial-scale=1.0> <meta http-equiv=X-UA -Compatible content=ie=edge> <title>Document</title></head><body> <ul>Control</ul> <script> setTimeout(() => { // Insert 100,000 pieces of data const total = 100000 // Insert 20 pieces at a time. If you feel the performance is not good, reduce const once = 20 // How many times will it take to render the data? const loopCount = total / once let countOfRender = 0 let ul = document. querySelector(ul); function add() { // Optimize performance, insertion will not cause backflow const fragment = document.createDocumentFragment(); for (let i = 0; i < once; i++) { const li = document.createElement(li); li.innerText = Math.floor(Math.random() * total); fragment.appendChild(li); } ul.appendChild(fragment); countOfRender += 1; loop(); } function loop() { if (countOfRender < loopCount) { window.requestAnimationFrame(add); } } loop(); }, 0); </script></body></html>compatibility
Some old browsers do not support this API. In order to use this API, you can customize this method and mount it under the window:
(function() { var lastTime = 0; var vendors = ['webkit', 'moz']; for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) { window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame']; window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame'] || window[vendors[x]+'CancelRequestAnimationFrame']; } if (!window.requestAnimationFrame) window.requestAnimationFrame = function(callback) { var currTime = new Date().getTime(); var timeToCall = Math.max( 0, 16 - (currTime - lastTime)); var id = window.setTimeout(function() { callback(currTime + timeToCall); }, timeToCall); lastTime = currTime + timeToCall; return id; }; if (!window.cancelAnimationFrame) window.cancelAnimationFrame = function(id) { clearTimeout(id); };}());
The above is the entire content of this article. I hope it will be helpful to everyone’s study. I also hope everyone will support VeVb Wulin Network.