Renderizado · 3 min de lectura

Un fotograma, 16,7 milisegundos: el pipeline de renderizado

Qué ocurre entre un cambio de estilo y la siguiente imagen, y por qué transform y opacity casi no cuestan nada.

Una pantalla de 60 Hz muestra una imagen nueva cada 16,7 ms; una de 120 Hz, cada 8,3 ms. Para que una animación o el desplazamiento se vean fluidos, el navegador tiene que hacer en ese tiempo todo lo necesario para la siguiente imagen. Parte del presupuesto lo necesita él mismo; para tu JavaScript quedan, siendo realistas, unos 10 ms a 60 Hz.

Cinco pasos

Simplificando, cada fotograma pasa por estas estaciones:

  1. JavaScript: handlers de eventos, temporizadores, callbacks de requestAnimationFrame; aquí nace la mayoría de los cambios.
  2. Style: el navegador averigua qué reglas CSS se aplican a cada elemento y calcula los valores.
  3. Layout: tamaño y posición de cada elemento. Como los elementos se influyen entre sí, un cambio pequeño puede afectar a gran parte de la página.
  4. Paint: rellenar píxeles: texto, colores, bordes, sombras, imágenes, normalmente repartidos en varias capas.
  5. Composite: superponer las capas en el orden correcto. De eso se encarga un hilo propio, a menudo con ayuda de la GPU.

No todos los cambios necesitan los cinco pasos. Cuáles se saltan depende de la propiedad que cambie:

Según la propiedad que cambie, se saltan pasos. La geometría cuesta los cinco; el color solo se ahorra el layout; transform y opacity no necesitan ni layout ni paint.

Por qué transform y opacity salen tan baratos

transform y opacity no cambian ni la geometría ni los píxeles de un elemento, solo cómo se compone su capa ya terminada. Si el elemento tiene su propia capa, el hilo del compositor puede continuar la animación por su cuenta, incluso cuando el hilo principal está ocupado con una tarea larga.

Los dos cuadrados recorren el mismo camino. El de arriba anima left y necesita layout y paint en cada fotograma; el de abajo anima transform y le basta con el compositor.

Aquí los dos se ven igual porque el hilo principal no tiene nada que hacer. Si JavaScript lo bloquea durante 200 ms, el cuadrado de arriba se queda quieto ese tiempo y el de abajo sigue moviéndose.

/* provoca layout y paint en cada fotograma */
@keyframes slide-left {
  from { left: 0; }
  to   { left: 300px; }
}

/* solo necesita el compositor */
@keyframes slide {
  from { transform: translateX(0); }
  to   { transform: translateX(300px); }
}

Usar capas con intención

Con will-change: transform se le puede avisar al navegador de que un elemento se va a animar; entonces lo pone en una capa propia. No es una solución mágica: cada capa consume memoria gráfica, y cientos de ellas vuelven lento el propio compositing. Lo mejor es ponerlo poco antes de la animación y quitarlo después.

Ahorrarse el trabajo

El paso más barato es el que no ocurre. En páginas largas, content-visibility: auto permite al navegador saltarse Style, Layout y Paint de las zonas fuera de pantalla hasta que se acercan:

.capitulo {
  content-visibility: auto;
  contain-intrinsic-size: auto 800px; /* altura estimada para que la barra de desplazamiento no salte */
}

Las herramientas de desarrollo muestran si funciona: en el panel Performance encogen los bloques morados (Style, Layout) y verdes (Paint), y en Rendering la opción Paint flashing colorea todas las zonas que se vuelven a pintar. Qué pasa cuando JavaScript fuerza el layout en mitad del fotograma lo cuenta Primero leer, luego escribir: evitar el layout thrashing.

← Todas las notas