Layers, layers, layers… be careful!

Mariola Moreno González
6 min readDec 17, 2018

--

Cada vez se utiliza más la tecnología html + css + javascript para otros dispositivos que nada tienen que ver con un ordenador / smartphone, tales como descodificadores de vídeo con bajas prestaciones (poco procesador, ram limitada…).

Cuando afrontas proyectos de este tipo el rendimiento es algo crucial y buscas técnicas que aligeren los procesos computacionales todo lo posible: limitar dependencias de librerías, minificar código, cachear todo lo posible, evitar el uso de iteraciones ineficientes, simplificar html todo lo posible... Se puede llegar a conseguir tiempos de carga realmente cortos, “pintados” de página en un abrir y cerrar de ojos y sin embargo la experiencia de usuario deja mucho que desear (animaciones lentas, transiciones dolorosas…). Aquí entra en juego lo que se llama REPAINTS y REFLOWS.

¿Cómo funciona el proceso de renderizado de una página?

  1. Creación del DOM y aplicación de estilos: se hace una petición al servidor y éste devuelve html, en ese momento el browser genera el DOM (sin estilos). Se combina el DOM + CSS y se genera lo que se llama RENDER TREE o CSSOM que sólo contiene elementos que serán visualizados en la página (aun no hay etiquetas <head>, <script>…).

2. Creación del LAYOUT: momento en que el navegador comienza a calcular el espacio necesario y la posición de cada nodo del DOM. Unos elementos influyen en el posicionamiento de otros (REFLOWS). En este punto aún son sólo cajas vectorizadas.

3. Proceso de PAINT y COMPOSITION. Es el proceso de renderizado, o de conversión de vectores a pixeles. Y se organizan los elementos en lo que se conoce como LAYERS o capas. Por defecto en una única capa (como si de una única capa de photoshop se tratase), a no ser que hayamos establecido determinados estilos que fuercen la creación de más “layers”, o que el navegador cree las layares que considere necesarias.

Un vez que todo ha sido pintado se pueden sufrir procesos de repintado y reposicionamiento. Cambios en propiedades como ‘display’, ‘floats’, redimensionar el navegador, insertar nuevos nodos… dará lugar a un recálculo de posiciones y dimensiones de elementos, un “reflow”. Por otro lado, ciertas propiedades css provocarán “repaints” (cambio de colores, cambios de assets, aplicación de sombras…).

¿Por qué tenemos lag en determinadas animaciones?

Hoy en día casi todos los dispositivos tiene una frecuencia de muestreo de unos 60fps (fotogramas por segundo). Muchos de ellos no pasan de los 30fps (pero esto no supondría dejar de ver fluidas ciertas animaciones o transiciones). Si estamos ante un dispositivo de 60fps, nos queda que tenemos 16.6ms para mostrar el pintado de cada fotograba en una animación (1seg/60). Lo más normal es que esa animación/ transición sea un “reflow”, es decir, un reposicionamiento de elementos. Lo que debemos evitar son los “repaints” durante esa animación, porque si se necesitan repaints probablemente tendremos lag en nuestra animación.

www.csstriggers.com. Web que nos enseña qué propiedades css desencadenan qué procesos en función del motor de renderizado del navegador.

Podemos usar las herramientas de desarrollo de chrome para ver qué partes de la web necesitan repintado. Para ello debemos activar “painting flashing” dentro del apartado de “rendering”. Se mostrará en verde aquellas zonas que sufran un repintado.

Además podemos acceder al “Paint profiler”, donde se ve el proceso entero de pintado. Para ello debemos ir a las herramientas de desarrollo y en la ficha “Performance” activar “Enable advanced paint instrumetation(slow)”. Activamos la grabación e interactuamos con la web. Seleccionamos un bloque de pintado y en la parte inferior vamos a la sección “paint profiler”.

En el paint profiler podemos seleccionar un rango para ir viendo cómo va “pintándose la web”.

Os dejo un vídeo para que veáis el proceso:

¿Qué son las “layers”? No, no son <div>

Vale… ya sé que el proceso de renderizado es que el navegador crea un layout, lo convierte en píxeles y se lo muestra al usuario como si todo fuera una única imagen. También sé que esa imagen renderizada puede sufrir cambios, reposicionamientos de elementos y repintados de los mismos. Este último proceso (repaint) es más costoso y encima si se ha de hacer en medio de una animación, probablemente provoque una mala experiencia de usuario. Y entonces, ¿qué puedo hacer? ¿Qué es una layer?

Hay que tener clara una cosa, es que el repintado de elementos afectará a todos los elementos que estén en su misma “layer” (recordemos que por defecto, en el momento de la composición solo es una). Lo que podemos hacer es detectar qué elementos provocarán esos repintados y forzar la creación de una “layer” para ellos de manera que no se debe aplicar a toda la web el repintado. Además, una “layer” hará uso de la memoria de la tarjeta gráfica (GPU). Fundamentalmente tendremos dos mecanismos para forzar la creación de esa capa:

  • Con la propiedad css “will-change” (funciona en Chrome/Opera/Firefox)
.my-css {
will-change: transform;
}

Esta propiedad avisa al navegador de qué propiedades css van a sufrir un cambio y mueve su contenido a una layer para hacer uso de la GPU. Hemos puesto ‘transform’ pero podría ser cualquier propiedad css.

  • Forzando una transformación 3D (hack utilizado fundamentalmente para Safari y navegadores antiguos)
.my-css {
transform: translateZ(0)
}

¿Qué más crea su propia capa?

Hay ocasiones en las que se crearán capas sin que nosotros seamos conscientes de ello. Cada navegador implementa las “layers” a su manera y nada asegura que esta característica permanezca en el futuro. Actualmente cualquiera de lo siguiente genera una “layer” haciendo uso de la GPU del sistema:

  • Transmorfaciones 3D o uso de perspectiva.
  • Etiqueta <video>.
  • <canvas> con 3d/2d context acelerado.
  • Elementos con animaciones en opacidad o en su elemento transform.
  • Elementos que tengan otros elementos descendientes que ya sean una layer.
  • Elementos posicionados encima de otros que han sido generados como layers (posicionamiento z-index), automáticamente se crea como layer independiente.
  • Pluggins como Flash.

Ejemplo de animación con y sin layers

Un simple ejemplo de animación haciendo uso de la GPU al promocionar una “layer”. Para comprobarlo, habrá que ir a la ficha “Layers” de las herramientas de desarrollo de Chrome. Es importante tener en cuanta que las animaciones que conlleven sólo cambios de opacity y transform, no de desencadenarán procesos de “paint”.

  • Animación de propiedad “left” sin hacer uso de GPU. Para comprobar que cada fotograma necesita hacer un repitando necesitarás activar la opción de “paint flashing”. Si quieres, puedes hacer fork de estos códigos de ejemplo de Codepen y activar la vista “Debug mode”. De esta manera podrás activar fácilmente la opción de “painting flashing” en la ficha “Rendering”.
No se usan las “layers”
  • Animación de propiedad “left” , en esta ocasión creando una “layer”. Al llevar la animación a una capa gestionada por la GPU verás que no se realiza el proceso de repintado, aun cuando se está animando una propiedad css que no es ni opacity ni transform.
Usando la propiedad will-change/ TransformZ para crear una layer gestionada por la GPU de la tarjeta gráfica
  • Animación segura usando sólo propiedades que no suponen repintado. Sin uso de Layers.

Demasiadas “layers” empeorarán el rendimiento al sobrecargar la GPU

No podemos crear capas para todo, porque tendremos otro problema de rendimiento dado que hay que gestionar esas capas y eso supone memoria, además de sobrecargar el uso de la GPU. El uso de layers tiene un coste en rendimiento porque el sistema debe hacer una gestión de estas capas y tener demasiadas producirá el efecto contrario al deseado. Cuidado!

--

--