I am trying to understand, in practice, how the layout → paint → composite pipeline of Chrome works. During my testing, I got confused about Chrome's behavior in the following situation. (Codepen)
var button = document.querySelector('button');
var red = document.querySelector('.red');
var blue = document.querySelector('.blue');
button.addEventListener('click', function() {
red.classList.toggle('test');
})
.wrapper {
height: 100%;
width: 100%;
background-color: teal;
position: static;
}
.square {
height: 200px;
width: 200px;
position: static;
transition: transform 0.3s ease,
opacity 0.3s ease,
width 0.3s ease,
height 0.3s ease,
box-shadow 0.3s ease;
}
.red {
/* position: absolute; */
background-color: red;
top: 100px;
left: 100px;
/* will-change: transform; */
opacity: 1;
}
.blue {
background-color: blue;
z-index: 3;
}
.test {
/* transform: translate3d(50px, 50px, 0); */
/* opacity: 0; */
width: 60px;
height: 60px;
/* box-shadow: 0px 0px 0px 10px rgba(0,0,0,.5) */
}
button {
position: fixed;
right: 100px;
top: 50px;
z-index: 10000;
font-weight: bold;
background-color: yellow;
padding: 5px 10px;
/* will-change: transform; */
}
<div class="wrapper">
<div class="red square"></div>
<div class="blue square"></div>
</div>
<button>Click</button>
To reproduce.
- Use Chrome;
- turn Highlight repaints on, in the dev tools;
- open the Layers panel;
- click the yellow button to toggle the size of the red square;
- see what is highlighted as "repainted", as well as the current layers;
- try doing it after changing the
position
of the red square too.
We have two squares, a red one and a blue one, inside a wrapper element. In Chrome's Layers panel this whole thing shows as one layer.
If both squares are
static
positioned, when I transition the width and height of the red one, I can see that the whole layer gets repainted, which, to me, makes sense, since, if all 3 elements are in the same layer, changing the dimensions of one, changes the whole layer's end result, so the whole layer has to be repainted.If I set the red square to
absolute
positioning (you can do that by uncommenting the line in the.red
style rules), when I transition its width and height, even though the dev tools still show one layer, only the red square, inside that layer, is shown as repainted.
Question.
The second scenario does not make sense to me.
If the two squares and the wrapper element are all in the same layer, shouldn't changing one element affect the whole layer and cause the layer as a whole to repaint, instead of just the red square?
Additional questions.
Does Chrome, during the layout phase (or whatever phase it is that determines the layers) separate some elements into their own layers (due to position
properties for example)? Is that why it is able to repaint them separately? Does it, after the compositing phase, dump them, so the developer only sees one layer in the dev tools?
Related background.
My rough understanding of the painting process of modern browsers is as follows:
- During the layout phase, the browser combines CSS with HTML information in order to figure out positions and dimensions of visual elements on screen. I also believe this is the time where it determines how many render layers are there (due to
will-change
for example). - During the paint phase, the browser makes a framebuffer for each layer, containing information about what color each pixel should display, based on the layout data.
- During the compositing phase, the GPU composites all the layers together, based on the data generated during the paint phase, and sends the final result to the screen.