Csak CSS-t használó Wolfenstein egy kis projekt, amit néhány hete készítettem. Ez egy kísérlet volt CSS 3D transzformációkkal és animációkkal.
Ihlette FPS demó és egy másik Wolfenstein CodePen, úgy döntöttem, hogy elkészítem a saját verziómat. Lazán az eredeti Wolfenstein 1D-s játék 9. epizódja – 3. emeletén alapul.
Szerkesztő: Ez a játék szándékosan igényel gyors reakciót, hogy elkerülje a Game Over képernyőt.
Itt egy átjátszó videó:
Dióhéjban a projektem nem más, mint egy gondosan megszerkesztett hosszú CSS-animáció. Plusz néhány példa a checkbox hack.
:checked ~ div { animation-name: spin; }
A környezet 3D rácslapokból áll, az animációk pedig többnyire sima 3D fordítások és elforgatások. Semmi igazán divatos.
Két problémát azonban különösen nehéz volt megoldani:
- Játssza le a „fegyverlövés” animációt, amikor a játékos rákattint egy ellenségre.
- Amikor a gyorsan mozgó főnök megkapta az utolsó találatot, írjon be egy drámai lassítást.
Technikai szinten ez a következőket jelentette:
- Játsszon újra egy animációt, ha a következő jelölőnégyzet be van jelölve.
- Lassítsa le az animációt, ha egy jelölőnégyzet be van jelölve.
Valójában egyik sem volt megfelelően megoldva a projektemben! Vagy elkerülő megoldásokat használtam, vagy egyszerűen feladtam.
Másrészt némi ásás után végül megtaláltam mindkét probléma kulcsát: a CSS-animációk futtatásának tulajdonságainak megváltoztatását. Ebben a cikkben részletesebben foglalkozunk ezzel a témával:
- Sok interaktív példa.
- Boncolás: hogyan működnek (vagy nem működnek) az egyes példák?
- A színfalak mögött: hogyan kezelik a böngészők az animációs állapotokat?
Hadd „dobd fel a téglaimat”.
1. probléma: Animáció újrajátszása
Az első példa: „csak egy jelölőnégyzet”
Az első megérzésem az volt, hogy „csak adj hozzá még egy jelölőnégyzetet”, ami nem működik:
Mindegyik jelölőnégyzet külön-külön működik, de együtt nem. Ha az egyik jelölőnégyzet már be van jelölve, a másik már nem működik.
Így működik (vagy „nem működik”):
- A
animation-name
of<div>
isnone
alapértelmezés szerint. - A felhasználó egy jelölőnégyzetre kattint,
animation-name
válikspin
, és az animáció elölről kezdődik. - Egy idő után a felhasználó rákattint a másik jelölőnégyzetre. Egy új CSS-szabály lép életbe, de
animation-name
is még mindigspin
, ami azt jelenti, hogy a rendszer nem ad hozzá animációt és nem távolítja el. Az animáció egyszerűen folytatódik, mintha mi sem történt volna.
A második példa: „animáció klónozása”
Az egyik működő megközelítés az animáció klónozása:
#spin1:checked ~ div { animation-name: spin1; }
#spin2:checked ~ div { animation-name: spin2; }
Itt van, hogyan működik:
animation-name
isnone
alapvetően.- A felhasználó rákattint a „Pörgetés!” gombra,
animation-name
válikspin1
. Az animációspin1
elölről indul, mert most lett hozzáadva. - A felhasználó rákattint a „Pörgetés újra!” gombra,
animation-name
válikspin2
. Az animációspin2
elölről indul, mert most lett hozzáadva.
Vegye figyelembe, hogy a 3. lépésben spin1
a CSS-szabályok sorrendje miatt eltávolítva. Nem fog működni, ha „Pörgessen újra!” először ellenőrzik.
A harmadik példa: „ugyanannak az animációnak a hozzáfűzése”
Egy másik működő megközelítés az „ugyanannak az animációnak a hozzáfűzése”:
#spin1:checked ~ div { animation-name: spin; }
#spin2:checked ~ div { animation-name: spin, spin; }
Ez hasonló az előző példához. Valójában a következőképpen értheti meg a viselkedést:
#spin1:checked ~ div { animation-name: spin1; }
#spin2:checked ~ div { animation-name: spin2, spin1; }
Vegye figyelembe, hogy amikor a „Pörgessen újra!” be van jelölve, akkor a régi futó animáció lesz a második animáció az új listán, ami nem biztos, hogy intuitív. Ennek egyenes következménye: a trükk nem fog működni, ha animation-fill-mode
is forwards
. Íme egy demó:
Ha kíváncsi, miért van ez így, íme néhány támpont:
animation-fill-mode
isnone
alapértelmezés szerint, ami azt jelenti, hogy „Az animációnak nincs hatása, ha nem játszik le”.animation-fill-mode: forwards;
azt jelenti, hogy „Miután az animáció lejátszása befejeződött, örökre az utolsó kulcskockánál kell maradnia”.spin1
döntése mindig felülírjaspin2
azért, mertspin1
később jelenik meg a listában.- Tegyük fel, hogy a felhasználó rákattint a „Pörgetés!” gombra, megvárja a teljes pörgetést, majd rákattint a „Pörgetés újra!” gombra. Ebben a pillanatban.
spin1
már elkészült, ésspin2
csak indul.
Megbeszélés
Ökölszabály: meglévő CSS-animációt nem lehet „újraindítani”. Ehelyett új animációt szeretne hozzáadni és lejátszani. Ezt megerősítheti a W3C spec:
Amint egy animáció elindult, addig folytatódik, amíg véget nem ér, vagy az animáció nevét el nem távolítják.
Az utolsó két példát összehasonlítva azt gondolom, hogy a gyakorlatban az „animációk klónozásának” gyakran jobban kell működnie, különösen, ha elérhető a CSS előfeldolgozó.
2. probléma: Lassítás
Azt gondolhatnánk, hogy az animáció lelassítása csak egy hosszabb beállítás kérdése animation-duration
:
div { animation-duration: 0.5s; }
#slowmo:checked ~ div { animation-duration: 1.5s; }
Valóban, ez működik:
… vagy mégis?
Néhány módosítással könnyebben átláthatja a problémát.
Igen, az animáció le van lassítva. És nem, nem néz ki jól. A kutya (majdnem) mindig „ugrik”, amikor átkapcsolja a jelölőnégyzetet. Továbbá úgy tűnik, hogy a kutya véletlenszerű pozícióba ugrik, nem pedig az eredeti pozícióba. Hogy-hogy?
Könnyebb lenne megérteni, ha bevezetnénk két „árnyékelemet”:
Mindkét árnyékelem ugyanazt az animációt futtatja, eltérően animation-duration
. És nem érinti őket a jelölőnégyzet.
A jelölőnégyzet átkapcsolásával az elem azonnal átvált két árnyékelem állapota között.
idézve a W3C spec:
Az animáció tulajdonságainak értékeinek módosítása az animáció futása közben úgy érvényesül, mintha az animáció kezdete óta rendelkezne ezekkel az értékekkel.
Ez követi a hontalan design, amely lehetővé teszi a böngészők számára, hogy könnyen meghatározzák az animált értéket. A tényleges számítás leírása itt és a itt.
Újabb kísérlet
Az egyik ötlet az, hogy szüneteltesse az aktuális animációt, majd adjon hozzá egy lassabb animációt, amely átveszi onnan:
div {
animation-name: spin1;
animation-duration: 2s;
}
#slowmo:checked ~ div {
animation-name: spin1, spin2;
animation-duration: 2s, 5s;
animation-play-state: paused, running;
}
Tehát működik:
… vagy mégis?
Lelassul, ha rákattint a „Slowmo!” gombra. De ha kivársz egy teljes kört, akkor "ugrást" fogsz látni. Valójában mindig arra a pozícióra ugrik, amikor „Slowmo!” rá van kattintva.
Ennek az az oka, hogy nincs nálunk a from
kulcskocka definiálva – és nem kellene. Amikor a felhasználó a „Slowmo!” gombra kattint, spin1
valamilyen helyen szünetel, és spin2
pontosan ugyanabban a pozícióban kezdődik. Egyszerűen nem tudjuk előre megjósolni ezt a helyzetet… vagy igen?
Működő Megoldás
Tudunk! Egy egyéni tulajdonság használatával rögzíthetjük a szöget az első animációban, majd átadhatjuk a második animációnak:
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);
}
}
Jegyzet: @property
ebben a példában használatos, ami az nem minden böngésző támogatja.
A "tökéletes" megoldás
Van egy figyelmeztetés az előző megoldáshoz: az „exiting slowmo” nem működik jól.
Itt van egy jobb megoldás:
Ebben a verzióban a lassított felvétel zökkenőmentesen be- vagy kiléphet. Nem használnak kísérleti funkciót sem. Szóval ez a tökéletes megoldás? Igen és nem.
Ez a megoldás úgy működik, mint a „fokozatok” „váltása”:
- Fogaskerekek: kettő van
<div>
s. Az egyik a másik szülője. Mindkettőnek megvan aspin
animáció, de másanimation-duration
. Az elem végső állapota mindkét animáció felhalmozódása. - Váltás: Az elején csak egy
<div>
fut az animációja. A másik szünetel. Ha a jelölőnégyzet be van kapcsolva, mindkét animáció felcseréli az állapotát.
Bár nagyon szeretem az eredményt, van egy probléma: ez egy szép kiaknázása a spin
animáció, ami más típusú animációknál általában nem működik.
Praktikus megoldás (JS-vel)
Az általános animációknál a lassított funkció elérhető egy kis JavaScript segítségével:
Gyors magyarázat:
- Egy egyéni tulajdonságot használunk az animáció előrehaladásának nyomon követésére.
- Az animáció „újraindul”, ha a jelölőnégyzet be van kapcsolva.
- A JS kód kiszámítja a helyes értéket
animation-delay
a zökkenőmentes átmenet biztosítása érdekében. ajánlom ezt a cikket ha nem ismeri a negatív értékeketanimation-delay
.
Ezt a megoldást az „újraindító animáció” és a „sebességváltó” megközelítés hibridjeként tekinthetjük.
Itt fontos az animáció előrehaladásának helyes nyomon követése. Megoldások lehetségesek, ha @property
nem elérhető. Példaként ez a verzió használja z-index
a fejlődés nyomon követéséhez:
Mellékes megjegyzés: eredetileg megpróbáltam létrehozni egy csak CSS-verziót is, de nem sikerült. Bár nem 100%-ig biztos, szerintem azért animation-delay
is nem animálható.
Itt van egy verzió minimális JavaScripttel. Csak a „slowmo belépés” működik.
Kérem, jelezze, ha sikerül működőképes CSS-verziót létrehoznia!
Lassított bármilyen animáció (JS-vel)
Végezetül egy olyan megoldást szeretnék megosztani, ami (majdnem) minden animációnál működik, még ha több bonyolult is @keyframes
:
Alapvetően hozzá kell adnia egy animációs folyamatkövetőt, majd gondosan ki kell számolnia animation-delay
az új animációhoz. Néha azonban trükkös lehet (de lehetséges) a helyes értékek meghatározása.
Például:
animation-timing-function
nemlinear
.animation-direction
nemnormal
.- több értéket tartalmaz
animation-name
különbözőanimation-duration
ésanimation-delay
„S.
Ezt a módszert is ismertetjük itt az Web Animations API.
Köszönetnyilvánítás
Ezen az úton indultam el, miután találkoztam a csak CSS-projektekkel. Valahol finom műalkotás, és néhányan az is voltak összetett konstrukciók. A kedvenceim a 3D objektumokat tartalmazók, például ez pattogó labda és ez a csomagoló kocka.
Kezdetben fogalmam sem volt, hogyan készülnek. Később sokat olvastam és tanultam belőle ez a szép oktatóanyag Ana Tudortól.
Mint kiderült, a 3D objektumok CSS-sel való építése és animálása nem sokban különbözik a CSS-sel való készítéstől keverőgép, csak egy kicsit más ízzel.
Következtetés
Ebben a cikkben megvizsgáltuk a CSS-animációk viselkedését, amikor egy animate-*
ingatlan megváltozik. Különösen az „animáció újrajátszására” és az „animáció lassítására” dolgoztunk ki megoldásokat.
Remélem, érdekesnek találja ezt a cikket. Kérem ismertesse velem a gondolatait!