Layers, layers, layers… Be careful!
Front-end technologies (html + css + javascript) are increasingly used by different devices, such as video decoders with low performance (slow processor, limited ram…).
When facing this kind of projects performance is crucial and there’s a need to look for techniques that lighten the computational processes as much as possible: limit dependencies of libraries, minify code, cache whatever possible, avoid the use of inefficient iterations, simplify HTML as much as possible … All these techniques will achieve really short loading times and render pages in a blink of an eye, and yet the user experience may leave much to be desired (slow animations, painful transitions …). Here comes into play what is called REPAINTS and REFLOWS.
How browser rendering works?
- DOM creation and style application: a request is made to the server and it returns html, at that moment the browser generates the DOM (without styles). The DOM + CSS are combined and generates what is called RENDER TREE or CSSOM that only contains elements that will be displayed on the page (there aren’t <head>, <script> … tags).
2. LAYOUT creation: the browser begins to calculate the necessary space and the position of each node of the DOM. Some elements influence the positioning of others (REFLOWS). At this point the elements are vector boxes.
3. PAINT and COMPOSITING process. This is the rendering process, the browser takes these vector boxes and rasterizes them (exchanges vectors to pixels) in a “Paint” step. The rasterized elements are put on “layers” (by default only one layer, unless there is a reason to move them away — similar to a photoshop layer). The layers are placed together and finally shown on the screen (COMPOSITING).
Once everything has been painted, when the user interacts, some changes are introduced to the interface (e.g. scrolling, triggering an animation), and the browser needs to create a series of frames to represent these changes. We are talking about repaints and reflows processes. Changes in properties such as ‘display’, ‘floats’, resize the browser, insert new nodes… will result in a recalculation of positions and dimensions of elements, a “reflow”. On the other hand, certain css properties will cause “repaints” (change of colors, changes of assets, application of shadows …).
Why do we have lags in certain animations?
Nowadays, almost every device has a sampling frequency of 60fps (frames per second). Many of them don’t exceed 30fps (but this doesn’t mean to stop watching the animations in a smooth way). In a 60fps device, we have 16.6ms to show each frame in an animation (1sec / 60). Normally the animation triggers a repositioning of elements (a reflow). We should avoid the repaints during the animation because they will probably add lag to it.
https://csstriggers.com Cheatsheet where you can check which CSS properties trigger layout, paint or composite processes
We can use chrome development tools to check those areas that have been repainted. Just enable “Painting Flashing” option at “Rendering” tab and tit will display green boxes on the repainted areas.
We can also access the “Paint profiler” to see the painting process. Go to the development tools and in the “Performance” tab activate “Enable advanced paint instrumetation (slow)”. Then turn on the recording and interact with the web. To check the results select a painting block and go to the “paint profiler” section
In the paint profiler we can select a range to see how the web is being painted.
What are layers? No, they are not <div>
So… We already know that the rendering process is when the browser creates a layout, converts it into pixels and shows it to the user as if everything were a single image. We also know that this rendered image can change, repositioning its elements and repainting them. Repaint is a more expensive process and if it triggers in the middle of an animation it’ll probably cause a bad user experience. And then, what should we do? What is a layer?
There is one thing that must be clear. The repainting process will affect all elements that are in the same layer (remember that by default, at the compositing time, there is only one layer). We can find what elements will cause these repaints and force the creation of a layer for them, and, this way, the repainting should not be applied to the whole page. In addition, this process will use the GPU.
Fundamentally, we will have two mechanisms to force the creation of that layer:
- With the css property “will-change” (works in Chrome/Opera/Firefox)
.my-css {
will-change: transform;
}
This property hints to browsers how an element is expected to change. Browsers that support this property may set up optimizations before an element is actually changed by creating a GPU layer. In the example we have used ‘transform’, but you can use whatever you need.
- Using a “3D transformation” (hack for Safari and older browsers)
.my-css {
transform: translateZ(0)
}
What else gets its own layer?
Sometimes, layers will be created without being aware of it. Each browser implements the layers in its own way and nothing ensures that this feature remains in the future. Currently any of the following trigger layer creation using the system’s GPU
- 3D or perspective transform CSS properties
- <video> elements using accelerated video decoding
- <canvas> elements with a 3D (WebGL) context or accelerated 2D context
- Composited plugins (i.e. Flash)
- Elements with CSS animation for their opacity or using an animated transform
- Elements with accelerated CSS filters
- Element has a descendant that has a compositing layer (in other words if the element has a child element that’s in its own layer)
- Element has a sibling with a lower z-index which has a compositing layer (in other words the it’s rendered on top of a composited layer)
Animation examples
A simple animation making use of the GPU by promoting a layer. To check it, you must go to the Layers tab in the Chrome development tools. It is important to take into account that animations that only involve changes of opacity and transform will not trigger “repaint” processes.
- “Left” css property animation without promoting a layer. To verify that each frame needs to be repainted, you should activate the “paint flashing” option. If you want, you can fork this Codepen and activate the “Debug mode” view. This way you can easily activate the “painting flashing” option in the “Rendering” tab.
- “Left” css property animation by creating a “layer”. Taking the animation to a layer managed by the GPU you will see that the repaint process is no needed.
- Safe animation, using only “opacity” and “transform”. No “layers” needed.
Using too many “layers” will not improve performance
We shouldn’t create layers for everything, because we will create another performance problem since we have to manage those layers and that means more memory consumption and GPU overloading. Having too many layers will produce the opposite effect that we want…be careful!