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:
- JavaScript: handlers de eventos, temporizadores, callbacks de
requestAnimationFrame; aquí nace la mayoría de los cambios. - Style: el navegador averigua qué reglas CSS se aplican a cada elemento y calcula los valores.
- 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.
- Paint: rellenar píxeles: texto, colores, bordes, sombras, imágenes, normalmente repartidos en varias capas.
- 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:
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.
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.