Hacking CSS Animation State og Playback Time PlatoBlockchain Data Intelligence. Lodret søgning. Ai.

Hacking CSS Animation State og Playback Time

Wolfenstein, der kun er CSS er et lille projekt, som jeg lavede for et par uger siden. Det var et eksperiment med CSS 3D-transformationer og animationer.

Inspireret af FPS demo og en anden Wolfenstein CodePen, besluttede jeg at bygge min egen version. Det er løst baseret på Episode 1 – Etage 9 af det originale Wolfenstein 3D-spil.

Redaktør: Dette spil kræver med vilje en hurtig reaktion for at undgå en Game Over-skærm.

Her er en playthrough-video:

[Indlejret indhold]

I en nøddeskal er mit projekt intet andet end en omhyggeligt scriptet lang CSS-animation. Plus et par forekomster af afkrydsningsfelt hack.

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

Miljøet består af 3D-gitterflader, og animationerne er for det meste almindelige 3D-oversættelser og -rotationer. Intet rigtig fancy.

To problemer var dog særligt vanskelige at løse:

  • Spil "våbenaffyring"-animationen, når spilleren klikker på en fjende.
  • Når den hurtige chef fik det sidste hit, indtast en dramatisk slowmotion.

På teknisk niveau betød dette:

  • Afspil en animation igen, når det næste afkrydsningsfelt er markeret.
  • Sæt farten ned for en animation, når et afkrydsningsfelt er markeret.

Faktisk blev ingen af ​​dem løst ordentligt i mit projekt! Jeg endte enten med at bruge løsninger eller gav bare op.

På den anden side, efter lidt gravearbejde, fandt jeg til sidst nøglen til begge problemer: at ændre egenskaberne ved at køre CSS-animationer. I denne artikel vil vi udforske dette emne yderligere:

  • Masser af interaktive eksempler.
  • Dissektioner: hvordan fungerer hvert eksempel (eller virker ikke)?
  • Bag scenen: hvordan håndterer browsere animationstilstande?

Lad mig "smid mine klodser".

Problem 1: Genafspilning af animation

Det første eksempel: "bare endnu et afkrydsningsfelt"

Min første intuition var "bare tilføje endnu et afkrydsningsfelt", som ikke virker:

Hvert afkrydsningsfelt fungerer individuelt, men ikke begge sammen. Hvis et afkrydsningsfelt allerede er markeret, virker det andet ikke længere.

Sådan fungerer det (eller "virker ikke"):

  1. animation-name of <div> is none som standard.
  2. Brugeren klikker på et afkrydsningsfelt, animation-name bliver spin, og animationen starter fra begyndelsen.
  3. Efter et stykke tid klikker brugeren på det andet afkrydsningsfelt. En ny CSS-regel træder i kraft, men animation-name is stadig spin, hvilket betyder, at ingen animation tilføjes eller fjernes. Animationen fortsætter simpelthen som om intet var hændt.

Det andet eksempel: "kloning af animationen"

En arbejdsmetode er at klone animationen:

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

Her er hvordan det virker:

  1. animation-name is none i første omgang.
  2. Brugeren klikker på "Spin!", animation-name bliver spin1. Animationen spin1 er startet fra begyndelsen, fordi den lige er blevet tilføjet.
  3. Brugeren klikker på "Spin igen!", animation-name bliver spin2. Animationen spin2 er startet fra begyndelsen, fordi den lige er blevet tilføjet.

Bemærk, at i trin #3, spin1 er fjernet på grund af rækkefølgen af ​​CSS-reglerne. Det virker ikke, hvis "Spin igen!" kontrolleres først.

Det tredje eksempel: "tilføj den samme animation"

En anden arbejdsmetode er at "tilføje den samme animation":

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

Dette svarer til det foregående eksempel. Du kan faktisk forstå adfærden på denne måde:

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

Bemærk, at når "Snurr igen!" er markeret, bliver den gamle kørende animation den anden animation på den nye liste, hvilket kan være uintuitivt. En direkte konsekvens er: tricket virker ikke hvis animation-fill-mode is forwards. Her er en demo:

Hvis du undrer dig over, hvorfor dette er tilfældet, er her nogle spor:

  • animation-fill-mode is none som standard, hvilket betyder "Animationen har overhovedet ingen effekt, hvis den ikke afspilles".
  • animation-fill-mode: forwards; betyder "Når animationen er færdig med at spille, skal den forblive ved den sidste keyframe for evigt".
  • spin1's beslutning altid tilsidesætte spin2skyldes det spin1 vises senere på listen.
  • Antag, at brugeren klikker på "Spin!", venter på et helt spin, og klikker derefter på "Spin igen!". I dette øjeblik. spin1 er allerede færdig, og spin2 bare starter.

Diskussion

Tommelfingerregel: du kan ikke "genstarte" en eksisterende CSS-animation. I stedet vil du tilføje og afspille en ny animation. Dette kan bekræftes af W3C-specifikationen:

Når en animation er startet, fortsætter den, indtil den slutter, eller animationsnavnet fjernes.

Når man nu sammenligner de sidste to eksempler, tror jeg i praksis, at "kloning af animationer" ofte burde fungere bedre, især når CSS-preprocessor er tilgængelig.

Opgave 2: Slowmotion

Man kan tro, at det at bremse en animation bare er et spørgsmål om at indstille en længere animation-duration:

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

Dette virker faktisk:

… eller gør det?

Med et par justeringer burde det være lettere at se problemet.

Ja, animationen er bremset. Og nej, det ser ikke godt ud. Hunden "hopper" (næsten) altid, når du skifter afkrydsningsfeltet. Desuden ser hunden ud til at hoppe til en tilfældig position i stedet for den oprindelige. Hvorfor?

Det ville være lettere at forstå det, hvis vi introducerede to "skyggeelementer":

Begge skyggeelementer kører de samme animationer med forskellige animation-duration. Og de påvirkes ikke af afkrydsningsfeltet.

Når du skifter afkrydsningsfeltet, skifter elementet bare øjeblikkeligt mellem tilstandene for to skyggeelementer.

Citerer W3C-specifikationen:

Ændringer af værdierne for animationsegenskaber, mens animationen kører, gælder, som om animationen havde disse værdier, fra da den startede.

Dette følger den statsløs design, som giver browsere mulighed for nemt at bestemme den animerede værdi. Selve beregningen er beskrevet link. , link..

Endnu et Forsøg

En idé er at sætte den aktuelle animation på pause og derefter tilføje en langsommere animation, der tager over derfra:

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

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

Så det virker:

… eller gør det?

Det går langsommere, når du klikker på "Slowmo!". Men hvis du venter på en fuld cirkel, vil du se et "hop". Faktisk hopper den altid til positionen, når "Slowmo!" er klikket på.

Grunden er, at vi ikke har en from keyframe defineret – og det burde vi ikke. Når brugeren klikker på "Slowmo!", spin1 er sat på pause i en eller anden position, og spin2 starter i nøjagtig samme position. Vi kan simpelthen ikke forudsige den holdning på forhånd … eller kan vi?

En fungerende løsning

Vi kan! Ved at bruge en brugerdefineret egenskab kan vi fange vinklen i den første animation og derefter overføre den til den anden animation:

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

Bemærk: @property bruges i dette eksempel, dvs understøttes ikke af alle browsere.

Den "perfekte" løsning

Der er en advarsel til den tidligere løsning: "at afslutte slowmo" fungerer ikke godt.

Her er en bedre løsning:

I denne version kan slowmotion aktiveres eller afsluttes problemfrit. Der bruges heller ikke nogen eksperimentel funktion. Så er det den perfekte løsning? Ja og nej.

Denne løsning fungerer som at "skifte" "gear":

  • Gear: der er to <div>s. Den ene er forælder til den anden. Begge har spin animation, men med anderledes animation-duration. Den endelige tilstand af elementet er akkumuleringen af ​​begge animationer.
  • Skift: I begyndelsen kun én <div> har sin animation kørende. Den anden er sat på pause. Når afkrydsningsfeltet er slået til/fra, skifter begge animationer deres tilstand.

Selvom jeg virkelig godt kan lide resultatet, er der et problem: det er en god udnyttelse af spin animation, som ikke virker til andre typer animationer generelt.

En praktisk løsning (med JS)

For generelle animationer er det muligt at opnå slowmotion-funktionen med en smule JavaScript:

En hurtig forklaring:

  • En tilpasset egenskab bruges til at spore animationens fremskridt.
  • Animationen "genstartes", når afkrydsningsfeltet er til/fra.
  • JS-koden beregner det rigtige animation-delay for at sikre en problemfri overgang. jeg anbefaler denne artikel hvis du ikke er bekendt med negative værdier af animation-delay.

Du kan se denne løsning som en hybrid af "genstart af animation" og "gearskifte"-tilgangen.

Her er det vigtigt at spore animationens fremskridt korrekt. Løsninger er mulige, hvis @property er ikke tilgængelig. Som et eksempel bruger denne version z-index for at spore fremskridt:

Sidenote: oprindeligt prøvede jeg også at oprette en CSS-only version, men det lykkedes ikke. Selvom jeg ikke er 100% sikker, tror jeg det er fordi animation-delay is ikke animerbar.

Her er en version med minimal JavaScript. Kun "at gå ind i slowmo" virker.

Giv mig venligst besked, hvis du formår at oprette en fungerende CSS-only version!

Slow-mo enhver animation (med JS)

Til sidst vil jeg gerne dele en løsning, der fungerer til (næsten) enhver animation, selv med flere komplicerede @keyframes:

Dybest set skal du tilføje en animationsfremskridtsmåler og derefter omhyggeligt beregne animation-delay til den nye animation. Nogle gange kan det dog være vanskeligt (men muligt) at få de korrekte værdier.

For eksempel:

  • animation-timing-function er ikke linear.
  • animation-direction er ikke normal.
  • flere værdier i animation-name med forskellig animation-durations og animation-delay'S.

Denne metode er også beskrevet link. for Web Animations API.

Tak

Jeg startede ned ad denne vej efter at have stødt på CSS-only-projekter. Nogle var delikat kunstværk, og nogle var komplekse ting. Mine favoritter er dem, der involverer 3D-objekter, for eksempel dette hoppende bold og dette pakning terning.

I begyndelsen havde jeg ingen anelse om, hvordan disse blev lavet. Senere læste jeg og lærte meget af denne fine tutorial af Ana Tudor.

Som det viste sig, er det ikke meget anderledes at bygge og animere 3D-objekter med CSS fra at gøre det med Blender, bare med lidt anderledes smag.

Konklusion

I denne artikel undersøgte vi adfærden af ​​CSS-animationer, når en animate-* ejendom er ændret. Vi har især udarbejdet løsninger til "genafspilning af en animation" og "animation slow-mo".

Jeg håber, du finder denne artikel interessant. Fortæl mig venligst dine tanker!

Tidsstempel:

Mere fra CSS-tricks