Mejoras de la API de Web Animations en Chromium 84

Organización de animaciones con promesas, mejoras de rendimiento con animaciones reemplazables, animaciones más fluidas con modos compuestos y mucho más.

Kevin Ellis
Kevin Ellis

Fecha de publicación: 27 de mayo de 2020

Cuando se usan correctamente, las animaciones mejoran la percepción y la memoria de tu marca, guían las acciones de los usuarios y los ayudan a navegar por tu aplicación, lo que proporciona contexto en un espacio digital.

La API de animaciones web es una herramienta que permite a los desarrolladores escribir animaciones imperativas con JavaScript. Se escribió para respaldar las implementaciones de animaciones y transiciones de CSS y permitir que se desarrollen efectos futuros, así como que se compongan y se programen los efectos existentes.

Si bien Firefox y Safari ya implementaron el conjunto completo de funciones de las especificaciones, Chromium 84 agrega una gran cantidad de funciones que antes no se admitían en Chrome y Edge, lo que permite la interoperabilidad entre navegadores.

La API de Web Animations llegó por primera vez a Chromium en la versión 36, en julio de 2014. Ahora, la especificación se completará en la versión 84, que se lanzará en julio de 2020.
La larga historia de la API de Web Animations en Chromium.

Cómo comenzar

Crear una animación con la API de Web Animations debería resultarte muy familiar si usaste reglas @keyframe. Primero, debes crear un objeto de fotograma clave. Podría verse así en CSS:

@keyframes openAnimation {
  0% {
    transform: scale(0);
  }
  100% {
    transform: scale(1);
  }
}

se vería así en JavaScript:

const openAnimation = [
  { transform: 'scale(0)' },
  { transform: 'scale(1)' },
];

Dónde estableces los parámetros para la animación en CSS:

.modal {
  animation: openAnimation 1s 1 ease-in;
}

que configurarías en JS:

document.querySelector('.modal').animate(
    openAnimation, {
      duration: 1000, // 1s
      iterations: 1, // single iteration
      easing: 'ease-in' // easing function
    }
);

La cantidad de código es aproximadamente la misma, pero con JavaScript, obtienes algunas superpoderes que no tienes solo con CSS. Esto incluye la capacidad de secuenciar efectos y un mayor control de sus estados de reproducción.

Más allá de element.animate()

Sin embargo, con la actualización, la API de Web Animations ya no se limita a las animaciones creadas con element.animate(). También podemos manipular las animaciones y transiciones de CSS.

getAnimations() es un método que muestra todas las animaciones de un elemento, independientemente de si se creó con element.animate() o con reglas de CSS (animación o transición de CSS). Este es un ejemplo de cómo se ve:

Primero, debes "get" los fotogramas clave de la transición para determinar el punto desde el que se realizará. Luego, creas dos animaciones de opacidad nuevas, lo que habilita el efecto de atenuación cruzada. Una vez que se complete la transición, borra la copia.

Cómo organizar animaciones con promesas

En Chromium 84, ahora tienes dos métodos que se pueden usar con promesas: animation.ready y animation.finished.

  • animation.ready te permite esperar a que se apliquen los cambios pendientes (es decir, cambiar entre métodos de control de reproducción, como reproducir y pausar).
  • animation.finished proporciona un medio para ejecutar código JavaScript personalizado cuando se completa una animación.

Continuando con nuestro ejemplo, crea una cadena de animación orquestada con animation.finished. Aquí, tienes una transformación vertical (scaleY), seguida de una transformación horizontal (scaleX) y, luego, un cambio de opacidad en un elemento secundario:

Aplicación de transformaciones y opacidad a un elemento modal de apertura. Ver la demostración en CodePen
const transformAnimation = modal.animate(openModal, openModalSettings);
transformAnimation.finished.then(() => { text.animate(fadeIn, fadeInSettings)});

Encadenamos estas animaciones con animation.finished.then() antes de ejecutar el siguiente conjunto de animaciones en la cadena. De esta manera, las animaciones aparecen en orden y hasta aplicas efectos a diferentes elementos de destino con diferentes opciones configuradas (como velocidad y facilidad).

En CSS, sería engorroso volver a crear esto, en especial cuando se aplican animaciones únicas, pero secuenciadas, a varios elementos. Deberás usar un @keyframe, ordenar los porcentajes de tiempo correctos para colocar las animaciones y usar animation-delay antes de activar las animaciones en la secuencia.

Ejemplo: Reproducir, pausar y revertir

Lo que se puede abrir, debería cerrarse. Por suerte, desde Chromium 39, la API de Web Animations nos permite reproducir, pausar y revertir nuestras animaciones.

Puedes tomar la animación que se mostró anteriormente y darle una animación suave y revertida cuando vuelvas a hacer clic en el botón con .reverse(). De esta manera, puedes crear una interacción más fluida y contextual para nuestro elemento modal.

Un ejemplo de una ventana modal que se abre y se cierra cuando se hace clic en un botón. Ver demostración en Glitch

Lo que puedes hacer es crear dos animaciones pendientes de reproducción (openModal y una transformación de opacidad intercalada) y, luego, pausar una de las animaciones y retrasarla hasta que termine la otra. Luego, puedes usar promesas para esperar a que cada una termine antes de reproducir. Por último, puedes verificar si se configuró una marca y, luego, invertir cada animación.

Ejemplo: Interacciones dinámicas con fotogramas clave parciales

Ejemplo de reorientación, en el que un clic del mouse ajusta la animación a una ubicación nueva. Ver la demostración de Glitch
selector.animate([{transform: `translate(${x}px, ${y}px)`}],
    {duration: 1000, fill: 'forwards'});

En este ejemplo, solo hay un fotograma clave y no se especifica una posición de inicio. Este es un ejemplo del uso de fotogramas clave parciales. El controlador del mouse realiza algunas acciones aquí: establece una nueva ubicación final y activa una nueva animación. La nueva posición de inicio se infiere de la posición subyacente actual.

Se pueden activar transiciones nuevas mientras se ejecutan las existentes. Esto significa que se interrumpe la transición actual y se crea una nueva.

Mejoras en el rendimiento con animaciones reemplazables

Cuando se crean animaciones basadas en eventos, como en 'mousemove', se crea una animación nueva cada vez, lo que puede consumir memoria rápidamente y degradar el rendimiento. Para abordar este problema, se introdujeron animaciones reemplazables en Chromium 83, lo que permite una limpieza automatizada, en la que las animaciones terminadas se marcan como reemplazables y se quitan automáticamente si se reemplazan por otra animación terminada. Consulta el siguiente ejemplo:

Se anima una estela de cometa cuando se mueve el mouse. Ver la demostración de Glitch
elem.addEventListener('mousemove', evt => {
  rectangle.animate(
    { transform: translate(${evt.clientX}px, ${evt.clientY}px) },
    { duration: 500, fill: 'forwards' }
  );
});

Cada vez que se mueve el mouse, el navegador vuelve a calcular la posición de cada bola en la estela del cometa y crea una animación para este nuevo punto. El navegador ahora sabe quitar las animaciones anteriores (lo que habilita el reemplazo) en los siguientes casos:

  1. La animación terminó.
  2. Hay una o más animaciones más altas en el orden compuesto que también están terminadas.
  3. Las animaciones nuevas animan las mismas propiedades.

Puedes ver exactamente cuántas animaciones se reemplazan si calculas un contador con cada animación que se quitó y usa anim.onremove para activar el contador.

Existen algunos métodos adicionales para llevar tu control de animación aún más lejos:

  • animation.replaceState() proporciona un medio para hacer un seguimiento de si una animación está activa, persiste o se quita.
  • animation.commitStyles() actualiza el estilo de un elemento según el estilo subyacente junto con todas las animaciones del elemento en el orden compuesto.
  • animation.persist() marca una animación como no reemplazable.

Animaciones más fluidas con modos compuestos

Con la API de Web Animations, ahora puedes establecer el modo compuesto de tus animaciones, lo que significa que pueden ser acumulativas o aditivos, además del modo predeterminado de "reemplazo". Los modos compuestos permiten a los desarrolladores escribir animaciones distintas y controlar cómo se combinan los efectos. Ahora se admiten tres modos compuestos: 'replace' (el modo predeterminado), 'add' y 'accumulate'.

Cuando creas animaciones, un desarrollador puede escribir efectos breves y diferentes, y verlos combinados. En el siguiente ejemplo, aplicamos una clave de fotogramas de rotación y escala a cada cuadro, y el único ajuste es el modo compuesto, que se agrega como una opción:

Una demostración que muestra los modos compuestos predeterminado, agregado y acumulado. Ver la demostración de Glitch

En el modo compuesto 'replace' predeterminado, la animación final reemplaza la propiedad de transformación y termina en rotate(360deg) scale(1.4). Para 'add', la composición agrega la rotación y multiplica la escala, lo que da como resultado un estado final de rotate(720deg) scale(1.96). 'accumulate' combina las transformaciones, lo que da como resultado rotate(720deg) scale(1.8). Para obtener más información sobre las particularidades de estos modos compuestos, consulta las enumeraciones CompositeOperation y CompositeOperationOrAuto de las especificaciones Web Animations.

Observa el siguiente ejemplo de elemento de la IU:

Un menú desplegable desplegable al que se aplican dos animaciones compuestas. Ver demostración en Glitch

Aquí, se combinan dos animaciones top. La primera es una macroanimación, que mueve el menú desplegable por toda la altura del menú como un efecto de deslizamiento desde la parte superior de la página, y la segunda, una microanimación, aplica un pequeño rebote cuando llega a la parte inferior. El uso del modo compuesto 'add' permite una transición más fluida.

const dropDown = menu.animate(
    [
      { top: `${-menuHeight}px`, easing: 'ease-in' },
      { top: 0 }
    ], { duration: 300, fill: 'forwards' });

  dropDown.finished.then(() => {
    const bounce = menu.animate(
      [
        { top: '0px', easing: 'ease-in' },
        { top: '10px', easing: 'ease-out' },
        { ... }
      ], { duration: 300, composite: 'add' });
  });

¿Qué sigue para la API de Web Animations?

Todas estas son adiciones emocionantes a las capacidades de animaciones en los navegadores actuales, y se agregarán más funciones en el futuro. Consulta estas especificaciones futuras para obtener más información sobre lo que sucederá a continuación: