Cuatro funciones nuevas de CSS para animaciones de entrada y salida fluidas

El movimiento es una parte fundamental de cualquier experiencia digital, ya que guía al usuario de una interacción a la siguiente. Sin embargo, hay algunas deficiencias en las animaciones fluidas en la plataforma web. Estas incluyen la capacidad de animar fácilmente animaciones de entrada y salida, y animar sin problemas hacia y desde la capa superior para elementos descartables, como diálogos y ventanas emergentes.

Para cubrir estas brechas, Chrome 116 y 117 incluyen cuatro nuevas funciones de la plataforma web, que permiten animaciones y transiciones fluidas para propiedades discretas.

Estas cuatro funciones nuevas incluyen las siguientes:

  • La capacidad de animar display y content-visibility en una línea de tiempo de fotogramas clave (a partir de Chrome 116)
  • La propiedad transition-behavior con la palabra clave allow-discrete para habilitar transiciones de propiedades discretas, como display (a partir de Chrome 117).
  • La regla @starting-style para animar los efectos de entrada desde display: none y hacia la capa superior (a partir de Chrome 117).
  • La propiedad overlay para controlar el comportamiento de la capa superior durante una animación (a partir de Chrome 117).

Mostrar animaciones en fotogramas clave

A partir de Chrome 116, puedes usar display y content-visibility en las reglas de fotogramas clave. Luego, se intercambiarán en el momento en que se produzca el fotograma clave. No se requieren valores nuevos adicionales para admitir esto:

.card {
  animation: fade-out 0.5s forwards;
}

@keyframes fade-out {
  100% {
    opacity: 0;
    display: none;
  }
}

En el ejemplo anterior, se anima la opacidad a 0 durante la duración de 0.5 s y, luego, se establece la visualización en ninguna. Además, la palabra clave forwards garantiza que la animación permanezca en su estado final, de modo que el elemento al que se aplica permanezca display: none y opacity: 0.

Este es un ejemplo sencillo que imita lo que puedes hacer con una transición (consulta la demostración en la sección de transición). Sin embargo, las transiciones no pueden crear animaciones más complejas, como el siguiente ejemplo:

.card {
  animation: spin-and-delete 1s ease-in forwards;
}

@keyframes spin-and-delete {
  0% {
    transform: rotateY(0);
    filter: hue-rotate(0);
  }
  80% {
    transform: rotateY(360deg);
    filter: hue-rotate(180deg);
    opacity: 1;
  }
  100% {
    opacity: 0;
    display: none;
  }
}

La animación spin-and-delete es una animación de salida. Primero, la tarjeta girará en el eje y, luego, pasará por una rotación de tono y, a continuación, en 80% a través de la línea de tiempo, cambiará su opacidad de 1 a 0. Por último, la tarjeta cambia de display: block a display: none.

Para estas animaciones de salida, en lugar de aplicarlas directamente a un elemento, puedes configurar un activador para las animaciones. Por ejemplo, adjuntando un objeto de escucha de eventos a un botón que active una clase para aplicar la animación, de la siguiente manera:

.spin-out {
   animation: spin-and-delete 1s ease-in forwards;
}
document.querySelector('.delete-btn').addEventListener('click', () => {
 document.querySelector('.card').classList.add('spin-out');
})

El ejemplo anterior ahora tiene un estado final de display:none. Hay muchos casos en los que querrás ir más allá y quitar el nodo del DOM con un tiempo de espera para permitir que la animación finalice primero.

Cómo realizar transiciones de animaciones discretas

A diferencia de lo que ocurre cuando se animan propiedades discretas con fotogramas clave, para realizar la transición de estas propiedades deberás usar el modo de comportamiento de transición allow-discrete.

La propiedad transition-behavior

El modo allow-discrete es lo que posibilita las transiciones discretas y es un valor de la propiedad transition-behavior. transition-behavior acepta dos valores: normal y allow-discrete.

.card {
  transition: opacity 0.25s, display 0.25s;
  transition-behavior: allow-discrete; /* Note: be sure to write this after the shorthand */
}

.card.fade-out {
  opacity: 0;
  display: none;
}
Nota: Esta demostración de transición muestra una técnica diferente a la primera demostración de animación, pero se ve similar.

El atajo transition también establece este valor, por lo que puedes omitir la propiedad y usar la palabra clave allow-discrete al final del atajo transition para cada transición.

.card {
  transition: opacity 0.5s, display 0.5s allow-discrete;
}

.card.fade-out {
  opacity: 0;
  display: none;
}

Si animas varias propiedades discretas, deberás incluir allow-discrete después de cada propiedad que quieras animar. Por ejemplo:

.card {
  transition: opacity 0.5s, display 0.5s allow-discrete, overlay 0.5s allow-discrete;
}

.card.fade-out {
  opacity: 0;
  display: none;
}

La regla @starting-style para animaciones de entrada

Hasta ahora, en este artículo, se abordaron las animaciones de salida. Para crear animaciones de entrada, debes usar la regla @starting-style.

Usa @starting-style para aplicar un estilo que el navegador pueda buscar antes de que el elemento esté abierto en la página. Este es el estado "antes de abrir" (desde el que animas).

/*  0. IS-OPEN STATE   */
/*  The state at which the element is open + transition logic */
.item {
  height: 3rem;
  display: grid;
  overflow: hidden;
  transition: opacity 0.5s, transform 0.5s, height 0.5s, display 0.5s allow-discrete;
}

/*  1. BEFORE-OPEN STATE   */
/*  Starting point for the transition */
@starting-style {
  .item {
    opacity: 0;
    height: 0;
  }
}

/*  2. EXITING STATE   */
/*  While it is deleting, before DOM removal in JS, apply this
    transformation for height, opacity, and a transform which
    skews the element and moves it to the left before setting
    it to display: none */
.is-deleting {
  opacity: 0;
  height: 0;
  display: none;
  transform: skewX(50deg) translateX(-25vw);
}

Ahora tienes un estado de entrada y salida para estos elementos de la lista de tareas pendientes:

Cómo animar elementos desde y hacia la capa superior

Para animar elementos hacia y desde la capa superior, especifica el @starting-style en el estado "abierto" para indicarle al navegador desde dónde animar. En el caso de un diálogo, el estado abierto se define con el atributo [open]. Para una ventana emergente, usa la seudoclase :popover-open.

Un ejemplo simple de un diálogo podría verse de la siguiente manera:

/*   0. IS-OPEN STATE   */
dialog[open] {
  translate: 0 0;
}

/*   1. BEFORE-OPEN STATE   */
@starting-style {
  dialog[open] {
    translate: 0 100vh;
  }
}

/*   2. EXIT STATE   */
dialog {
  transition: translate 0.7s ease-out, overlay 0.7s ease-out allow-discrete, display 0.7s ease-out allow-discrete;
  translate: 0 100vh;
}

En el siguiente ejemplo, los efectos de entrada y salida son diferentes. Para ingresar, anima desde la parte inferior del viewport hacia arriba y sal del efecto en la parte superior del viewport. También se escribe con CSS anidado para lograr un encapsulamiento más visual.

Cuando animes un popover, usa la pseudoclase :popover-open en lugar del atributo open que se usó anteriormente.

.settings-popover {
  &:popover-open {
    /*  0. IS-OPEN STATE  */
    /*  state when popover is open, BOTH:
        what we're transitioning *in* to 
        and transitioning *out* from */
    transform: translateY(0);
    opacity: 1;

    /*  1. BEFORE-OPEN STATE  */
    /*  Initial state for what we're animating *in* from, 
        in this case: goes from lower (y + 20px) to center  */
    @starting-style {
      transform: translateY(20px);
      opacity: 0;
    }
  }
  
  /*  2. EXIT STATE  */
  /*  Initial state for what we're animating *out* to , 
      in this case: goes from center to (y - 50px) higher */
  transform: translateY(-50px);
  opacity: 0;
  
  /*  Enumerate transitioning properties, 
      including display and allow-discrete mode */
  transition: transform 0.5s, opacity 0.5s, display 0.5s allow-discrete;
}

overlay propiedad

Por último, para atenuar un popover o dialog de la capa superior, agrega la propiedad overlay a tu lista de transiciones. popover y dialog escapan de los clips y las transformaciones ancestrales, y también colocan el contenido en la capa superior. Si no haces la transición de overlay, tu elemento volverá a recortarse, transformarse y cubrirse de inmediato, y no verás la transición.

[open] {
  transition: opacity 1s, display 1s allow-discrete;
}

En su lugar, incluye overlay en la transición o animación para animar overlay junto con el resto de los componentes y asegúrate de que permanezca en la capa superior durante la animación. Esto se verá mucho más fluido.

[open] {
  transition: opacity 1s, display 1s allow-discrete, overlay 1s allow-discrete;
}

Además, cuando tienes varios elementos abiertos en la capa superior, la superposición te ayuda a controlar la transición fluida dentro y fuera de la capa superior. Puedes ver la diferencia en este ejemplo sencillo. Si no aplicas overlay al segundo pop-up cuando lo quitas, primero saldrá de la capa superior y saltará detrás del otro pop-up antes de iniciar la transición. Este efecto no es muy fluido.

Nota sobre las transiciones de vista

Si realizas cambios en el DOM, como agregar y quitar elementos del DOM, otra gran solución para obtener animaciones fluidas son las transiciones de vista. Estos son dos de los ejemplos anteriores creados con transiciones de vistas.

En esta primera demostración, en lugar de configurar @starting-style y otras transformaciones de CSS, las transiciones de vista controlarán la transición. La transición de vista se configura de la siguiente manera:

Primero, en CSS, asigna a cada tarjeta un view-transition-name individual.

.card-1 {
  view-transition-name: card-1;
}

.card-2 {
  view-transition-name: card-2;
}

/* etc. */

Luego, en JavaScript, une la mutación del DOM (en este caso, quitar la tarjeta) en una transición de vista.

deleteBtn.addEventListener('click', () => {
  // Check for browser support
  if (document.startViewTransition) {
    document.startViewTransition(() => {
      // DOM mutation
      card.remove();
    });
  } 
  // Alternative if no browser support
  else {
    card.remove();
  }
})

Ahora, el navegador puede controlar la atenuación y la transformación de cada tarjeta a su nueva posición.

Otro ejemplo en el que esto puede resultar útil es con la demostración de agregar o quitar elementos de lista. En este caso, recordarás agregar un view-transition-name único para cada tarjeta creada.

Conclusión

Estas nuevas funciones de la plataforma nos acercan un poco más a las animaciones de entrada y salida fluidas en la plataforma web. Para obtener más información, consulta estos vínculos: