Hacken van CSS-animatiestatus en afspeeltijd PlatoBlockchain Data Intelligence. Verticaal zoeken. Ai.

Hacken van CSS-animatiestatus en afspeeltijd

Alleen CSS Wolfenstein is een klein project dat ik een paar weken geleden heb gemaakt. Het was een experiment met CSS 3D-transformaties en animaties.

Geรฏnspireerd door de FPS-demo en een ander Wolfenstein CodePen, besloot ik mijn eigen versie te bouwen. Het is losjes gebaseerd op Episode 1 โ€“ Floor 9 van de originele Wolfenstein 3D-game.

Editor: dit spel vereist opzettelijk een snelle reactie om een โ€‹โ€‹Game Over-scherm te vermijden.

Hier is een afspeelvideo:

[Ingesloten inhoud]

In een notendop, mijn project is niets anders dan een zorgvuldig gescripte lange CSS-animatie. Plus een paar voorbeelden van de checkbox-hack.

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

De omgeving bestaat uit 3D-rastervlakken en de animaties zijn meestal eenvoudige 3D-vertalingen en -rotaties. Niets echt deftigs.

Twee problemen waren echter bijzonder lastig op te lossen:

  • Speel de "wapenvuren" -animatie af wanneer de speler op een vijand klikt.
  • Wanneer de snel bewegende baas de laatste treffer kreeg, voer je een dramatische slow motion in.

Op technisch vlak betekende dit:

  • Speel een animatie opnieuw af wanneer het volgende selectievakje is aangevinkt.
  • Een animatie vertragen wanneer een selectievakje is aangevinkt.

In feite werd geen van beide goed opgelost in mijn project! Ik heb uiteindelijk een tijdelijke oplossing gebruikt of heb het gewoon opgegeven.

Aan de andere kant, na wat speurwerk, vond ik uiteindelijk de sleutel tot beide problemen: het wijzigen van de eigenschappen van het uitvoeren van CSS-animaties. In dit artikel gaan we verder op dit onderwerp in:

  • Veel interactieve voorbeelden.
  • Dissecties: hoe werkt elk voorbeeld (of niet)?
  • Achter de schermen: hoe gaan browsers om met animatiestatussen?

Laat me "gooi mijn stenen".

Probleem 1: Animatie opnieuw afspelen

Het eerste voorbeeld: โ€œgewoon weer een checkboxโ€

Mijn eerste intuรฏtie was "voeg gewoon nog een selectievakje toe", wat niet werkt:

Elk selectievakje werkt afzonderlijk, maar niet beide samen. Als het ene selectievakje al is aangevinkt, werkt het andere niet meer.

Zo werkt het (of "werkt niet"):

  1. De animation-name of <div> is none standaard.
  2. De gebruiker klikt op een selectievakje, animation-name wordt spin, en de animatie begint vanaf het begin.
  3. Na een tijdje klikt de gebruiker op het andere selectievakje. Een nieuwe CSS-regel wordt van kracht, maar animation-name is nog spin, wat betekent dat er geen animatie wordt toegevoegd of verwijderd. De animatie blijft gewoon spelen alsof er niets is gebeurd.

Het tweede voorbeeld: โ€œklonen van de animatieโ€

Een werkmethode is om de animatie te klonen:

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

Hier is hoe het werkt:

  1. animation-name is none in eerste instantie.
  2. De gebruiker klikt op "Spin!", animation-name wordt spin1. De animatie spin1 is vanaf het begin gestart omdat het zojuist is toegevoegd.
  3. De gebruiker klikt op "Spin nogmaals!", animation-name wordt spin2. De animatie spin2 is vanaf het begin gestart omdat het zojuist is toegevoegd.

Merk op dat in stap #3, spin1 wordt verwijderd vanwege de volgorde van de CSS-regels. Het zal niet werken als "Spin nogmaals!" wordt eerst gecontroleerd.

Het derde voorbeeld: "dezelfde animatie toevoegen"

Een andere manier van werken is om "dezelfde animatie toe te voegen":

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

Dit is vergelijkbaar met het vorige voorbeeld. Je kunt het gedrag eigenlijk op deze manier begrijpen:

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

Houd er rekening mee dat wanneer "Spin opnieuw!" is aangevinkt, wordt de oude actieve animatie de tweede animatie in de nieuwe lijst, die niet intuรฏtief kan zijn. Een direct gevolg is: de truc werkt niet als animation-fill-mode is forwards. Hier is een demo:

Als je je afvraagt โ€‹โ€‹waarom dit het geval is, zijn hier enkele aanwijzingen:

  • animation-fill-mode is none standaard, wat betekent "De animatie heeft helemaal geen effect als hij niet wordt afgespeeld".
  • animation-fill-mode: forwards; betekent "Nadat de animatie klaar is met spelen, moet deze voor altijd op het laatste keyframe blijven".
  • spin1's beslissing altijd overschrijven spin2is omdat spin1 verschijnt later in de lijst.
  • Stel dat de gebruiker op "Spin!" klikt, wacht op een volledige draai en vervolgens op "Spin opnieuw!" klikt. Op dit moment. spin1 is al klaar, en spin2 begint gewoon.

Discussie

Vuistregel: u kunt een bestaande CSS-animatie niet "herstarten". In plaats daarvan wilt u een nieuwe animatie toevoegen en afspelen. Dit kan worden bevestigd door: de W3C-specificatie:

Als een animatie eenmaal is gestart, gaat deze door totdat deze eindigt of de animatienaam wordt verwijderd.

Nu ik de laatste twee voorbeelden vergelijk, denk ik dat in de praktijk "kloonanimaties" vaak beter zouden moeten werken, vooral wanneer CSS-preprocessor beschikbaar is.

Probleem 2: Slow Motion

Je zou kunnen denken dat het vertragen van een animatie gewoon een kwestie is van het instellen van een langere animation-duration:

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

Dit werkt inderdaad:

โ€ฆ of toch?

Met een paar aanpassingen zou het gemakkelijker moeten zijn om het probleem te zien.

Ja, de animatie is vertraagd. En nee, het ziet er niet goed uit. De hond "springt" (bijna) altijd als je het selectievakje inschakelt. Bovendien lijkt de hond naar een willekeurige positie te springen in plaats van naar de eerste. Hoe kan dat?

Het zou gemakkelijker zijn om het te begrijpen als we twee "schaduwelementen" zouden introduceren:

Beide schaduwelementen voeren dezelfde animaties uit met verschillende animation-duration. En ze worden niet beรฏnvloed door het selectievakje.

Wanneer u het selectievakje inschakelt, schakelt het element onmiddellijk tussen de toestanden van twee schaduwelementen.

citeren de W3C-specificatie:

Wijzigingen in de waarden van animatie-eigenschappen terwijl de animatie wordt uitgevoerd, zijn van toepassing alsof de animatie die waarden had vanaf het begin.

Dit volgt de staatloze design, waarmee browsers eenvoudig de geanimeerde waarde kunnen bepalen. De eigenlijke berekening wordt beschreven hier en hier.

Nog een poging

Een idee is om de huidige animatie te pauzeren en vervolgens een langzamere animatie toe te voegen die het vanaf daar overneemt:

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

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

Het werkt dus:

โ€ฆ of toch?

Het vertraagt โ€‹โ€‹wel als je op "Slowmo!" klikt. Maar als je wacht op een volledige cirkel, zie je een "sprong". Eigenlijk springt het altijd naar de positie wanneer "Slowmo!" wordt aangeklikt.

De reden is dat we geen from keyframe gedefinieerd - en dat zouden we niet moeten doen. Wanneer de gebruiker op โ€œSlowmo!โ€ klikt, spin1 is op een bepaalde positie gepauzeerd, en spin2 begint op precies dezelfde positie. Die positie kunnen we gewoon niet op voorhand voorspellenโ€ฆ of wel?

Een werkende oplossing

Wij kunnen! Door een aangepaste eigenschap te gebruiken, kunnen we de hoek in de eerste animatie vastleggen en deze vervolgens doorgeven aan de tweede animatie:

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

Opmerking: @property wordt in dit voorbeeld gebruikt, namelijk: niet door alle browsers ondersteund.

De "perfecte" oplossing

Er is een voorbehoud bij de vorige oplossing: "slowmo afsluiten" werkt niet goed.

Hier is een betere oplossing:

In deze versie kan slow motion naadloos worden ingevoerd of verlaten. Er wordt ook geen experimentele functie gebruikt. Dus is het de perfecte oplossing? Ja en nee.

Deze oplossing werkt als het "verschuiven" van "versnellingen":

  • Versnellingen: er zijn twee <div>s. De een is de ouder van de ander. Beide hebben de spin animatie maar met verschillende animation-duration. De uiteindelijke toestand van het element is de accumulatie van beide animaties.
  • Schakelen: in het begin maar รฉรฉn <div> heeft zijn animatie draaien. De ander staat stil. Wanneer het selectievakje is ingeschakeld, wisselen beide animaties van status.

Hoewel ik het resultaat erg mooi vind, is er รฉรฉn probleem: het is een mooie exploit van de spin animatie, die over het algemeen niet werkt voor andere soorten animaties.

Een praktische oplossing (met JS)

Voor algemene animaties is het mogelijk om de slow motion-functie te bereiken met een beetje JavaScript:

Een korte uitleg:

  • Een aangepaste eigenschap wordt gebruikt om de voortgang van de animatie bij te houden.
  • De animatie wordt "opnieuw gestart" wanneer het selectievakje wordt ingeschakeld.
  • De JS-code berekent de juiste animation-delay om een โ€‹โ€‹naadloze overgang te garanderen. ik raad aan dit artikel als u niet bekend bent met negatieve waarden van animation-delay.

Je kunt deze oplossing zien als een hybride van "restarting animation" en de "gear-shifting"-aanpak.

Hier is het belangrijk om de voortgang van de animatie correct bij te houden. Tijdelijke oplossingen zijn mogelijk als: @property is niet beschikbaar. Als voorbeeld gebruikt deze versie z-index om de voortgang te volgen:

Kanttekening: oorspronkelijk heb ik ook geprobeerd om een โ€‹โ€‹CSS-only versie te maken, maar dat is niet gelukt. Hoewel ik het niet 100% zeker weet, denk ik dat het komt omdat animation-delay is niet animeerbaar.

Hier is een versie met minimaal JavaScript. Alleen "slowmo invoeren" werkt.

Laat het me weten als het je lukt om een โ€‹โ€‹werkende CSS-only versie te maken!

Slow-mo Elke animatie (met JS)

Ten slotte wil ik een oplossing delen die werkt voor (bijna) elke animatie, zelfs met meerdere gecompliceerde @keyframes:

Kortom, u moet een voortgangstracker voor animaties toevoegen en vervolgens zorgvuldig berekenen animation-delay voor de nieuwe animatie. Soms kan het echter lastig (maar mogelijk) zijn om de juiste waarden te krijgen.

Bijvoorbeeld:

  • animation-timing-function is niet linear.
  • animation-direction is niet normal.
  • meerdere waarden in animation-name met verschillende animation-durationen animation-delay'S.

Deze methode wordt ook beschreven hier voor de API voor webanimaties.

Dankwoord

Ik begon dit pad na het tegenkomen van CSS-only projecten. Sommigen waren delicaat kunstwerk, en sommigen waren complexe constructies. Mijn favorieten zijn die met 3D-objecten, bijvoorbeeld dit stuiterende bal en dit verpakking kubus.

In het begin had ik geen idee hoe deze gemaakt werden. Later heb ik veel gelezen en geleerd van deze leuke tutorial van Ana Tudor.

Het bleek dat het bouwen en animeren van 3D-objecten met CSS niet veel verschilt van het doen met Blenders, alleen met een iets andere smaak.

Conclusie

In dit artikel hebben we het gedrag van CSS-animaties onderzocht wanneer een animate-* eigendom wordt gewijzigd. Met name hebben we oplossingen uitgewerkt voor โ€œreplay an animatieโ€ en โ€œanimatie slow-moโ€.

Ik hoop dat je dit artikel interessant vindt. Laat me weten hoe je er over denkt!

Tijdstempel:

Meer van CSS-trucs