[Front-end performance] High-Performance rolling scroll and page rendering optimization, front-end scroll
I recently studied the performance of page rendering and web Animation, and read the masterpiece CSS SECRET.
This article mainly talks about page optimization rolling optimization.
The main content includes the reasons for optimizing rolling events, the relationship between scrolling and page rendering, throttling and anti-shake, and pointer-events: none to optimize scrolling. Because this article involves a lot of basics, you can compare the knowledge above and choose to jump to the corresponding place to read.
Origin of rolling Optimization
In fact, rolling optimization not only refers to scroll events, but also includes events that are frequently triggered, such as resize. A simple look:
var i = 0;window.addEventListener('scroll',function(){console.log(i++);},false);
The output is as follows:
When binding events such as scroll and resize, when it occurs, it is triggered very frequently and the interval is very close. If the event involves a lot of location computing, DOM operations, element re-painting, and other work that cannot be completed before the next scroll event is triggered, the browser will drop the frame. In addition, users' mouse scrolling is often continuous, and scroll events will be triggered continuously, resulting in frame dropping enlargement, increased browser CPU usage, and affected user experience.
There are also many application scenarios for binding callback in rolling events, which are widely used in lazy loading of images, automatic loading of falling data, and floating navigation bar on the side.
When a user browses a webpage, smooth scrolling is often ignored but is a crucial part of the user experience. When the rolling performance is normal, the user will feel that the application is very smooth and pleasant. On the contrary, the heavy and unnaturally choppy rolling will bring a great sense of discomfort to the user.
Relationship between scrolling and page rendering
Why do rolling events need to be optimized? Because it affects performance. So what performance does it affect? The question of page performance is determined.
I think that technology must be traced back to the source. I don't want to see an article saying that rolling events will lead to choppy and talking about a bunch of solution optimization skills, what we need is not to take the arguments but to criticize. Let's look at the source.
Starting from the problem and finding the problem step by step, it is easy to find the crux of the problem. Only such a solution can be easily remembered.
I spoke a bunch of nonsense and did not like to ignore it directly. Back to the topic, we need to know where the problem is. For page optimization, we need to know the page rendering principle:
The principle of browser rendering is also described in detail in my previous article, but it is more about animation rendering: [Web Animation] CSS3 3D planetary operation & browser Rendering Principle.
After thinking about it, I would like to briefly describe it. I found that every time I review these knowledge points, I had a new harvest. This time I used a new figure, taking chrome as an example and displaying a Web page, to put it simply, we can think that we have gone through the following steps:
JavaScript: In general, we will use JavaScript to achieve some visual changes. For example, make an animation or add some DOM elements to the page.
Style:Calculate the style. This process matches each DOM element with the corresponding CSS style based on the CSS selector. After this step is completed, the CSS style rules applied to each DOM element are determined.
Layout:Layout. The style rules of each DOM element are determined in the previous step. This step is to calculate the size and position of each DOM element displayed on the screen. The layout of elements on the web page is relative. Therefore, the layout of an element changes, causing the layout of other elements to change. For example, the width change of the <body> element will affect the width of its child element, and the change of its child element width will continue to affect its child element. Therefore, for browsers, the layout process often occurs.
Paint:Painting is essentially the process of filling in pixels. Including drawing text, color, image, border, and shadow, that is, all the visual effects of a DOM element. In general, this painting process is completed on multiple layers.
Composite:The rendering layer is merged. We can see from the previous step that DOM elements in the page are drawn on multiple layers. After the painting process is completed on each layer, the browser combines all layers into one layer in a reasonable order and then displays them on the screen. This process is especially important for pages with overlapping elements, because once the merging order of layers fails, the element display will be abnormal.
The GraphicsLayer concept is involved here. The GraphicsLayer is uploaded to the GPU as a texture (texture). Now we can often see that GPU hardware acceleration is used, it is closely related to the so-called layer concept. But it is not relevant to the rolling Optimization in this article. If you are interested in in-depth understanding, You can google more on your own.
To put it simply, when a webpage is generated, it will be rendered at least once (Layout + Paint. During user access, reflow and repainting will be re-performed ).
Among them, the user scroll and resize behavior (that is, sliding the page and changing the window size) will cause the page to be continuously re-rendered.
When you scroll through a page, the browser may need to draw pixels in these layers (sometimes called compositing layers. Through element grouping, when the content of a layer changes, we only need to update the structure of the layer and only repaint and raster the changed part of the layer structure, instead of completely re-painting. Obviously, if something like a parallax website (stamp me to see) is moving when you scroll, it may cause large-area content adjustment in multiple layers, which will lead to a lot of painting work.
Debouncing and Throttling)
The scroll event will trigger page re-rendering, and the handler of the scroll event will be triggered at a high frequency. Therefore, the handler of the event should not have complicated operations, for example, DOM operations should not be placed in event processing.
To solve such high-frequency trigger events (such as page scroll, screen resize, and user input listening), the following describes two common solutions: Anti-shake and throttling.
Debouncing)
The anti-shake technique combines multiple calls in sequence into one call, that is, the number of times an event is triggered within a certain period of time.
To put it simply, let's take a look at the simplified example below:
// Simple anti-jitter function debounce (func, wait, immediate) {// timer variable var timeout; return function () {// clear the timer clearTimeout (timeout) each time scroll handler is triggered; // specify the xx MS to trigger the operation handlertimeout = setTimeout (func, wait );};}; // handlerfunction realFunc () {console. log ("Success");} // uses the anti-jitter window. addEventListener ('scroll ', debounce (realFunc, 500); // No jitter-proof window is used. addEventListener ('scroll ', realFunc );
The simple anti-shake example above can be used in the browser to try it out. The approximate function is that if two scroll events are not triggered consecutively within 500 ms, then it will trigger the function that we really want to trigger in the scroll event.
The above example can better encapsulate the following:
// Debounce (func, wait, immediate) {var timeout; return function () {var context = this, args = arguments; var later = function () {timeout = null; if (! Immediate) func. apply (context, args) ;}; var callNow = immediate &&! Timeout; clearTimeout (timeout); timeout = setTimeout (later, wait); if (callNow) func. apply (context, args) ;};}; var myEfficientFn = debounce (function () {// The true action in the scroll}, 250); // bind the listener window. addEventListener ('resize', myEfficientFn );
Throttling)
The anti-shake function is really good, but there are also problems. The slice is too lazy to load. I hope that images will be constantly loaded during the decline, instead of when I stop downgrading, the image is loaded. Or the ajax request loading of data during the decline is the same.
At this time, we hope that even if the page is continuously rolled, the rolling handler can be triggered at a certain frequency (for example, once triggered in 250 ms). In such scenarios, another technique called throttling is required ).
The throttling function allows only one function to be executed within X milliseconds. The function can be called next time only after the specified interval is reached after the execution of the functions.
Compared with anti-shake, the main difference between the throttling function is that it guarantees to execute the event handler we want to trigger at least once within X milliseconds.
Compared with anti-shake, the throttling function has a mustRun attribute, which indicates that a handler is triggered within milliseconds of mustRun. The timer is also used to look at a simple example:
// Simple throttling function throttle (func, wait, mustRun) {var timeout, startTime = new Date (); return function () {var context = this, args = arguments, curTime = new Date (); clearTimeout (timeout); // if the specified trigger interval is reached, handlerif (curTime-startTime> = mustRun) {func is triggered. apply (context, args); startTime = curTime; // when the trigger interval is not reached, reset the timer} else {timeout = setTimeout (func, wait );}};}; // handlerfunction realFunc () {console. log ("Success");} // uses the throttling function window. addEventListener ('scroll ', throttle (realFunc ));
The preceding simple example of the throttling function can be used in the browser. The approximate function is that if the scroll trigger interval within a period of time is shorter than 500 ms, this ensures that the handler we want to call will be triggered at least once within 1000 ms.
Use rAF (requestAnimationFrame) to trigger a rolling event
The preceding Implementation of jitter and throttling is based on the timer setTimeout. However, if the page only needs to be compatible with a later browser or application on a mobile terminal, or the page needs to achieve high precision, then you can use the native method rAF (requestAnimationFrame) of the browser ).
RequestAnimationFrame
Window. requestAnimationFrame () is used to notify the browser to call a specified function before page re-painting. This method accepts a function as a parameter, which is called before re-painting.
RAF is often used in the production of web Animation. It is used to accurately control the frame refresh rendering of the page to make the animation effect smoother. Of course, its role is not limited to animation production, we can use its features to regard it as a timer. (Of course it is not a timer)
Generally, rAF is called 60 times per second, that is, 1000/60. The trigger frequency is about 16.7 ms. (When performing complex operations, when it finds that it cannot maintain the 60 FPS frequency, it will reduce the frequency to 30 FPS to maintain the stability of the number of frames .)
In short, using requestAnimationFrame to trigger a rolling event is equivalent to the above:
Throttle (func, xx, 1000/60) // xx indicates that the event handler will not be repeatedly triggered within xx ms
A simple example is as follows:
Var ticking = false; // rAF triggers the lock function onScroll () {if (! Ticking) {requestAnimationFrame (realFunc); ticking = true ;}} function realFunc () {// do something... console. log ("Success"); ticking = false;} // scroll event listening window. addEventListener ('scroll ', onScroll, false );
The preceding simple example of rAF can be used in the browser to give it a try. The general function is to keep the event handler triggered at a frequency of 16.7 ms during the rolling process.
The advantages and disadvantages of using requestAnimationFrame coexist. First, we have to consider its compatibility problem. Second, because it can only be triggered at a frequency of 16.7 ms, it indicates that its controllability is very poor. However, compared with throttle (func, xx, 16.7), rAF may have better results and better performance in more complex scenarios.
To sum up
Anti-jitter: the anti-shake technique combines multiple calls in sequence into one call, that is, the number of times an event is triggered within a certain period of time.
Throttling function: Only one function can be executed once within X milliseconds. The next call to this function can be performed only when the interval after the callback function is executed.
RAF: 16.7 ms triggers handler once, reducing controllability, but improving performance and accuracy.
Simplify operations in scroll
The methods described above are how to optimize the triggering of scroll events to avoid excessive resource consumption of scroll events.
However, in essence, we should try our best to streamline the handler of the scroll event, so that initialization of some variables and calculation that does not depend on the rolling position change should be made out of the scroll event in advance.
The suggestions are as follows:
AvoidScroll eventModifying style attributes/Remove style operations from scroll events
Input event processing functions, such as scroll/touch event processing, will be called and executed before requestAnimationFrame.
Therefore, if you modify the style attributes in the scroll event processing function, these operations will be saved by the browser. Then, when you call requestAnimationFrame, if you read style attributes at the beginning, this will trigger the forced synchronous layout of the browser.
Use pointer-events: none to disable mouse events during sliding.
Most people may not know this attribute. Well, what is it?
Pointer-events is a CSS attribute that can have multiple different values. Some values of this attribute are only associated with SVG. Here we only focus on the situation of pointer-events: none, the general meaning is to prohibit the mouse action. After this attribute is applied, functions such as mouse clicking and hover will become invalid, that is, the element will not become the target of the mouse event.
You can open the developer tool panel at the nearest F12, add the pointer-events: none style to the <body> label, and then feel the effect on the page. All mouse events are forbidden.
So what is its use?
Pointer-events: none can be used to increase the frame rate during rolling. Indeed, when you scroll over a certain element, the hover effect is triggered. However, these effects are usually not noticed by users, and most of them cause problems in scrolling. Apply pointer-events: none to the body element, and disable mouse events, including hover, to improve scroll performance.
.disable-hover { pointer-events: none;}
The general practice is to add the. disable-hover style to the <body> when the page is scrolling. All mouse events will be disabled before the scroll stops. After the scroll ends, remove the property.
You can view this demo page.
The above mentioned pointer-events: none can be used to increase the frame rate during rolling. This section is taken from pointer-events-MDN and has been specially explained in this article:
Use pointer-events: none to implement 60 FPS scrolling.
Is this all done? No. Zhang xinxu wrote a special article to explore whether pointer-events: none can accelerate rolling performance and raise his own question:
Pointer-events: none improves the rendering performance during Page scrolling?
Conclusion When pointer-events: none is used, it should be decided based on the business itself, refuse to take the arguments, go to the source to see more, practice it and decide again.
Other references (both good articles and worth reading ):
- Debouncing and Throttling)
- Wireless performance optimization: Composite
- Javascript high-performance animation and page rendering
- Google Developers -- Rendering Performance
- High-performance Web Animation
At the end of this article, if you have any questions or suggestions, you can have a lot of discussions, original articles, and limited writing skills. If there are any mistakes in this article, please kindly advise.
If this article is helpful to you, click here for recommendations. It is not easy to write articles.