Hackear el estado de animación CSS y el tiempo de reproducción PlatoBlockchain Data Intelligence. Búsqueda vertical. Ai.

Hackear el estado de animación CSS y el tiempo de reproducción

Wolfenstein solo con CSS es un pequeño proyecto que hice hace unas semanas. Fue un experimento con transformaciones y animaciones 3D CSS.

Inspirado por el demostración de disparos en primera persona y otro Código Wolfenstein, decidí construir mi propia versión. Se basa libremente en el Episodio 1 - Piso 9 del juego Wolfenstein 3D original.

Editor: este juego requiere intencionalmente una reacción rápida para evitar una pantalla Game Over.

Aquí hay un video de reproducción:

[Contenido incrustado]

En pocas palabras, mi proyecto no es más que una animación CSS larga cuidadosamente escrita. Además de algunos casos de la truco de casilla de verificación.

:checked ~ div { animation-name: spin; }

El entorno consta de caras de cuadrícula 3D y las animaciones son en su mayoría traslaciones y rotaciones 3D simples. Nada realmente lujoso.

Sin embargo, dos problemas fueron particularmente difíciles de resolver:

  • Reproduzca la animación de "disparo de armas" cada vez que el jugador haga clic en un enemigo.
  • Cuando el jefe de movimiento rápido haya dado el último golpe, ingrese una cámara lenta dramática.

A nivel técnico, esto significaba:

  • Vuelva a reproducir una animación cuando se marque la siguiente casilla de verificación.
  • Reduzca la velocidad de una animación, cuando una casilla de verificación está marcada.

De hecho, ¡ninguno de los dos se resolvió correctamente en mi proyecto! Terminé usando soluciones alternativas o simplemente me di por vencido.

Por otro lado, después de investigar un poco, finalmente encontré la clave para ambos problemas: alterar las propiedades de ejecutar animaciones CSS. En este artículo, exploraremos más a fondo este tema:

  • Muchos ejemplos interactivos.
  • Disecciones: ¿cómo funciona (o no funciona) cada ejemplo?
  • Detrás de escena: ¿cómo manejan los navegadores los estados de animación?

Permítame "tirar mis ladrillos".

Problema 1: reproducción de animación

El primer ejemplo: "solo otra casilla de verificación"

Mi primera intuición fue "simplemente agregue otra casilla de verificación", que no funciona:

Cada casilla de verificación funciona individualmente, pero no ambas juntas. Si una casilla de verificación ya está marcada, la otra ya no funciona.

Así es como funciona (o "no funciona"):

  1. El animation-name of <div> is none por defecto
  2. El usuario hace clic en una casilla de verificación, animation-name se convierte en spin, y la animación comienza desde el principio.
  3. Después de un tiempo, el usuario hace clic en la otra casilla de verificación. Entra en vigor una nueva regla CSS, pero animation-name is aun spin, lo que significa que no se agrega ni elimina ninguna animación. La animación simplemente continúa reproduciéndose como si nada hubiera pasado.

El segundo ejemplo: “clonar la animación”

Un enfoque de trabajo es clonar la animación:

#spin1:checked ~ div { animation-name: spin1; }
#spin2:checked ~ div { animation-name: spin2; }

Así es como funciona:

  1. animation-name is none inicialmente.
  2. El usuario hace clic en "¡Girar!", animation-name se convierte en spin1. La animación spin1 se inicia desde el principio porque se acaba de agregar.
  3. El usuario hace clic en "¡Girar de nuevo!", animation-name se convierte en spin2. La animación spin2 se inicia desde el principio porque se acaba de agregar.

Tenga en cuenta que en el Paso #3, spin1 se elimina debido al orden de las reglas CSS. No funcionará si "¡Gira de nuevo!" se comprueba primero.

El tercer ejemplo: "agregar la misma animación"

Otro enfoque de trabajo es "agregar la misma animación":

#spin1:checked ~ div { animation-name: spin; }
#spin2:checked ~ div { animation-name: spin, spin; }

Esto es similar al ejemplo anterior. De hecho, puedes entender el comportamiento de esta manera:

#spin1:checked ~ div { animation-name: spin1; }
#spin2:checked ~ div { animation-name: spin2, spin1; }

Tenga en cuenta que cuando "¡Girar de nuevo!" está marcado, la antigua animación en ejecución se convierte en la segunda animación de la nueva lista, lo que podría resultar poco intuitivo. Una consecuencia directa es: el truco no funcionará si animation-fill-mode is forwards. Aquí hay una demostración:

Si te preguntas por qué es así, aquí tienes algunas pistas:

  • animation-fill-mode is none por defecto, lo que significa "La animación no tiene ningún efecto si no se está reproduciendo".
  • animation-fill-mode: forwards; significa "Después de que la animación termine de reproducirse, debe permanecer en el último fotograma clave para siempre".
  • spin1La decisión de siempre anula spin2es porque spin1 aparece más adelante en la lista.
  • Supongamos que el usuario hace clic en "¡Girar!", espera un giro completo y luego hace clic en "¡Girar de nuevo!". En este momento. spin1 ya esta terminado y spin2 solo comienza

Discusión

Regla general: no puede "reiniciar" una animación CSS existente. En su lugar, desea agregar y reproducir una nueva animación. Esto puede ser confirmado por la especificación W3C:

Una vez que una animación ha comenzado, continúa hasta que finaliza o se elimina el nombre de la animación.

Ahora, comparando los dos últimos ejemplos, creo que, en la práctica, la "clonación de animaciones" a menudo debería funcionar mejor, especialmente cuando el preprocesador CSS está disponible.

Problema 2: cámara lenta

Uno podría pensar que ralentizar una animación es solo una cuestión de establecer un tiempo más largo. animation-duration:

div { animation-duration: 0.5s; }
#slowmo:checked ~ div { animation-duration: 1.5s; }

De hecho, esto funciona:

… o lo hace?

Con algunos ajustes, debería ser más fácil ver el problema.

Sí, la animación se ralentiza. Y no, no se ve bien. El perro (casi) siempre "salta" cuando activa la casilla de verificación. Además, el perro parece saltar a una posición aleatoria en lugar de la inicial. ¿Cómo?

Sería más fácil de entender si introdujéramos dos “elementos de sombra”:

Ambos elementos de sombra ejecutan las mismas animaciones con diferentes animation-duration. Y no se ven afectados por la casilla de verificación.

Cuando activa la casilla de verificación, el elemento cambia inmediatamente entre los estados de dos elementos de sombra.

Citando la especificación W3C:

Los cambios en los valores de las propiedades de la animación mientras se ejecuta la animación se aplican como si la animación tuviera esos valores desde que comenzó.

Esto sigue a la apátrida design, que permite a los navegadores determinar fácilmente el valor animado. El cálculo real se describe esta página y esta página.

Otro intento

Una idea es pausar la animación actual y luego agregar una animación más lenta que tome el control desde allí:

div {
  animation-name: spin1;
  animation-duration: 2s;
}

#slowmo:checked ~ div {
  animation-name: spin1, spin2;
  animation-duration: 2s, 5s;
  animation-play-state: paused, running;
}

Entonces funciona:

… o lo hace?

Se ralentiza cuando hace clic en "¡Cámara lenta!". Pero si espera un círculo completo, verá un "salto". En realidad, siempre salta a la posición cuando "¡Cámara lenta!" se hace clic en.

La razón es que no tenemos un from fotograma clave definido, y no deberíamos. Cuando el usuario hace clic en "¡Cámara lenta!", spin1 se detiene en alguna posición, y spin2 comienza exactamente en la misma posición. Simplemente no podemos predecir esa posición de antemano... ¿o sí?

Una solución de trabajo

¡Podemos! Al usar una propiedad personalizada, podemos capturar el ángulo en la primera animación y luego pasarlo a la segunda animación:

div {
  transform: rotate(var(--angle1));
  animation-name: spin1;
  animation-duration: 2s;
}

#slowmo:checked ~ div {
  transform: rotate(var(--angle2));
  animation-name: spin1, spin2;
  animation-duration: 2s, 5s;
  animation-play-state: paused, running;
}

@keyframes spin1 {
  to {
    --angle1: 360deg;
  }
}

@keyframes spin2 {
  from {
    --angle2: var(--angle1);
  }
  to {
    --angle2: calc(var(--angle1) + 360deg);
  }
}

Nota: @property se utiliza en este ejemplo, que es no es compatible con todos los navegadores.

La solución "perfecta"

Hay una advertencia a la solución anterior: "salir de cámara lenta" no funciona bien.

Aquí hay una mejor solución:

En esta versión, se puede ingresar o salir de la cámara lenta sin problemas. Tampoco se utiliza ninguna característica experimental. Entonces, ¿es la solución perfecta? Si y no.

Esta solución funciona como "cambiar" "marchas":

  • Engranajes: hay dos <div>s. Uno es el padre del otro. Ambos tienen la spin animación pero con diferentes animation-duration. El estado final del elemento es la acumulación de ambas animaciones.
  • Cambio: Al principio, solo uno <div> tiene su animación en ejecución. El otro está en pausa. Cuando se activa la casilla de verificación, ambas animaciones intercambian sus estados.

Si bien me gusta mucho el resultado, hay un problema: es una buena explotación de la spin animación, que no funciona para otros tipos de animaciones en general.

Una solución práctica (con JS)

Para animaciones generales, es posible lograr la función de cámara lenta con un poco de JavaScript:

Una explicación rápida:

  • Se utiliza una propiedad personalizada para realizar un seguimiento del progreso de la animación.
  • La animación se "reinicia" cuando se activa la casilla de verificación.
  • El código JS calcula la correcta animation-delay para garantizar una transición fluida. yo recomiendo este artículo si no está familiarizado con los valores negativos de animation-delay.

Puede ver esta solución como un híbrido de "reinicio de la animación" y el enfoque de "cambio de marcha".

Aquí es importante seguir correctamente el progreso de la animación. Las soluciones alternativas son posibles si @property no está disponible. Como ejemplo, esta versión utiliza z-index para seguir el progreso:

Nota al margen: originalmente, también intenté crear una versión solo de CSS, pero no tuve éxito. Si bien no estoy 100% seguro, creo que es porque animation-delay is no animable.

Aquí hay una versión con JavaScript mínimo. Solo funciona “entrar en cámara lenta”.

¡Avísame si logras crear una versión que solo funcione con CSS!

Cámara lenta Cualquier animación (con JS)

Por último, me gustaría compartir una solución que funciona para (casi) cualquier animación, incluso con múltiples y complicados @keyframes:

Básicamente, debe agregar un rastreador de progreso de animación, luego calcular cuidadosamente animation-delay para la nueva animación. Sin embargo, a veces puede ser complicado (pero posible) obtener los valores correctos.

Por ejemplo:

  • animation-timing-function no es linear.
  • animation-direction no es normal.
  • múltiples valores en animation-name con diferente animation-durationy animation-delayS

Este método también se describe esta página para API de animaciones web.

AGRADECIMIENTOS

Empecé por este camino después de encontrarme con proyectos de solo CSS. Algunos fueron ilustraciones delicadas, y algunos eran artilugios complejos. Mis favoritos son los que involucran objetos 3D, por ejemplo, este Bouncing Ball y este cubo de embalaje.

Al principio, no tenía ni idea de cómo se hacían. Más tarde leí y aprendí mucho de este buen tutorial de Ana Tudor.

Al final resultó que, construir y animar objetos 3D con CSS no es muy diferente de hacerlo con Batidora de vaso - Blender, solo que con un sabor un poco diferente.

Conclusión

En este artículo examinamos el comportamiento de las animaciones CSS cuando un animate-* se altera la propiedad. Especialmente, elaboramos soluciones para "reproducir una animación" y "animación en cámara lenta".

Espero que encuentres este artículo interesante. ¡Por favor, hágame saber sus pensamientos!

Sello de tiempo:

Mas de Trucos CSS