Dimitris Karagiannis
Dimitris Karagiannis

Reputation: 9366

Why does Chrome not need to repaint the entire layer on a change?

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.

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.

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:

Upvotes: 18

Views: 2552

Answers (1)

Dimitris Karagiannis
Dimitris Karagiannis

Reputation: 9366

So, the confusing bit here was the fact that the Paint flashing of the dev tools, only flashes the part of the layer that gets invalidated from the previous frame (so if one absolute positioned square starts getting smaller, the invalidated area between frames is an area with the dimensions and coordinates that the square had in the previous frame)

However, internally the whole layer gets repainted, no matter how big or small the invalidated parts are between frames.

So, for example, a blinking cursor will look small on Paint Flashing, but in reality the entire layer needs to be repainted.

Indeed, if we open up the Performance panel and enable the Advanced Painting Instrumentation option, we can see that between the square transitions the whole layer gets painted in both scenarios described in the question.

Chrome Paint analysis 1

Chrome Paint analysis 2

Sources

https://twitter.com/paul_irish/status/971196975013027840
https://twitter.com/paul_irish/status/971196996924030977
https://twitter.com/paul_irish/status/971197842713800704

Some observations

If we were to minimise the Layout and Painting steps to make as few operations as possible we should separate the red square and the yellow button to their own render layers.

This way interacting with the button and resizing the square will only affect their respective layers and will not cause a repaint to the background layer (which includes the background and the blue square).

Upvotes: 17

Related Questions