I’ve been researching performance issues for page rendering and web animation, and I’ve been reading CSS SECRET.
This article mainly wants to talk about page optimization scroll optimization.
The main content includes why we need to optimize scroll events, the relationship between scroll and page rendering, throttling and anti-shock, pointer-events: None to optimize scroll. Because this article involves a lot of foundations, it is a process of my own learning record, if the knowledge points listed above are clear in mind, you can not look down.
The origin of rolling optimization
Scroll optimization not only refers to scroll events, but also to events such as resize that are triggered frequently. A quick look:
var i = 0;
window.addEventListener('scroll',function(){
console.log(i++);
},false);Copy the code
The output is as follows:
When binding events like scroll and resize, when it happens, it will be triggered very frequently and very close together. If the event involves a lot of position calculation, DOM manipulation, element redrawing, etc., and this work cannot be completed before the next Scroll event is triggered, the browser will drop frames. In addition, the user’s mouse scrolling is often continuous, so the Scroll event will be triggered continuously, resulting in the expansion of frame drop, the increase of browser CPU usage, and the user experience will be affected.
Binding callback in scroll events is also widely used in lazy loading of images, automatic loading of slide data, floating navigation bar and so on.
Having smooth scrolling is an often overlooked but vital part of the user experience when browsing the web. When scrolling is normal, the user will feel that the application is smooth and pleasant, whereas when scrolling is cumbersome and awkward, the user will feel extremely uncomfortable.
The 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 forehead… This starts with what determines page performance.
I think it’s important to go back to the source of technology. Don’t read an article that says rolling events can lead to Caton and a bunch of solution optimization techniques are taken as a mantra. What we need is not a doctrine but a critique, look at the source.
Starting from the problem, step by step to find the end, it is easy to find the crux of the problem, only in this way, the solution is easy to remember.
Preaching a bunch of nonsense, don’t like the direct ignore ha, back to the topic, to find the entrance to the optimization of the problem is to know where, for page optimization, then we need to know the page rendering principle:
Browser rendering principle I in my last article also want to talk in detail, but more from the perspective of animation rendering to speak: [Web animation] CSS3 3D planet operation && browser rendering principle.
After a moment’s reflection, or to give a simple description, I found that each time I reviewed these knowledge points, I gained something new. This time, I will change the picture, take Chrome as an example, and show a Web page. Simply speaking, I can think of the following steps:
- JavaScript: In general, we use JavaScript for visual changes. Do an animation or add some DOM elements to the page.
- Style: Calculates the Style. This process is based on the CSS selector, matching the corresponding CSS Style for each DOM element. Once you’ve done this, you’ve determined what CSS style rules to apply to each DOM element.
- The previous step defines the style rules for each DOM element by calculating the final size and position of each DOM element on the screen. The layout of elements on a Web page is relative, so a change in the layout of one element triggers a change in the layout of other elements. For example, a change in the width of an element affects the width of its children, and a change in the width of its children continues to affect its grandchildren. So for browsers, the layout process happens all the time.
- Paint: Basically, the process of filling in pixels. This includes drawing text, colors, images, borders, and shadows, which are all visual effects of a DOM element. Typically, this drawing process is done on multiple layers.
- Composite: Render layer merge. As you know from the previous step, the DOM elements in the page are drawn on multiple layers. Once the drawing process is complete on each layer, the browser merges all layers into one layer in a reasonable order and displays them on the screen. This process is especially important for pages with overlapping elements, because if the layers are merged in the wrong order, the elements will display incorrectly.
GraphicsLayer is a layer that is uploaded to the GPU as a texture. However, it is not relevant to this article’s scroll optimization. If you are interested in learning more, please Google more.
In simple terms, when a web page is generated, it will render (Layout+Paint) at least once. As users visit, they are constantly reflow and repaint.
The user’s scroll and resize actions (i.e. sliding the page and resizing the window) cause the page to be constantly rerendered.
As you scroll through the page, the browser may need to draw some pixels in these layers (sometimes called composite layers). With grouping of elements, when the content of a layer changes, we only need to update the structure of that layer and only redraw and rasterize the changed parts of the layer structure without completely redrawing. Obviously, if something like a parallax site is moving as you scroll, it can cause a lot of content tweaking at multiple levels, which can cause a lot of drawing work.
Debouncing and Throttling
The Scroll event itself will trigger the page rendering, and the Scroll event handler will be triggered at a high frequency. Therefore, there should not be complex operations inside the event handler, such as DOM operations, which should not be included in the event processing.
To solve the problem of triggering events with high frequency (such as page scroll, screen resize, and user input monitoring), the following two common solutions are introduced: anti-shake and throttling.
Debouncing
Anti-shake technology is to combine multiple sequential calls into one, that is, within a certain period of time, the number of specified events to be triggered.
In layman’s terms, consider this simplified example:
Function debounce(func, wait, immediate) {var timeout; Return function() {// Each time the Scroll handler is triggered, clear the timer clearTimeout(timeout); Handler timeout = setTimeout(func, wait); // Handler timeout = setTimeout(func, wait); }; }; // Handler function realFunc(){console.log("Success"); } // Use anti-jitter window.addeventListener ('scroll',debounce(realFunc,500)); // Window. addEventListener('scroll',realFunc);Copy the code
If the scroll event does not trigger twice in a row within 500ms, then the function we really want to trigger in the scroll event will be triggered.
The above example can be better encapsulated:
Function 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() {// // bind listener window.addEventListener('resize', myEfficientFn);Copy the code
Throttling
The anti-shake function is good, but there are some problems, such as lazy loading of images. I want images to be loaded continuously during the slide, not only when I stop sliding. The same is true for ajax requests to load data as it slides.
In this case, we want the scrolling handler to be fired at a certain frequency (say 250ms once) even though the page is constantly being scrolled. In this case, another technique is used, called throttling.
Throttling functions allow a function to execute only once in X milliseconds, and only when the interval specified by you has elapsed since the last function execution can the function be called again.
The main difference with the choke function is that it guarantees that the event handler we want to fire will be executed at least once in X milliseconds.
The throttling function has a mustRun attribute that represents a mustRun handler in milliseconds, again using a timer.
Function throttle(func, wait, mustRun) {var timeout, startTime = new Date(); return function() { var context = this, args = arguments, curTime = new Date(); clearTimeout(timeout); // If (curtime-startTime >= mustRun){func. Apply (context,args); startTime = curTime; }else{timeout = setTimeout(func, wait); }}; }; // Handler function realFunc(){console.log("Success"); } // use the throttle function window.addEventListener('scroll',throttle(realFunc,500,1000));Copy the code
If the scroll firing interval is less than 500ms over a period of time, then the event we want to call handler will fire at least once within 1000ms.
Use rAF (requestAnimationFrame) to trigger the scroll event
The jitter and throttling methods described above are implemented with the help of timer setTimeout, but if the page only needs to be compatible with advanced browsers or applications on mobile terminals, or the page needs to pursue high-precision effects, then the browser’s native method rAF (requestAnimationFrame) can be used.
requestAnimationFrame
Window. RequestAnimationFrame () this method is used to before the page re-paint, notify the browser call a specified function. This method takes a function as a parameter that will be called before redrawing.
RAF is often used in web animation production, used to accurately control the page frame refresh rendering, so that the animation effect is more smooth, of course, its role is not limited to animation production, because it is also a timer.
Generally speaking, rAF is invoked at a rate of 60 times per second, i.e. 1000/60, with a trigger frequency of about 16.7ms.
In simple terms, use requestAnimationFrame to trigger the scroll event, equivalent to the above:
Throttle (func, xx, 16.7) //xx means that event handlers will not be fired repeatedly within xx msCopy the code
A simple example is as follows:
var ticking = false; Function onScroll(){if(! ticking) { requestAnimationFrame(realFunc); ticking = true; } } function realFunc(){ // do something... console.log("Success"); ticking = false; } // The scroll event listens for window.addEventListener('scroll', onScroll, false);Copy the code
The simple rAF example above can be taken to the browser for a try. The main function is to keep the event handler firing at 16.7ms during scrolling.
There are pros and cons to using requestAnimationFrame, first we have to consider its compatibility issues, and second because it can only be implemented at 16.7ms, which means it is very poorly tuned. However, rAF may be more effective and perform better for more complex scenarios than throttle(func, XX, 16.7).
To summarize
- Anti-jitter: Anti-jitter technology is to combine multiple sequential calls into one, that is, within a certain period of time, the number of events triggered.
- Throttling functions: only allow a function to execute once in X milliseconds, and only allow the function to be called again after the specified interval has passed.
- RAF: 16.7ms triggers a handler, which reduces controllability but improves performance and accuracy.
Simplify operations in Scroll
The methods introduced above are all about how to optimize the triggering of Scroll events to avoid excessive consumption of resources.
But in essence, we should try to simplify the Scroll event handler, so that some variable initialization, calculation that does not depend on the scroll position change should be prepared in advance outside the Scroll event.
The suggestions are as follows:
To avoid theScroll eventTo modify the style attribute /Strip style operations from scroll events
Input event handlers, such as scroll/Touch event handlers, are called before requestAnimationFrame.
Therefore, if you modify style properties in the Scroll event handler, those operations will be temporarily stored by the browser. Then when you call requestAnimationFrame, if you read the style property in the first place, this will trigger the browser’s forced synchronous layout.
Try using pointer-events: None to disable mouse events while sliding
Most people probably don’t recognize this property, well, what does it do?
Pointer -events is a CSS property that can have multiple different values. Some of the values of the property are only associated with SVG. Here we only focus on pointer-events: None disables mouse actions, such as mouse clicks and hover functions, meaning that the element does not become the target of mouse events.
Go to the developer tools panel near F12 and add the pointer-Events: None style to the TAB. Then take a look at the page and see that all mouse events are disabled.
So what does it do?
Pointer-events: None Is used to increase the frame rate of scrolling. It is true that hovering over some element triggers the hover effect when scrolling, but these effects are usually not noticed by users and most likely cause scrolling problems. Applying pointer-events: None to the body element disables mouse events, including hover, to improve scrolling performance.
.disable-hover {
pointer-events: none;
}Copy the code
To do this, add a.disable-hover style to a scrolling page so that all mouse events are disabled until scrolling stops. When the scrolling is complete, remove the property.
Check out the demo page.
Pointer -events: None can be used to increase the frame rate of a scroll.
Use pointer-events: None for a 60fps scroll.
Is that it? No, Zhang Xinxu has a special article on whether pointer-Events: None really speeds up scrolling performance and raises his own doubts:
Pointer-events: None Improves drawing performance when scrolling?
Conclusions: Pointer-events: None should be decided according to the business itself, refuse to take doctrine, look at the source more, and then make a decision after hands-on practice.
Other references (all good articles and worth reading) :
- Example analysis Debouncing and Throttling
- Wireless performance optimization: Composite
- Javascript high performance animation with page rendering
- Google Developers – Rendering capabilities
- Web High performance Animation
To the end of this article, if there are any questions or suggestions, you can exchange more, original articles, writing is limited, talent is shallow, if there is a wrong place, hope to inform.