Hacka CSS-animeringstillstånd och uppspelningstid PlatoBlockchain Data Intelligence. Vertikal sökning. Ai.

Hacka CSS-animeringstillstånd och uppspelningstid

Wolfenstein endast för CSS är ett litet projekt som jag gjorde för några veckor sedan. Det var ett experiment med CSS 3D-transformationer och animationer.

Inspirerad av den FPS demo och en annan Wolfenstein CodePen, jag bestämde mig för att bygga min egen version. Det är löst baserat på avsnitt 1 – våning 9 av det ursprungliga Wolfenstein 3D-spelet.

Redaktör: Det här spelet kräver avsiktligt lite snabb reaktion för att undvika en Game Over-skärm.

Här är en genomspelningsvideo:

[Inbäddat innehåll]

I ett nötskal är mitt projekt inget annat än en noggrant skriptad lång CSS-animation. Plus några exempel på hack i kryssrutan.

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

Miljön består av 3D-rutnätsytor och animationerna är mestadels vanliga 3D-översättningar och -rotationer. Inget riktigt fancy.

Två problem var dock särskilt svåra att lösa:

  • Spela animationen "vapenskjutning" när spelaren klickar på en fiende.
  • När den snabbrörliga chefen fick den sista träffen, gå in i en dramatisk slow motion.

På teknisk nivå innebar detta:

  • Spela upp en animation igen när nästa kryssruta är markerad.
  • Sakta ner en animering när en kryssruta är markerad.

I själva verket var inget av dem ordentligt löst i mitt projekt! Det slutade med att jag använde lösningar eller så gav jag bara upp.

Å andra sidan, efter lite grävande, hittade jag till slut nyckeln till båda problemen: att ändra egenskaperna för att köra CSS-animationer. I den här artikeln kommer vi att utforska mer om detta ämne:

  • Många interaktiva exempel.
  • Dissektioner: hur fungerar varje exempel (eller fungerar inte)?
  • Bakom scenen: hur hanterar webbläsare animerade tillstånd?

Låt mig "kasta mina tegelstenar".

Problem 1: Spela om animation

Det första exemplet: "bara en annan kryssruta"

Min första intuition var "lägg bara till en annan kryssruta", vilket inte fungerar:

Varje kryssruta fungerar individuellt, men inte båda tillsammans. Om en kryssruta redan är markerad fungerar inte den andra längre.

Så här fungerar det (eller "fungerar inte"):

  1. Smakämnen animation-name of <div> is none som standard.
  2. Användaren klickar på en kryssruta, animation-name blir spin, och animeringen börjar från början.
  3. Efter ett tag klickar användaren på den andra kryssrutan. En ny CSS-regel träder i kraft, men animation-name is fortfarande spin, vilket innebär att ingen animering läggs till eller tas bort. Animationen fortsätter helt enkelt att spelas som om ingenting hade hänt.

Det andra exemplet: "klona animationen"

Ett arbetssätt är att klona animationen:

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

Såhär fungerar det:

  1. animation-name is none initialt.
  2. Användaren klickar på "Snurra!", animation-name blir spin1. Animationen spin1 startas från början eftersom den precis lades till.
  3. Användaren klickar på "Snurra igen!", animation-name blir spin2. Animationen spin2 startas från början eftersom den precis lades till.

Observera att i steg #3, spin1 tas bort på grund av ordningen i CSS-reglerna. Det kommer inte att fungera om "Snurra igen!" kontrolleras först.

Det tredje exemplet: "lägga till samma animation"

Ett annat arbetssätt är att "lägga till samma animation":

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

Detta liknar det föregående exemplet. Du kan faktiskt förstå beteendet så här:

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

Observera att när "Snurra igen!" är markerad, blir den gamla körande animationen den andra animeringen i den nya listan, vilket kan vara ointuitivt. En direkt konsekvens är: tricket fungerar inte om animation-fill-mode is forwards. Här är en demo:

Om du undrar varför detta är fallet, här är några ledtrådar:

  • animation-fill-mode is none som standard, vilket betyder "Animeringen har ingen effekt alls om den inte spelas upp".
  • animation-fill-mode: forwards; betyder "När animationen har spelat klart måste den stanna vid den sista nyckelbildrutan för alltid".
  • spin1s beslut alltid åsidosätta spin2beror på det spin1 visas senare i listan.
  • Anta att användaren klickar på "Snurra!", väntar på ett helt snurr och sedan klickar på "Snurra igen!". I detta ögonblick. spin1 är redan färdig, och spin2 bara börjar.

Diskussion

Tumregel: du kan inte "starta om" en befintlig CSS-animation. Istället vill du lägga till och spela upp en ny animation. Detta kan bekräftas av W3C-specifikationen:

När en animation har startat fortsätter den tills den slutar eller animationsnamnet tas bort.

När jag nu jämför de två sista exemplen tror jag i praktiken att "kloning av animationer" ofta borde fungera bättre, speciellt när CSS-förprocessor är tillgänglig.

Problem 2: Slow Motion

Man kan tro att sakta ner en animation bara är en fråga om att ställa in en längre animation-duration:

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

Det här fungerar faktiskt:

... eller gör det?

Med några justeringar borde det vara lättare att se problemet.

Ja, animeringen saktas ner. Och nej, det ser inte bra ut. Hunden "hoppar" (nästan) alltid när du växlar kryssrutan. Dessutom verkar hunden hoppa till en slumpmässig position snarare än den ursprungliga. Hurså?

Det skulle vara lättare att förstå det om vi introducerade två "skuggelement":

Båda skuggelementen kör samma animationer med olika animation-duration. Och de påverkas inte av kryssrutan.

När du växlar kryssrutan växlar elementet omedelbart mellan tillstånden för två skuggelement.

citerar W3C-specifikationen:

Ändringar av värdena för animeringsegenskaper medan animeringen körs gäller som om animeringen hade dessa värden från när den började.

Detta följer statslösa design, vilket gör det möjligt för webbläsare att enkelt bestämma det animerade värdet. Den faktiska beräkningen beskrivs här. och här..

Ännu ett försök

En idé är att pausa den aktuella animeringen och sedan lägga till en långsammare animering som tar över därifrån:

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 fungerar:

... eller gör det?

Det saktar ner när du klickar på "Slowmo!". Men om du väntar på en hel cirkel kommer du att se ett "hopp". Egentligen hoppar den alltid till positionen när "Slowmo!" klickas på.

Anledningen är att vi inte har en from keyframe definierad – och det borde vi inte. När användaren klickar på "Slowmo!", spin1 är pausad vid någon position, och spin2 börjar i exakt samma position. Vi kan helt enkelt inte förutse den positionen i förväg ... eller kan vi?

En fungerande lösning

Vi kan! Genom att använda en anpassad egenskap kan vi fånga vinkeln i den första animeringen och sedan skicka den till den andra animeringen:

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

Notera: @property används i detta exempel, dvs stöds inte av alla webbläsare.

Den "perfekta" lösningen

Det finns en varning till den tidigare lösningen: "avsluta slowmo" fungerar inte bra.

Här är en bättre lösning:

I den här versionen kan slow motion aktiveras eller avslutas sömlöst. Ingen experimentell funktion används heller. Så är det den perfekta lösningen? Ja och nej.

Den här lösningen fungerar som att "växla" "växlar":

  • Kugghjul: det finns två <div>s. Den ena är den andras förälder. Båda har spin animation men med olika animation-duration. Det slutliga tillståndet för elementet är ackumuleringen av båda animationerna.
  • Skiftning: I början bara en <div> har sin animation igång. Den andra är pausad. När kryssrutan är aktiverad byter båda animationerna status.

Även om jag verkligen gillar resultatet, finns det ett problem: det är ett trevligt utnyttjande av spin animation, som inte fungerar för andra typer av animationer i allmänhet.

En praktisk lösning (med JS)

För allmänna animationer är det möjligt att uppnå slow motion-funktionen med lite JavaScript:

En snabb förklaring:

  • En anpassad egenskap används för att spåra animeringens framsteg.
  • Animeringen "startas om" när kryssrutan är växlad.
  • JS-koden beräknar rätt animation-delay för att säkerställa en sömlös övergång. jag rekomenderar den här artikeln om du inte är bekant med negativa värden på animation-delay.

Du kan se den här lösningen som en hybrid av "att starta om animation" och "växling"-metoden.

Här är det viktigt att spåra animeringens framsteg korrekt. Lösningar är möjliga om @property är inte tillgänglig. Som ett exempel använder denna version z-index för att spåra framstegen:

Sidanteckning: ursprungligen försökte jag också skapa en CSS-version men det lyckades inte. Även om jag inte är 100% säker, tror jag att det beror på animation-delay is inte animerbar.

Här är en version med minimal JavaScript. Endast "att gå in i slowmo" fungerar.

Meddela mig om du lyckas skapa en fungerande CSS-version!

Slow-mo valfri animering (med JS)

Slutligen skulle jag vilja dela en lösning som fungerar för (nästan) alla animationer, även med flera komplicerade @keyframes:

I grund och botten måste du lägga till en animationsförloppsspårare och sedan noggrant beräkna animation-delay för den nya animationen. Men ibland kan det vara knepigt (men möjligt) att få rätt värden.

Till exempel:

  • animation-timing-function är inte linear.
  • animation-direction är inte normal.
  • flera värden i animation-name med olika animation-durations och animation-delayS.

Denna metod beskrivs också här. för Web Animations API.

Erkännanden

Jag började på den här vägen efter att ha stött på CSS-bara projekt. Någon stans delikat konstverk, och några var komplexa tillbehör. Mina favoriter är de som involverar 3D-objekt, till exempel detta studsande boll och detta förpackningskub.

I början hade jag ingen aning om hur dessa gjordes. Senare läste jag och lärde mig mycket av denna trevliga handledning av Ana Tudor.

Det visade sig att bygga och animera 3D-objekt med CSS är inte mycket annorlunda än att göra det med Blandare, bara med lite annorlunda smak.

Slutsats

I den här artikeln undersökte vi beteendet hos CSS-animationer när en animate-* egendomen ändras. Speciellt utarbetade vi lösningar för att "spela om en animation" och "animation slow-mo".

Jag hoppas att du tycker att den här artikeln är intressant. Snälla låt mig få veta vad du tänker!

Tidsstämpel:

Mer från CSS-tricks