Cómo mejorar el rendimiento de las animaciones con CSS

13 de noviembre de 2013

Los navegadores web modernos pueden aplicar con facilidad los siguientes cuatro efectos CSS: cambio de posición, escalado, rotación y opacidad. Si aplicas cualquier otro efecto CSS en la página, es muy posible que el rendimiento se resienta y no puedas mantener los 60 frames por segundo que hacen que la página se vea tan bien.

Por este motivo, todos los efectos gráficos de tus páginas se deberían reducir a las siguientes cuatro instrucciones:

/* mover un elemento */
... { transform: translate(0px, 0px); }

/* escalar (cambiar el tamaño) de un elemento */
... { transform: scale(0); }

/* rotar un elemento */
... { transform: rotate(0deg); }

/* cambiar la opacidad de un elemento */
... { opacity: 0...1; }

Observa el siguiente vídeo que muestra a cámara lenta la diferencia entre animar un objeto con la propiedad transform (parte derecha del vídeo) o hacerlo con las propiedades tradicionales position, top y left (parte izquierda del vídeo):

Del DOM al píxel a través de las DevTools

Cuando analizas una página con las DevTools o Herramientas del desarrollador de Google Chrome, deberías ver un patrón similar al siguiente:

Análisis del rendimiento mediante las DevTools de Google Chrome

El proceso que sigue tu navegador es muy sencillo:

  1. Determina qué estilos se aplican a cada elemento (esta es la fase "Recalculate Style")
  2. Calcula la geometría y posición de cada elemento (fase Layout)
  3. Pinta los píxeles de cada elemento en diferentes capas (fase Paint Setup and Paint)
  4. Dibuja las capas en la pantalla (fase Composite Layers).

Para hacer que las animaciones se vean fluidas en el navegador, la clave está en evitar la máxima cantidad de trabajo. La mejor manera de conseguirlo consiste en utilizar solamente las propiedades transform y opacity. El motivo es que cuanto más arriba empieces en la línea del tiempo mostrada anteriormente, más trabajo tiene que realizar el navegador para mostrar los píxeles en pantalla.

Este consejo es aplicable a casi todos los navegadores, ya que Chrome, Firefox, Safari y Opera aceleran las propiedades transform y opacity mediante el hardware. Lamentablemente, no está claro el criterio que sigue Internet Explorer 10, así que tendremos que esperar hasta el lanzamiento de Internet Explorer 11.

Animando elementos con propiedades que afectan a su layout

Cuando modificas algún elemento de la página, es probable que el navegador tenga que crear un nuevo layout, es decir, que tenga que recalcular el tamaño y la posición de todos los elementos afectados por el cambio. Así que aunque sólo cambies un elemento, es posible que también se recalcule la geometría de muchos otros elementos.

Si modificas por ejemplo la anchura del elemento <html>, todos los elementos de la página se ven afectados. Además, como es normal que un elemento afecte a muchos otros, cuando modificas un elemento de la página es muy común que llegue a afectar hasta al elemento <html>.

Cuanto más grande sea el árbol DOM de elementos visibles en la página, más tiempo le cuesta al navegador realizar todas esas operaciones relacionadas con el layout. Por eso es tan importante evitar cualquier efecto gráfico que obligue a recalcular el layout de los elementos.

Por otra parte, utilizar las clases CSS de los elementos para controlar el estado de la aplicación también puede afectar al rendimiento. El motivo es que cuando modificas las clases CSS de esos elementos, es posible que el navegador tenga que recalcular el layout de todos ellos. ¡Así que el rendimiento de tu aplicación se puede resentir seriamente incluso cuando no utilizas animaciones!

La siguiente tabla muestra las propiedades CSS más populares que provocan un recálculo del layout al modificar sus valores:

Propiedades CSS que afectan al layout de los elementos
border border-width bottom clear display float
font-family font-size font-weight height left line-height
margin min-height overflow overflow-y padding position
right text-align top vertical-align white-space width

Animando elementos con propiedades que afectan al pintado

Al modificar un elemento, también es posible que se tenga que realizar una fase de pintado de píxeles. El problema es que incluso en los navegadores más modernos, el pintado se realiza mediante software. Dependiendo de cómo se agrupen los elementos de la página en capas, es posible que el pintado también afecte a otros elementos adyacentes. Si no conoces el concepto de capas o layers, puedes leer la introducción que realizó Tom Wiltzius sobre el tema.

Muchas propiedades CSS obligan a repintar los elementos, pero estas son las más populares:

Propiedades CSS que afectan al pintado de los elementos
background background-image background-position background-repeat background-size
border-style border-radius box-shadow color outline
outline-color outline-style outline-width text-decoration visibility

Si modificas el valor de cualquiera de estas propiedades, se vuelve a pintar el elemento afectado y todas las capas relacionadas con ese elemento. Este problema es especialmente importante en los dispositivos móviles, ya que sus CPU suelen ser menos potentes que las de los equipos de escritorio.

Animando elementos con propiedades que afectan a su composición

Una de las propiedades CSS que parece que debería provocar el repintado de los elementos pero que normalmente no lo hace es la propiedad opacity. En realidad, los cambios en la opacidad de un elemento los gestiona directamente la GPU modificando su transparencia. Ten en cuenta que esto solamente se aplica cuando ese elemento es lo único que contiene la capa.

En los navegadores basados en los motores Blink y WebKit, se crea una nueva capa para cada elemento que tiene una transición CSS o un cambio en su opacidad. Por eso algunos programadores aplican las propiedades translateZ(0) o translate3d(0,0,0) como truco para forzar la creación de una nueva capa para ese elemento. Forzar la creación de capas asegura que la capa estará lista tan pronto como empiece la animación y que sus contenidos no se verán afectados por los cambios en el antialiasing. A pesar de sus ventajas, es recomendable no utilizar en exceso el truco de forzar la creación de las capas.

Al modificar el valor de la propiedad transform sobre un elemento, se modifica su posición, su rotación o su escala. Normalmente la posición del elemento se modifica con las propiedades left y top. El problema de esta técnica, tal y como se mostró anteriormente, es que penaliza el rendimiento de la página. Para mover un elemento, lo mejor es usar el valor translate de la propiedad transform, ya que no obliga a recalcular el layout de los elementos de la página.

Animaciones imperativas frente a animaciones declarativas

Los diseñadores a menudo tienen que decidir entre animar los elementos de la página con JavaScript (se denomina, estilo imperativo) o con CSS (estilo declarativo). Cada uno de los dos métodos tiene sus ventajas e inconvenientes.

Estilo imperativo

Paradójicamente, la principal ventaja de JavaScript es a la vez su mayor desventaja: todo el código JavaScript se ejecuta en el principal hilo de ejecución del navegador. El problema es que este hilo de ejecución ya está ocupado con muchas otras tareas relacionadas con JavaScript y con el cálculo de los estilos, por lo que es común que se produzcan atascos y por tanto, que las animaciones de la página no sean fluidas.

Las animaciones con JavaScript permiten un control absoluto sobre el comienzo de la animación, su finalización, cancelación, pausa, etc. Además, algunos efectos avanzados como el scroll parallax solamente son posibles con JavaScript.

Estilo declarativo

La alternativa a JavaScript consiste en crear las animaciones y las transiciones directamente con CSS. La principal ventaja de este método es que el navegador puede optimizar la animación. Entre otras optimizaciones, el navegador crea todas las capas que sean necesarias y realiza los cálculos fuera del hilo de ejecución principal. La desventaja de las animaciones CSS es que no son tan expresivas como las creadas con JavaScript. Además, no resulta fácil combinar dos o más animaciones, por lo que suelen producirse errores al crear animaciones muy complejas.

Un vistazo al futuro

Como los estándares web evolucionan tan rápidamente, es seguro que pronto desaparecerán todas estas limitaciones con las animaciones y los efectos gráficos. De hecho, el programador Ian Vollick, de la empresa Google, ha realizado una propuesta para que las animaciones JavaScript básicas (las que no necesitan recalcular layouts) se ejecuten mediante workers, en vez de mediante el hijo de ejecución principal.

Por último, se ha creado la especificación Web Animations para aquellos diseñadores que prefieren el estilo declarativo. El diseñador Jake Archibald ha escrito algún artículo sobre el tema.

En resumen

Saber animar correctamente los elementos de una página es esencial para crear una buena experiencia de usuario. Siempre que sea posible, evita el uso de propiedades que obligan a recalcular el layout de los elementos o a repintarlos. Resulta mucho mejor animar los elementos mediante transformaciones CSS en vez de mediante código JavaScript, ya que así el navegador puede optimizar esas animaciones.

Hoy en día, la propiedad transform es la mejor forma de crear animaciones y efectos gráficos, ya que la GPU ayuda en todo el procesamiento gráfico. Si es posible, limita los efectos gráficos de tu sitio o aplicación web a opacity, translate, rotate y scale.

En un futuro no muy lejano, es posible que se creen nuevas formas de animar elementos que sean tan expresivas como JavaScript (pero sin penalizar tanto el rendimiento) y tan optimizadas como CSS (pero sin sus restricciones). Hasta ese momento, optimiza al máximo las animaciones para proporcionar una experiencia de usuario excelente.

 Recursos útiles


Este tutorial es una traducción del artículo High Performance Animations realizada de acuerdo a la licencia del contenido original.