Hakowanie stanu animacji CSS i czasu odtwarzania PlatoBlockchain Data Intelligence. Wyszukiwanie pionowe. AI.

Hakowanie stanu animacji CSS i czasu odtwarzania

Tylko CSS Wolfenstein to mały projekt, który zrobiłem kilka tygodni temu. Był to eksperyment z przekształceniami i animacjami CSS 3D.

Zainspirowany Demo FPS i kolejny Wolfenstein CodePen, postanowiłem zbudować własną wersję. Jest luźno oparty na odcinku 1 – 9 piętrze oryginalnej gry Wolfenstein 3D.

Redaktor: Ta gra celowo wymaga szybkiej reakcji, aby uniknąć ekranu Game Over.

Oto film przedstawiający grę:

[Osadzone treści]

W skrócie, mój projekt to nic innego jak starannie oskryptowana długa animacja CSS. Plus kilka przypadków hack pola wyboru.

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

Środowisko składa się z powierzchni siatki 3D, a animacje są w większości zwykłymi translacjami i rotacjami 3D. Nic naprawdę wymyślnego.

Jednak dwa problemy były szczególnie trudne do rozwiązania:

  • Odtwórz animację „wystrzeliwania broni” za każdym razem, gdy gracz kliknie na wroga.
  • Kiedy szybko poruszający się boss dostanie ostatni cios, wejdź w dramatyczne spowolnienie.

Na poziomie technicznym oznaczało to:

  • Odtwórz animację po zaznaczeniu następnego pola wyboru.
  • Spowolnij animację, gdy pole wyboru jest zaznaczone.

W rzeczywistości żaden z nich nie został poprawnie rozwiązany w moim projekcie! Skończyło się na tym, że korzystałem z obejścia lub po prostu się poddałem.

Z drugiej strony, po pewnym kopaniu, w końcu znalazłem klucz do obu problemów: zmianę właściwości uruchamiania animacji CSS. W tym artykule omówimy dalej ten temat:

  • Wiele interaktywnych przykładów.
  • Sekcje: jak działa każdy przykład (lub nie działa)?
  • Za kulisami: jak przeglądarki obsługują stany animacji?

Niech mnie „Rzuć moje cegły”.

Problem 1: Ponowne odtwarzanie animacji

Pierwszy przykład: „tylko kolejne pole wyboru”

Moją pierwszą intuicją było „po prostu dodaj kolejne pole wyboru”, co nie działa:

Każde pole wyboru działa osobno, ale nie oba razem. Jeśli jedno pole wyboru jest już zaznaczone, drugie już nie działa.

Oto jak to działa (lub „nie działa”):

  1. Połączenia animation-name of <div> is none domyślnie.
  2. Użytkownik klika jedno pole wyboru, animation-name staje się spin, a animacja zaczyna się od początku.
  3. Po chwili użytkownik klika drugie pole wyboru. Nowa reguła CSS zaczyna obowiązywać, ale animation-name is nadal spin, co oznacza, że ​​animacja nie jest dodawana ani usuwana. Animacja po prostu kontynuuje odtwarzanie, jakby nic się nie wydarzyło.

Drugi przykład: „klonowanie animacji”

Jednym ze sposobów działania jest sklonowanie animacji:

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

Oto jak to działa:

  1. animation-name is none początkowo.
  2. Użytkownik klika „Zakręć!”, animation-name staje się spin1. Animacja spin1 zaczyna się od początku, ponieważ właśnie został dodany.
  3. Użytkownik klika „Zakręć ponownie!”, animation-name staje się spin2. Animacja spin2 zaczyna się od początku, ponieważ właśnie został dodany.

Zwróć uwagę, że w kroku 3 spin1 jest usuwany ze względu na kolejność reguł CSS. Nie zadziała, jeśli „Zakręć ponownie!” jest sprawdzany jako pierwszy.

Trzeci przykład: „dołączanie tej samej animacji”

Innym podejściem roboczym jest „dołączenie tej samej animacji”:

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

Jest to podobne do poprzedniego przykładu. Możesz właściwie zrozumieć zachowanie w ten sposób:

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

Zauważ, że kiedy „Zakręć ponownie!” jest zaznaczone, stara animacja uruchomiona staje się drugą animacją na nowej liście, co może być nieintuicyjne. Bezpośrednią konsekwencją jest to, że sztuczka nie zadziała, jeśli animation-fill-mode is forwards. Oto demo:

Jeśli zastanawiasz się, dlaczego tak jest, oto kilka wskazówek:

  • animation-fill-mode is none domyślnie, co oznacza „Animacja nie ma żadnego efektu, jeśli nie jest odtwarzana”.
  • animation-fill-mode: forwards; oznacza „Po zakończeniu odtwarzania animacji musi pozostać na zawsze w ostatniej klatce kluczowej”.
  • spin1's decyzja zawsze ma pierwszeństwo spin2ponieważ spin1 pojawia się później na liście.
  • Załóżmy, że użytkownik klika „Zakręć!”, czeka na pełne zakręcenie, a następnie klika „Zakręć ponownie!”. W tym momencie. spin1 jest już skończony i spin2 dopiero się zaczyna.

Dyskusja

Ogólna zasada: nie można „zrestartować” istniejącej animacji CSS. Zamiast tego chcesz dodać i odtworzyć nową animację. Może to potwierdzić specyfikacja W3C:

Raz rozpoczęta animacja trwa do końca lub do usunięcia nazwy animacji.

Teraz porównując dwa ostatnie przykłady, myślę, że w praktyce „klonowanie animacji” powinno często działać lepiej, zwłaszcza gdy dostępny jest preprocesor CSS.

Problem 2: Zwolnione tempo

Można by pomyśleć, że spowolnienie animacji to tylko kwestia ustawienia dłuższego animation-duration:

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

Rzeczywiście, to działa:

… czy to prawda?

Po kilku poprawkach łatwiej będzie zobaczyć problem.

Tak, animacja jest spowolniona. I nie, to nie wygląda dobrze. Pies (prawie) zawsze „podskakuje” po przełączeniu pola wyboru. Co więcej, pies wydaje się skakać do przypadkowej pozycji, a nie początkowej. Dlaczego?

Łatwiej byłoby to zrozumieć, gdybyśmy wprowadzili dwa „elementy cienia”:

Oba elementy cienia uruchamiają te same animacje z różnymi animation-duration. Pole wyboru nie ma na nie wpływu.

Po przełączeniu pola wyboru element natychmiast przełącza się między stanami dwóch elementów cienia.

Cytowanie specyfikacja W3C:

Zmiany wartości właściwości animacji, gdy animacja jest uruchomiona, są stosowane tak, jakby animacja miała te wartości od momentu jej rozpoczęcia.

Wynika z tego bezpaństwowiec projekt, który umożliwia przeglądarkom łatwe określenie animowanej wartości. Opisano rzeczywiste obliczenia tutaj i tutaj.

Kolejna próba

Jednym z pomysłów jest wstrzymanie bieżącej animacji, a następnie dodanie wolniejszej animacji, która zastąpi ją:

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

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

Więc to działa:

… czy to prawda?

Spowalnia po kliknięciu „Slowmo!”. Ale jeśli poczekasz na pełne koło, zobaczysz „skok”. Właściwie zawsze przeskakuje do pozycji, gdy „Slowmo!” jest kliknięty.

Powodem jest to, że nie mamy from zdefiniowana klatka kluczowa – a nie powinniśmy. Gdy użytkownik kliknie „Slowmo!”, spin1 jest zatrzymany w jakiejś pozycji i spin2 zaczyna się dokładnie w tej samej pozycji. Po prostu nie możemy z góry przewidzieć tej pozycji… czy możemy?

Działające rozwiązanie

Możemy! Używając niestandardowej właściwości, możemy przechwycić kąt w pierwszej animacji, a następnie przekazać go do drugiej animacji:

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

Uwaga: @property jest używany w tym przykładzie, czyli nie obsługiwane przez wszystkie przeglądarki.

„Doskonałe” rozwiązanie

Jest zastrzeżenie do poprzedniego rozwiązania: „wychodzenie ze slowmo” nie działa dobrze.

Oto lepsze rozwiązanie:

W tej wersji można płynnie wchodzić i wychodzić ze zwolnionego tempa. Nie jest też używana żadna funkcja eksperymentalna. Czy to idealne rozwiązanie? Tak i nie.

To rozwiązanie działa jak „przesuwanie” „biegów”:

  • Koła zębate: są dwa <div>s. Jeden jest rodzicem drugiego. Oba mają spin animacja, ale z inną animation-duration. Ostatecznym stanem elementu jest kumulacja obu animacji.
  • Przesunięcie: na początku tylko jeden <div> ma uruchomioną animację. Drugi jest wstrzymany. Gdy pole wyboru jest włączone, obie animacje zamieniają się stanami.

Chociaż bardzo podoba mi się wynik, jest jeden problem: jest to fajny eksploatacja spin animacja, która ogólnie nie działa w przypadku innych typów animacji.

Praktyczne rozwiązanie (z JS)

W przypadku ogólnych animacji możliwe jest uzyskanie funkcji slow motion za pomocą odrobiny JavaScript:

Szybkie wyjaśnienie:

  • Właściwość niestandardowa służy do śledzenia postępu animacji.
  • Animacja jest „ponownie uruchamiana” po przełączeniu pola wyboru.
  • Kod JS oblicza poprawne animation-delay aby zapewnić płynne przejście. polecam ten artykuł jeśli nie znasz ujemnych wartości animation-delay.

Możesz postrzegać to rozwiązanie jako hybrydę „animacji ponownego uruchamiania” i podejścia „zmiany biegów”.

Tutaj ważne jest prawidłowe śledzenie postępu animacji. Obejścia są możliwe, jeśli @property jest niedostępne. Jako przykład ta wersja używa z-index aby śledzić postępy:

Uwaga na marginesie: początkowo próbowałem również stworzyć wersję tylko w CSS, ale nie udało mi się. Chociaż nie jestem w 100% pewien, myślę, że to dlatego, że animation-delay is nie można animować.

Oto wersja z minimalnym JavaScriptem. Działa tylko „wchodzenie w slowmo”.

Daj mi znać, jeśli uda Ci się stworzyć działającą wersję tylko dla CSS!

Dowolna animacja w zwolnionym tempie (z JS)

Na koniec chciałbym udostępnić rozwiązanie, które działa dla (prawie) każdej animacji, nawet z wieloma skomplikowanymi @keyframes:

Zasadniczo musisz dodać śledzenie postępu animacji, a następnie dokładnie obliczyć animation-delay dla nowej animacji. Czasami jednak uzyskanie prawidłowych wartości może być trudne (ale możliwe).

Na przykład:

  • animation-timing-function nie jest linear.
  • animation-direction nie jest normal.
  • wiele wartości w animation-name z różnych animation-durationi animation-delay.

Ta metoda jest również opisana tutaj dla Interfejs API animacji internetowych.

Podziękowanie

Zacząłem podążać tą ścieżką po napotkaniu projektów tylko CSS. Gdzieś delikatna grafika, a niektóre były skomplikowane urządzenia. Moje ulubione to te, które dotyczą obiektów 3D, na przykład ten odbijając piłkę i to kostka pakująca.

Na początku nie miałem pojęcia, jak to się robi. Później dużo czytałem i wiele się nauczyłem ten fajny samouczek autorstwa Any Tudor.

Jak się okazało, budowanie i animowanie obiektów 3D za pomocą CSS nie różni się zbytnio od robienia tego za pomocą mikser, tylko o nieco innym smaku.

Wnioski

W tym artykule zbadaliśmy zachowanie animacji CSS, gdy animate-* właściwość jest zmieniona. W szczególności opracowaliśmy rozwiązania dotyczące „odtwarzania animacji” i „animacji w zwolnionym tempie”.

Mam nadzieję, że ten artykuł Cię zainteresuje. Proszę, podziel się swoimi myślami!

Znak czasu:

Więcej z Sztuczki CSS