Saltar enlaces

Una comprensión preliminar de las animaciones activadas por desplazamiento

Chrome ya ofrece animaciones activadas por desplazamiento y es el primer navegador en hacerlo. Si actualizaste a Cromo 146puedes ver la demostración a continuación donde el fondo del cuadrado se desvanece a lo largo de la duración. 300mspero sólo si todo el elemento está dentro de la ventana gráfica.

Esto es un poco diferente del método. voluta-conducido animación Cómo funcionan, por eso en este artículo las compararé y luego te mostraré cómo funcionan las animaciones activadas por desplazamiento.

Una vez que se excede un cierto umbral de desplazamiento, la animación activada por el desplazamiento se reproduce durante un período fijo. (Piense en JavaScript API de observador de intersección Pero para la animación CSS. )

Esto es diferente de las animaciones basadas en desplazamiento, donde el progreso de la animación se sincroniza con el progreso del desplazamiento (animation-timeline: scroll()) o grado de intersección (animation-timeline: view()) y por tanto no tiene duración.

La parte clave es timeline-trigger: view() en lugar de animation-timeline: view()que espera a que el elemento esté dentro de un umbral en lugar de medir Cuántos Está en ello y hace algo en consecuencia. Sin embargo, comencemos con lo práctico. @keyframes animación, se pone background:

/* Define the animation */
@keyframes fade-bg-in {
  to {
    background: currentColor;
  }
}

esta ambientado en .square dentro de la duración 300ms:

.square {
  /* Declare animation */
  animation: fade-bg-in 300ms;
}

De forma predeterminada, las animaciones CSS se activan cuando se declara la aplicación, pero en el siguiente fragmento de extensión, timeline-trigger Anule este comportamiento. La animación ahora se activa cuando el elemento ingresa. view(). este --trigger es solo una marca de línea de puntos que actúa como identificador del disparador, mientras que entry 100% exit 0% es un rango de línea de tiempo. El rango de la línea de tiempo especifica el área de desplazamiento donde comienza la animación y donde se le permite permanecer activa.

En este caso, la animación se activa cuando el borde inferior .square aporte (entry 100%) y cancele el disparador (suponiendo que todavía esté ejecutándose) cuando el borde superior salga del puerto de desplazamiento (exit 0%). Para mayor claridad, entry 0% cuando arriba Entrada por borde. entry maneja elementos provenientes de la parte inferior del puerto de desplazamiento, mientras exit Manéjelo desde arriba. Un poco confuso, pero más fácil de entender si no lo explico demasiado.

.square {
  /* Declare animation */
  animation: fade-bg-in 300ms;

  /* Animation trigger conditions */
  timeline-trigger: --trigger view() entry 100% exit 0%;
}

para animation-triggerprimero especificamos el disparador del que estamos hablando y luego declaramos algunas configuraciones (por ejemplo, play-forwards):

.square {
  /* Declare animation */
  animation: fade-bg-in 300ms;

  /* Animation trigger conditions */
  timeline-trigger: --trigger view() entry 100% exit 0%;

  /* Animation trigger settings */
  animation-trigger: --trigger play-forwards;
}

este play-forwards La palabra clave activa la animación cada vez que el cuadrado se vuelve completamente visible y como aún no hemos declarado un modo de relleno para la animación (usando animation-fill-mode o como animation abreviatura), lo que significa que el cuadrado no conserva el fondo y la animación es más bien un flash.

Por lo tanto, necesitamos lograr resultados diferentes en base a esto.

animation-fill-mode y <animation-action>

Primero, revise el propósito de los diferentes valores del modo de llenado. animation-fill-mode o como animation taquigrafía:

  • forwards: el estilo se conserva atrás animación.
  • backwards: estilo de aplicación adelante animación.
  • both: Ambos comportamientos se aplican.

Ahora, suponemos <animation-action>play-forwards (como antes) y el modo de llenado es forwards (both Sería redundante porque background Ni siquiera configurado para empezar):

.square {
  animation: fade-bg-in 300ms forwards;
  timeline-trigger: --trigger view() entry 100% exit 0%;
  animation-trigger: --trigger play-forwards;
}

Esto hace que el estilo se conserve; sin embargo, si el cuadrado sale parcial o completamente de la ventana gráfica y luego vuelve a ingresar a ella, la animación se reiniciará, lo que puede causar parpadeos dependiendo de cómo terminó la animación, que es lo que sucedió en este ejemplo.

Hay dos maneras diferentes de resolver este problema…

Método de “bloqueo”: uso play-once en lugar de play-forwardsen comparación con forwardslo que hace que la animación se reproduzca una vez sin reiniciarse y luego conserve el estilo.

.square {
  /* Play once */
  animation-trigger: --trigger play-once;

  /* Retain the styles */
  animation: fade-bg-in 300ms forwards;

  timeline-trigger: --trigger view() entry 100% exit 0%;
}

Método de “ida y vuelta”: play-forwards play-backwards Animar normalmente cuando el elemento sea completamente visible y invertir cuando ya no sea completamente visible. No hay flash porque el elemento se anima hacia atrás con la misma suavidad que hacia adelante. Además, incluso si la dirección de la animación puede cambiar, el modo de relleno puede permanecer en forwards en lugar de estar configurado para both.

¿Por qué?

play-forwards significa “reproducir animación de 0% a 100%”, mientras que play-backwards Significa “reproducir animación del 100% al 0%”. Al mismo tiempo, como mencioné antes, forwards El modo de relleno significa “preservar el estilo cuando se completa la animación”; bueno, no importa si el fotograma clave final es 0% o 100%.

.square {
  /* Play forward and backward, as appropriate */
  animation-trigger: --trigger play-forwards play-backwards;

  /* Retain the styles either way */
  animation: fade-bg-in 300ms forwards;

  timeline-trigger: --trigger view() entry 100% exit 0%;
}

play-forwards, play-oncey play-backwards No es la única palabra clave <animation-action>. Aquí hay una descripción general rápida:

<animation-action> Influencia
none Se utiliza para desactivar condicionalmente un disparador al entrar pero no al salir (o viceversa), o para manejar múltiples disparadores con un solo disparador. animation-trigger
play-forwards Permitir que la animación se reproduzca
play-backwards Permitir que la animación se reproduzca al revés
play-once Adelante o atrás (lo que ocurra primero)
play Juega en la última dirección especificada, o hacia adelante si no se especifica ninguna.
pause pausar la animación
reset Pausa la animación y establece el progreso en 0.
replay Establecer el progreso en 0 sin pausar la animación

Estos <animation-action>No solo permite un gran control sobre la animación mientras se desplaza, sino que también permite diferentes combinaciones de acciones, modos de relleno, rangos de línea de tiempo, y podemos crear la animación de salida en @keyframes Las reglas significan que normalmente hay varias formas de lograr un determinado resultado.

Si bien las animaciones activadas por desplazamiento, que consisten en acciones de animación, modos de relleno, rangos de línea de tiempo, etc., pueden parecer demasiado complejas, el hecho de que estos mecanismos estén desacoplados nos permite reutilizar la lógica manteniendo la flexibilidad, reduciendo la duplicación y haciendo que el mecanismo sea más amigable para el sistema de diseño.

Esta vez considere tres cuadrados y para agregar un poco de complejidad afirmamos scale: 70% (animado a initial) y definir dos animaciones de rotación.

<div id="squares">
  <div class="square rotate-left"></div>
  <div class="square"></div>
  <div class="square rotate-right"></div>
</div>
/* Define animations */
@keyframes intensify {
  to {
    scale: initial;
    background: currentColor;
  }
}

@keyframes rotate-left {
  to {
    rotate: -5deg;
  }
}

@keyframes rotate-right {
  to {
    rotate: 5deg;
  }
}

.square {
  /* Set starting value */
  scale: 70%;
}

Eso es todo después de eso, si bien este es obviamente un ejemplo más complejo, la capacidad de combinar valores en propiedades abreviadas y desacoplarlos en propiedades normales, así como la naturaleza desacoplada de los diferentes mecanismos, no solo mejora la flexibilidad sino también la reutilización (en este caso, intercalando varias animaciones usando la misma configuración de activación de animación):

.square {
  /* Set starting value */
  scale: 70%;

  /* Define animation name */
  --base-animation: intensify;

  /* Declare animation */
  animation: var(--base-animation) 300ms forwards;

  /* Define animation trigger settings */
  --animation-trigger: --trigger play-forwards play-backwards;

  /* Declare for intensify, then for one of either rotate animations */
  animation-trigger: var(--animation-trigger), var(--animation-trigger);

  /* Declare animation trigger conditions (without timeline ranges) */
  timeline-trigger: --trigger view();

  /* Declare active range end */
  timeline-trigger-active-range-end: normal;

  /* Append other animations */
  &.rotate-left {
    animation-name: var(--base-animation), rotate-left;
  }

  &.rotate-right {
    animation-name: var(--base-animation), rotate-right;
  }

  /* Stagger activation ranges */
  &:first-child {
    timeline-trigger-activation-range-start: entry 33.3333%;
  }

  &:nth-child(2) {
    timeline-trigger-activation-range-start: entry 66.6666%;
  }

  &:last-child {
    timeline-trigger-activation-range-start: entry 99.9999%;
  }
}

Esta es una versión más limpia y robusta que utiliza sibling-count() y sibling-index() (Falta de soporte para Firefox) Animación escalonada:

En esta versión, en lugar de configurar timeline-trigger-activation-range-start En cada cuadrado individual, simplemente apuntamos .square Y calcule el valor de entrada sobre la marcha:

/* Maximum entry ÷ number of squares */
--stagger-interval: calc(100% / sibling-count());

/* Current square’s index × stagger interval */
--entry: calc(sibling-index() * var(--stagger-interval));

/* Declare animation trigger conditions */
timeline-trigger: --trigger view() entry var(--entry) exit 0%;

Dejar que un elemento desencadene otros elementos

En este caso, moveremos el disparador y su alcance al primer bloque y dejaremos que los otros bloques sigan con un retraso de animación escalonado. Como puede ver, todas las animaciones son creadas por animation-trigger Una vez que se haya ingresado el 50% del primer cuadrado (entry 50%) ventana (view()). animation-trigger motivado timeline-trigger Porque el logotipo de la línea de puntos (acertadamente llamado --trigger) para vincularlos:

/* Define animations */
@keyframes intensify {
  to {
    scale: initial;
    background: currentColor;
  }
}

@keyframes rotate-left {
  to {
    rotate: -5deg;
  }
}

@keyframes rotate-right {
  to {
    rotate: 5deg;
  }
}

.square {
  /* Set starting value */
  scale: 70%;

  /* Define animation name */
  --base-animation: intensify;

  /* Maximum delay ÷ number of squares */
  --stagger-interval: calc(300ms / sibling-count());

  /* Current square’s index × stagger interval */
  --animation-delay: calc(sibling-index() * var(--stagger-interval));

  /* Declare animation */
  animation: var(--base-animation) 300ms var(--animation-delay) forwards;

  /* Define animation trigger settings */
  --animation-trigger: --trigger play-forwards play-backwards;

  /* Declare for intensify, then for one of either rotate animations */
  animation-trigger: var(--animation-trigger), var(--animation-trigger);

  &:first-child {
    /* Declare animation trigger conditions */
    timeline-trigger: --trigger view() entry 50%;

    /* Declare active range end */
    timeline-trigger-active-range-end: normal;
  }

  /* Append other animations */
  &.rotate-left {
    animation-name: var(--base-animation), rotate-left;
  }

  &.rotate-right {
    animation-name: var(--base-animation), rotate-right;
  }
}

Una desventaja es que cuando animation-trigger esta en play-backwards En el modo, la animación no se escalonará. Esto se debe a que, Creoel retraso también se incluye cuando se invierte la animación. Esto me parece un descuido, sobre todo porque ese no es el caso. animation-direction: reversepero podría estar completamente equivocado en esto.

Comprender el marco de tiempo

Los rangos de la línea de tiempo son una parte importante de las animaciones activadas por desplazamiento, pero son un mecanismo independiente. Para desplazarse-conducido Animaciones que querrás animation-range y sus propiedades comunes. Viene con pergamino –motivado Para la animación, la sintaxis es básicamente la misma, pero utiliza propiedades diferentes y dos alcances diferentes. El rango de activación determina el área de desplazamiento donde se activa la animación, mientras que el rango de actividad determina el área donde persiste la animación (incluso si ya no está dentro del rango de activación).

El alcance de la línea de tiempo es un poco pesado. Sin embargo, view() entry 100% exit 0% (cuando sea completamente visible) y view() contain (Lo mismo, pero si es más grande que la ventana gráfica) La mayoría de las veces es suficiente.

Pero si desea profundizar más, animation-rangeaunque se usa para desplazarse –conducido La animación es más ligera y proporciona una comprensión de nivel principiante del alcance de la línea de tiempo. Después recomiendo leer. Especificación del activador de animación para cubrir las muchas complejidades del alcance de la línea de tiempo en el contexto de estas animaciones activadas por desplazamiento.

Otro componente de la animación del activador de desplazamiento que también es propio es view() función, pero es más fácil de resumir aquí. Básicamente, cuando se trata de animaciones activadas por desplazamiento, view() es la ventana gráfica. Entonces si tienes un 5rem título pegajoso, view(y 0 5rem) hará que el rango de la línea de tiempo se factorice a lo largo del eje y.

pensamientos finales

Las animaciones activadas por desplazamiento pueden ser complicadas porque, al igual que las animaciones controladas por desplazamiento, aprovechan características CSS antiguas (principalmente animation) y otros mecanismos de función nuevos (marca de líneas de puntos, view()rango de línea de tiempo) y propiedades CSS específicas de animaciones activadas por desplazamiento. Están sucediendo muchas cosas al mismo tiempo.

Para ser honesto, no estoy seguro de lo que siento por ellos. Son geniales, divertidos y útiles, claro, pero también son complicados, y me tomó un tiempo antes de que realmente pudiera comenzar a hablar maravillas de ellos.

Home
Account
Cart
Search
¡Hola! ¡Pregúntame lo que quieras!
Explore
Drag