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 especificaciones, Chromium 84 ofrece una gran cantidad de funciones que antes no eran compatibles con Chrome y Edge, lo que permite la interoperabilidad entre navegadores.

La API de Web Animations llegó a Chromium por primera vez en la versión 36, julio de 2014. Ahora, la especificación estará completa en la versión 84, a partir de 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, deberás crear un objeto de fotogramas clave. Cómo 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 esto:

Primero, "get" los fotogramas clave de la transición para determinar de dónde estamos haciendo la transición. Luego, creas dos animaciones de opacidad nuevas, lo que habilita el efecto de atenuación cruzada. Una vez que se complete el encadenado, borrarás 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 permiten esperar a que se apliquen los cambios pendientes (es decir, el cambio entre los 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 un cambio de opacidad en un elemento secundario:

Aplicación de transformaciones y opacidad a un elemento modal de apertura. Ver 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 de reproducción pendiente (openModal y una transformación de opacidad intercalada) y, luego, pausar una de las animaciones y retrasarla hasta que la otra finalice. Luego, puedes usar promesas para esperar a que cada una finalice antes de jugar. Por último, puedes comprobar si hay un indicador establecido 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 demostración en 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 de uso de fotogramas clave parciales. El controlador del mouse hace algunas acciones: establece una nueva ubicación de destino y activa una nueva animación. La nueva posición de inicio se infiere de la posición subyacente actual.

Las transiciones nuevas se pueden activar mientras las existentes aún se están ejecutando. Esto significa que se interrumpe la transición actual y se crea una nueva.

Mejoras de rendimiento con animaciones reemplazables

Cuando creas animaciones basadas en eventos, como en 'mousemove', se crea una animación nueva cada vez, lo que puede consumir memoria y disminuir el rendimiento rápidamente. 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 demostración en 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. Ahora, el navegador sabe que debe quitar las animaciones antiguas (lo que habilita el reemplazo) cuando:

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

Para ver exactamente cuántas animaciones se reemplazan, puedes sumar un contador con cada animación quitada y usar anim.onremove para activarlo.

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 animaciones web, ahora puedes configurar el modo compuesto de tus animaciones, lo que significa que pueden ser aditivas o acumulativas, además del modo predeterminado de "reemplazar". Los modos compuestos permiten a los desarrolladores escribir animaciones distintas y tener control sobre cómo se combinan los efectos. Ahora se admiten tres modos compuestos: 'replace' (el modo predeterminado), 'add' y 'accumulate'.

Cuando se combinan animaciones, un desarrollador puede escribir efectos cortos y distintos, 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 demostración en 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 genera rotate(720deg) scale(1.8). Para obtener más información sobre las complejidades de estos modos compuestos, consulta Las enumeraciones CompositeOperation y CompositeOperationOrAuto de la especificación de animaciones web.

Observa el siguiente ejemplo de elemento de la IU:

Un menú desplegable con movimiento que tiene dos animaciones compuestas aplicadas. 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 incorporaciones emocionantes a las capacidades de animación de los navegadores actuales, y aún hay más en camino. Consulta estas especificaciones futuras para obtener más información sobre lo que sucederá a continuación: