Hacking CSS Starea animației și timpul de redare PlatoBlockchain Data Intelligence. Căutare verticală. Ai.

Hacking starea animației CSS și timpul de redare

Wolfenstein numai CSS este un mic proiect pe care l-am făcut acum câteva săptămâni. A fost un experiment cu transformări și animații 3D CSS.

Inspirat de Demo FPS şi un alt Wolfenstein CodePen, am decis să-mi construiesc propria versiune. Se bazează în general pe Episodul 1 – Etajul 9 al jocului original Wolfenstein 3D.

Editor: Acest joc necesită în mod intenționat o reacție rapidă pentru a evita ecranul Game Over.

Iată un videoclip de redare:

[Conținutul încorporat]

Pe scurt, proiectul meu nu este altceva decât o animație CSS lungă cu scripturi atent. Plus câteva exemple ale hack casetă de selectare.

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

Mediul este format din fețe de grilă 3D, iar animațiile sunt în mare parte translații și rotații 3D simple. Nimic cu adevărat luxos.

Cu toate acestea, două probleme au fost deosebit de dificil de rezolvat:

  • Redați animația „tragere cu arme” ori de câte ori jucătorul face clic pe un inamic.
  • Când șeful care se mișcă rapid a primit ultima lovitură, introduceți o mișcare lentă dramatică.

La nivel tehnic, aceasta însemna:

  • Redați din nou o animație când următoarea casetă de selectare este bifată.
  • Încetiniți o animație, când o casetă de selectare este bifată.

De fapt, niciunul nu a fost rezolvat corect în proiectul meu! Fie am ajuns să folosesc soluții de soluționare, fie pur și simplu am renunțat.

Pe de altă parte, după câteva săpături, în cele din urmă am găsit cheia ambelor probleme: modificarea proprietăților rulării animațiilor CSS. În acest articol, vom explora în continuare acest subiect:

  • O mulțime de exemple interactive.
  • Disecții: cum funcționează (sau nu funcționează) fiecare exemplu?
  • În spatele scenei: cum gestionează browserele stările de animație?

Lasa-ma „aruncă-mi cărămizile”.

Problema 1: Reluarea animației

Primul exemplu: „doar o altă casetă de selectare”

Prima mea intuiție a fost „doar adăugați o altă casetă de selectare”, care nu funcționează:

Fiecare casetă de selectare funcționează individual, dar nu ambele împreună. Dacă o casetă de selectare este deja bifată, cealaltă nu mai funcționează.

Iată cum funcționează (sau „nu funcționează”):

  1. animation-name of <div> is none în mod implicit.
  2. Utilizatorul face clic pe o casetă de selectare, animation-name devine spin, iar animația începe de la început.
  3. După un timp, utilizatorul face clic pe cealaltă casetă de selectare. O nouă regulă CSS intră în vigoare, dar animation-name is încă spin, ceea ce înseamnă că nicio animație nu este adăugată sau eliminată. Pur și simplu, animația continuă să fie redată ca și cum nimic nu s-ar fi întâmplat.

Al doilea exemplu: „clonarea animației”

O abordare de lucru este clonarea animației:

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

Iată cum funcționează:

  1. animation-name is none inițial.
  2. Utilizatorul face clic pe „Rotire!”, animation-name devine spin1. Animația spin1 este început de la început pentru că tocmai a fost adăugat.
  3. Utilizatorul face clic pe „Rotiți din nou!”, animation-name devine spin2. Animația spin2 este început de la început pentru că tocmai a fost adăugat.

Rețineți că la Pasul #3, spin1 este eliminat din cauza ordinii regulilor CSS. Nu va funcționa dacă „Rotiți din nou!” este verificat mai întâi.

Al treilea exemplu: „adăugarea aceleiași animații”

O altă abordare de lucru este „adăugarea aceleiași animații”:

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

Acesta este similar cu exemplul anterior. Puteți înțelege comportamentul astfel:

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

Rețineți că atunci când „Rotiți din nou!” este bifată, vechea animație de rulare devine a doua animație din noua listă, ceea ce ar putea fi neintuitiv. O consecință directă este: trucul nu va funcționa dacă animation-fill-mode is forwards. Iată un demo:

Dacă vă întrebați de ce este cazul, iată câteva indicii:

  • animation-fill-mode is none implicit, ceea ce înseamnă „animația nu are niciun efect dacă nu este redată”.
  • animation-fill-mode: forwards; înseamnă „După ce animația se termină de redat, trebuie să rămână la ultimul cadru cheie pentru totdeauna”.
  • spin1decizia lui anulează întotdeauna spin2pentru că spin1 apare mai târziu în listă.
  • Să presupunem că utilizatorul dă clic pe „Rotire!”, așteaptă o rotire completă, apoi face clic pe „Învârtire din nou!”. În acest moment. spin1 este deja terminat și spin2 abia începe.

Discuție

Regula generală: nu puteți „reporni” o animație CSS existentă. În schimb, doriți să adăugați și să redați o nouă animație. Acest lucru poate fi confirmat de specificațiile W3C:

Odată ce o animație a început, aceasta continuă până când se termină sau numele animației este eliminat.

Acum comparând ultimele două exemple, cred că în practică, „animațiile de clonare” ar trebui să funcționeze adesea mai bine, mai ales când este disponibil preprocesorul CSS.

Problema 2: Mișcare lentă

S-ar putea crede că încetinirea unei animații este doar o chestiune de a seta o mai lungă animation-duration:

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

Într-adevăr, aceasta funcționează:

… sau asa?

Cu câteva modificări, ar trebui să fie mai ușor să vedeți problema.

Da, animația este încetinită. Și nu, nu arată bine. Câinele (aproape) întotdeauna „sare” atunci când comutați caseta de selectare. În plus, câinele pare să sară într-o poziție aleatorie mai degrabă decât cea inițială. Cum se face?

Ar fi mai ușor de înțeles dacă am introduce două „elemente umbră”:

Ambele elemente de umbră rulează aceleași animații cu diferite animation-duration. Și nu sunt afectați de caseta de selectare.

Când comutați caseta de selectare, elementul comută imediat între stările a două elemente umbră.

Citându-l specificațiile W3C:

Modificările aduse valorilor proprietăților animației în timp ce animația rulează se aplică ca și cum animația ar avea acele valori de când a început.

Aceasta urmeaza Fara stare design, care permite browserelor să determine cu ușurință valoarea animată. Este descris calculul propriu-zis aici și aici.

O altă încercare

O idee este să întrerupeți animația curentă, apoi să adăugați o animație mai lentă care preia de acolo:

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

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

Deci funcționează:

… sau asa?

Se încetinește atunci când dați clic pe „Slowmo!”. Dar dacă așteptați un cerc complet, veți vedea un „salt”. De fapt, sare întotdeauna în poziția când „Slowmo!” se face clic pe.

Motivul este că nu avem un from cadru cheie definit – și nu ar trebui. Când utilizatorul face clic pe „Slowmo!”, spin1 este întrerupt într-o anumită poziție și spin2 începe exact în aceeași poziție. Pur și simplu nu putem prezice această poziție dinainte... sau putem?

O soluție de lucru

Putem! Folosind o proprietate personalizată, putem capta unghiul din prima animație, apoi îl putem trece la a doua animație:

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);
  }
}

Notă: @property este folosit în acest exemplu, care este nu este acceptat de toate browserele.

Soluția „perfectă”.

Există o avertizare la soluția anterioară: „exiting slowmo” nu funcționează bine.

Iată o soluție mai bună:

În această versiune, încetinitorul poate fi introdus sau ieșit fără probleme. Nici o caracteristică experimentală nu este utilizată. Deci este soluția perfectă? Da și nu.

Această soluție funcționează ca „schimbarea” „vitezelor”:

  • Unelte: sunt două <div>s. Unul este părintele celuilalt. Ambele au spin animație dar cu diferite animation-duration. Starea finală a elementului este acumularea ambelor animații.
  • Schimbarea: La început, doar una <div> își rulează animația. Celălalt este întrerupt. Când caseta de selectare este activată, ambele animații își schimbă stările.

Deși îmi place foarte mult rezultatul, există o problemă: este o exploatare frumoasă a spin animație, care nu funcționează pentru alte tipuri de animații în general.

O soluție practică (cu JS)

Pentru animațiile generale, este posibil să se realizeze funcția de mișcare lentă cu puțin JavaScript:

O explicație rapidă:

  • O proprietate personalizată este utilizată pentru a urmări progresul animației.
  • Animația este „repornită” când caseta de selectare este comutată.
  • Codul JS calculează corect animation-delay pentru a asigura o tranziție fără întreruperi. Vă recomand acest articol dacă nu sunteți familiarizat cu valorile negative ale animation-delay.

Puteți vedea această soluție ca un hibrid între „repornirea animației” și abordarea „schimbarea vitezelor”.

Aici este important să urmăriți corect progresul animației. Sunt posibile soluții dacă @property nu este disponibil. De exemplu, această versiune folosește z-index pentru a urmări progresul:

Notă secundară: inițial, am încercat să creez și o versiune numai CSS, dar nu am reușit. Deși nu sunt 100% sigur, cred că este pentru că animation-delay is nu animabil.

Iată o versiune cu JavaScript minim. Funcționează doar „introducerea în slowmo”.

Vă rog să-mi spuneți dacă reușiți să creați o versiune funcțională numai pentru CSS!

Încet orice animație (cu JS)

În cele din urmă, aș dori să împărtășesc o soluție care funcționează pentru (aproape) orice animație, chiar și cu mai multe @keyframes:

Practic, trebuie să adăugați un instrument de urmărire a progresului animației, apoi să calculați cu atenție animation-delay pentru noua animație. Cu toate acestea, uneori ar putea fi dificil (dar posibil) să obțineți valorile corecte.

De exemplu:

  • animation-timing-function nu este linear.
  • animation-direction nu este normal.
  • valori multiple în animation-name cu diferite animation-durationși animation-delayE.

Este descrisă și această metodă aici pentru API-ul Web Animations.

recunoasteri

Am început pe această cale după ce am întâlnit proiecte numai CSS. Undeva opera de artă delicată, iar unii au fost unelte complexe. Preferatele mele sunt cele care implică obiecte 3D, de exemplu, aceasta minge care sari iar acest lucru cub de ambalare.

La început, nu aveam nicio idee cum au fost făcute acestea. Mai târziu am citit și am învățat multe din acest tutorial frumos de Ana Tudor.

După cum sa dovedit, construirea și animarea obiectelor 3D cu CSS nu este mult diferită de a face asta cu Blender, doar cu o aromă puțin diferită.

Concluzie

În acest articol am examinat comportamentul animațiilor CSS atunci când un animate-* proprietatea este modificată. În special am elaborat soluții pentru „reluarea unei animații” și „animație slow-mo”.

Sper că veți găsi acest articol interesant. Te rog sa imi spui la ce te gandesti!

Timestamp-ul:

Mai mult de la CSS Trucuri