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:
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"):
- De
animation-name
of<div>
isnone
standaard. - De gebruiker klikt op een selectievakje,
animation-name
wordtspin
, en de animatie begint vanaf het begin. - Na een tijdje klikt de gebruiker op het andere selectievakje. Een nieuwe CSS-regel wordt van kracht, maar
animation-name
is nogspin
, 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:
animation-name
isnone
in eerste instantie.- De gebruiker klikt op "Spin!",
animation-name
wordtspin1
. De animatiespin1
is vanaf het begin gestart omdat het zojuist is toegevoegd. - De gebruiker klikt op "Spin nogmaals!",
animation-name
wordtspin2
. De animatiespin2
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
isnone
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 overschrijvenspin2
is omdatspin1
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, enspin2
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 despin
animatie maar met verschillendeanimation-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 vananimation-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 nietlinear
.animation-direction
is nietnormal
.- meerdere waarden in
animation-name
met verschillendeanimation-duration
enanimation-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!