Hacking dello stato dell'animazione CSS e del tempo di riproduzione PlatoBlockchain Data Intelligence. Ricerca verticale. Ai.

Hacking dello stato di animazione CSS e del tempo di riproduzione

Solo CSS Wolfenstein è un piccolo progetto che ho realizzato qualche settimana fa. È stato un esperimento con trasformazioni e animazioni 3D CSS.

Ispirato al Demo FPS e un'altra Codice Wolfenstein Penna, ho deciso di creare la mia versione. È vagamente basato sull'Episodio 1 – Piano 9 del gioco originale Wolfenstein 3D.

Editor: questo gioco richiede intenzionalmente una reazione rapida per evitare una schermata di Game Over.

Ecco un video di gioco:

[Contenuto incorporato]

In poche parole, il mio progetto non è altro che una lunga animazione CSS accuratamente programmata. Più alcuni casi di casella di controllo hackerare.

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

L'ambiente è costituito da facce di griglia 3D e le animazioni sono per lo più semplici traslazioni e rotazioni 3D. Niente di veramente elegante.

Tuttavia, due problemi erano particolarmente difficili da risolvere:

  • Riproduci l'animazione "sparare armi" ogni volta che il giocatore fa clic su un nemico.
  • Quando il boss in rapido movimento ha ottenuto l'ultimo colpo, entra in un drammatico rallentatore.

A livello tecnico, ciò significava:

  • Riproduci un'animazione quando viene selezionata la casella di controllo successiva.
  • Rallenta un'animazione, quando una casella di controllo è selezionata.

In effetti, nessuno dei due è stato risolto correttamente nel mio progetto! Ho finito per usare soluzioni alternative o ho semplicemente rinunciato.

D'altra parte, dopo aver scavato un po', alla fine ho trovato la chiave di entrambi i problemi: alterare le proprietà dell'esecuzione delle animazioni CSS. In questo articolo analizzeremo ulteriormente questo argomento:

  • Molti esempi interattivi.
  • Dissezioni: come funziona (o non funziona) ogni esempio?
  • Dietro le quinte: in che modo i browser gestiscono gli stati di animazione?

Lasciami “lancia i miei mattoni”.

Problema 1: riproduzione dell'animazione

Il primo esempio: "solo un'altra casella di controllo"

La mia prima intuizione è stata "basta aggiungere un'altra casella di controllo", che non funziona:

Ogni casella di controllo funziona individualmente, ma non entrambe insieme. Se una casella è già selezionata, l'altra non funziona più.

Ecco come funziona (o “non funziona”):

  1. I animation-name of <div> is none per impostazione predefinita.
  2. L'utente fa clic su una casella di controllo, animation-name diventa spine l'animazione riprende dall'inizio.
  3. Dopo un po', l'utente fa clic sull'altra casella di controllo. Una nuova regola CSS ha effetto, ma animation-name is ancora spin, il che significa che non viene aggiunta né rimossa alcuna animazione. L'animazione continua semplicemente a essere riprodotta come se nulla fosse.

Il secondo esempio: "clonare l'animazione"

Un approccio di lavoro consiste nel clonare l'animazione:

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

Ecco come funziona:

  1. animation-name is none inizialmente.
  2. L'utente fa clic su "Spin!", animation-name diventa spin1. L'animazione spin1 è iniziato dall'inizio perché è stato appena aggiunto.
  3. L'utente fa clic su "Gira di nuovo!", animation-name diventa spin2. L'animazione spin2 è iniziato dall'inizio perché è stato appena aggiunto.

Nota che nel passaggio n. 3, spin1 viene rimosso a causa dell'ordine delle regole CSS. Non funzionerà se "Gira di nuovo!" viene prima controllato.

Il terzo esempio: "aggiungere la stessa animazione"

Un altro approccio di lavoro è "aggiungere la stessa animazione":

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

Questo è simile all'esempio precedente. Puoi effettivamente capire il comportamento in questo modo:

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

Nota che quando "Ruota di nuovo!" è selezionato, la vecchia animazione in esecuzione diventa la seconda animazione nel nuovo elenco, il che potrebbe non essere intuitivo. Una conseguenza diretta è: il trucco non funzionerà se animation-fill-mode is forwards. Ecco una demo:

Se ti chiedi perché questo è il caso, ecco alcuni indizi:

  • animation-fill-mode is none per impostazione predefinita, il che significa "L'animazione non ha alcun effetto se non viene riprodotta".
  • animation-fill-mode: forwards; significa "Al termine della riproduzione dell'animazione, deve rimanere per sempre sull'ultimo fotogramma chiave".
  • spin1La decisione di prevalere sempre spin2è perché spin1 appare più avanti nell'elenco.
  • Supponiamo che l'utente faccia clic su "Giro!", aspetti un giro completo, quindi clicchi su "Giro di nuovo!". In questo momento. spin1 è già finito, e spin2 inizia.

Discussione

Regola pratica: non puoi "riavviare" un'animazione CSS esistente. Invece, vuoi aggiungere e riprodurre una nuova animazione. Ciò può essere confermato da le specifiche del W3C:

Una volta che un'animazione è iniziata, continua finché non finisce o il nome dell'animazione viene rimosso.

Ora, confrontando gli ultimi due esempi, penso che in pratica la "clonazione delle animazioni" dovrebbe spesso funzionare meglio, specialmente quando è disponibile il preprocessore CSS.

Problema 2: rallentatore

Si potrebbe pensare che rallentare un'animazione sia solo questione di impostare un tempo più lungo animation-duration:

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

In effetti, questo funziona:

... o lo fa?

Con alcune modifiche, dovrebbe essere più facile vedere il problema.

Sì, l'animazione è rallentata. E no, non sembra buono. Il cane (quasi) sempre "salta" quando si attiva la casella di controllo. Inoltre, il cane sembra saltare in una posizione casuale piuttosto che in quella iniziale. Come mai?

Sarebbe più facile capirlo se introducessimo due “elementi ombra”:

Entrambi gli elementi ombra eseguono le stesse animazioni con diverse animation-duration. E non sono interessati dalla casella di controllo.

Quando si attiva la casella di controllo, l'elemento passa immediatamente tra gli stati di due elementi ombra.

Quoting le specifiche del W3C:

Le modifiche ai valori delle proprietà dell'animazione durante l'esecuzione dell'animazione si applicano come se l'animazione avesse quei valori dall'inizio.

Questo segue il apolide design, che consente ai browser di determinare facilmente il valore animato. Viene descritto il calcolo vero e proprio qui ed qui.

Un altro tentativo

Un'idea è quella di mettere in pausa l'animazione corrente, quindi aggiungere un'animazione più lenta che riprende da lì:

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

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

Quindi funziona:

... o lo fa?

Rallenta quando fai clic su "Slowmo!". Ma se aspetti un cerchio completo, vedrai un "salto". In realtà, salta sempre alla posizione quando "Slowmo!" viene cliccato.

Il motivo è che non abbiamo un from keyframe definito – e non dovremmo. Quando l'utente fa clic su "Slowmo!", spin1 è in pausa in una certa posizione, e spin2 inizia esattamente nella stessa posizione. Semplicemente non possiamo prevedere quella posizione in anticipo... o possiamo?

Una soluzione funzionante

Noi possiamo! Utilizzando una proprietà personalizzata, possiamo catturare l'angolo nella prima animazione, quindi passarlo alla seconda animazione:

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 è usato in questo esempio, che è non supportato da tutti i browser.

La soluzione "perfetta".

C'è un avvertimento alla soluzione precedente: "l'uscita da slowmo" non funziona bene.

Ecco una soluzione migliore:

In questa versione, il rallentatore può essere inserito o uscito senza problemi. Non viene nemmeno utilizzata alcuna funzione sperimentale. Quindi è la soluzione perfetta? Sì e no.

Questa soluzione funziona come "cambio di marcia":

  • Ingranaggi: ce ne sono due <div>S. Uno è il genitore dell'altro. Entrambi hanno il spin animazione ma con diverso animation-duration. Lo stato finale dell'elemento è l'accumulo di entrambe le animazioni.
  • Spostamento: All'inizio, solo uno <div> ha la sua animazione in esecuzione. L'altro è in pausa. Quando la casella di controllo è attivata, entrambe le animazioni scambiano i loro stati.

Anche se il risultato mi piace molto, c'è un problema: è un bel exploit del spin animazione, che non funziona per altri tipi di animazioni in generale.

Una soluzione pratica (con JS)

Per le animazioni generali, è possibile ottenere la funzione slow motion con un po' di JavaScript:

Una rapida spiegazione:

  • Una proprietà personalizzata viene utilizzata per tenere traccia dell'avanzamento dell'animazione.
  • L'animazione viene "riavviata" quando la casella di controllo è attivata.
  • Il codice JS calcola il corretto animation-delay per garantire una transizione senza interruzioni. raccomando Questo articolo se non hai familiarità con i valori negativi di animation-delay.

Puoi vedere questa soluzione come un ibrido tra il "riavvio dell'animazione" e l'approccio del "cambio di marcia".

Qui è importante monitorare correttamente l'avanzamento dell'animazione. Sono possibili soluzioni alternative se @property Non è disponibile. Ad esempio, questa versione utilizza z-index per monitorare i progressi:

Nota a margine: originariamente, ho anche provato a creare una versione solo CSS ma non ci sono riuscito. Anche se non sono sicuro al 100%, penso che sia perché animation-delay is non animabile.

Ecco una versione con JavaScript minimo. Funziona solo "entrare in slowmo".

Per favore fatemi sapere se riuscite a creare una versione solo CSS funzionante!

Slow-motion Qualsiasi animazione (con JS)

Infine, vorrei condividere una soluzione che funziona per (quasi) qualsiasi animazione, anche con più complicati @keyframes:

Fondamentalmente, devi aggiungere un tracker di avanzamento dell'animazione, quindi calcolare attentamente animation-delay per la nuova animazione. Tuttavia, a volte potrebbe essere difficile (ma possibile) ottenere i valori corretti.

Per esempio:

  • animation-timing-function non è linear.
  • animation-direction non è normal.
  • più valori in animation-name con differenti animation-duration'S e animation-delay'S.

Questo metodo è anche descritto qui per l' API Animazioni Web.

Ringraziamenti

Ho iniziato questo percorso dopo aver incontrato progetti solo CSS. Alcuni erano opera d'arte delicata, e alcuni lo erano congegni complessi. I miei preferiti sono quelli che coinvolgono oggetti 3D, per esempio questo palla che rimbalza e questo cubo di imballaggio.

All'inizio non avevo idea di come fossero fatti. In seguito ho letto e imparato molto questo bel tutorial di Ana Tudor.

Come si è scoperto, costruire e animare oggetti 3D con CSS non è molto diverso dal farlo con Frullatore, solo con un sapore un po' diverso.

Conclusione

In questo articolo abbiamo esaminato il comportamento delle animazioni CSS quando un file animate-* la proprietà è alterata. In particolare abbiamo elaborato soluzioni per "riproduzione di un'animazione" e "animazione al rallentatore".

Spero che troviate questo articolo interessante. Per favore fammi sapere cosa pensi!

Timestamp:

Di più da Trucchi CSS