Hacking CSS-Animationsstatus und Wiedergabezeit PlatoBlockchain Data Intelligence. Vertikale Suche. Ai.

Hacken des CSS-Animationsstatus und der Wiedergabezeit

Nur-CSS-Wolfenstein ist ein kleines Projekt, das ich vor ein paar Wochen gemacht habe. Es war ein Experiment mit CSS-3D-Transformationen und -Animationen.

Inspiriert durch die FPS-Demo und ein anderer Wolfenstein CodePen, beschloss ich, meine eigene Version zu bauen. Es basiert lose auf Episode 1 – Etage 9 des ursprünglichen Wolfenstein 3D-Spiels.

Editor: Dieses Spiel erfordert absichtlich eine schnelle Reaktion, um einen Game Over-Bildschirm zu vermeiden.

Hier ist ein Playthrough-Video:

[Eingebetteten Inhalt]

Kurz gesagt, mein Projekt ist nichts anderes als eine sorgfältig geskriptete lange CSS-Animation. Plus ein paar Instanzen der Checkbox-Hack.

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

Die Umgebung besteht aus 3D-Gitterflächen und die Animationen sind meist einfache 3D-Translationen und -Rotationen. Nichts wirklich Besonderes.

Zwei Probleme waren jedoch besonders knifflig zu lösen:

  • Spielen Sie die „Waffe abfeuern“-Animation ab, wenn der Spieler auf einen Feind klickt.
  • Wenn der sich schnell bewegende Boss den letzten Treffer hat, geben Sie eine dramatische Zeitlupe ein.

Auf technischer Ebene bedeutete dies:

  • Wiederholen Sie eine Animation, wenn das nächste Kontrollkästchen aktiviert ist.
  • Eine Animation verlangsamen, wenn ein Kontrollkästchen aktiviert ist.

Tatsächlich wurde beides in meinem Projekt nicht richtig gelöst! Ich habe entweder Workarounds verwendet oder einfach aufgegeben.

Andererseits fand ich nach einigem Suchen schließlich den Schlüssel zu beiden Problemen: das Ändern der Eigenschaften von laufenden CSS-Animationen. In diesem Artikel werden wir uns weiter mit diesem Thema befassen:

  • Viele interaktive Beispiele.
  • Dissektionen: Wie funktioniert jedes Beispiel (oder funktioniert es nicht)?
  • Hinter den Kulissen: Wie gehen Browser mit Animationszuständen um?

Lass mich „Wirf meine Steine“.

Problem 1: Animation wiedergeben

Das erste Beispiel: „nur ein weiteres Kontrollkästchen“

Meine erste Intuition war „einfach ein weiteres Kontrollkästchen hinzufügen“, was nicht funktioniert:

Jedes Kontrollkästchen funktioniert einzeln, aber nicht beide zusammen. Wenn ein Kontrollkästchen bereits aktiviert ist, funktioniert das andere nicht mehr.

So funktioniert es (oder „funktioniert nicht“):

  1. Das animation-name of <div> is none standardmäßig.
  2. Der Benutzer klickt auf ein Kontrollkästchen, animation-name wird spin, und die Animation beginnt von vorne.
  3. Nach einer Weile klickt der Benutzer auf das andere Kontrollkästchen. Eine neue CSS-Regel tritt in Kraft, aber animation-name is Noch spin, was bedeutet, dass keine Animation hinzugefügt oder entfernt wird. Die Animation läuft einfach weiter, als ob nichts passiert wäre.

Das zweite Beispiel: „Klonen der Animation“

Ein funktionierender Ansatz besteht darin, die Animation zu klonen:

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

Hier ist, wie es funktioniert:

  1. animation-name is none anfänglich.
  2. Der Benutzer klickt auf „Spin!“, animation-name wird spin1. Die Animation spin1 wird von vorne begonnen, weil es gerade hinzugefügt wurde.
  3. Der Benutzer klickt auf „Erneut drehen!“, animation-name wird spin2. Die Animation spin2 wird von vorne begonnen, weil es gerade hinzugefügt wurde.

Beachten Sie, dass in Schritt #3, spin1 wird aufgrund der Reihenfolge der CSS-Regeln entfernt. Es funktioniert nicht, wenn „Spin again!“ wird zuerst geprüft.

Das dritte Beispiel: „Anhängen derselben Animation“

Ein weiterer Arbeitsansatz ist das „Anhängen derselben Animation“:

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

Dies ähnelt dem vorherigen Beispiel. Sie können das Verhalten tatsächlich so verstehen:

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

Beachten Sie, dass bei „Erneut drehen!“ aktiviert ist, wird die alte laufende Animation zur zweiten Animation in der neuen Liste, was unintuitiv sein könnte. Eine direkte Konsequenz ist: Der Trick funktioniert nicht, wenn animation-fill-mode is forwards. Hier ist eine Demo:

Wenn Sie sich fragen, warum das so ist, hier sind einige Hinweise:

  • animation-fill-mode is none standardmäßig, was bedeutet „Die Animation hat überhaupt keinen Effekt, wenn sie nicht abgespielt wird“.
  • animation-fill-mode: forwards; bedeutet „Nachdem die Animation abgespielt wurde, muss sie für immer beim letzten Keyframe bleiben“.
  • spin1's Entscheidung immer außer Kraft setzen spin2ist weil spin1 erscheint später in der Liste.
  • Angenommen, der Benutzer klickt auf „Drehen!“, wartet auf eine vollständige Drehung und klickt dann auf „Erneut drehen!“. In diesem Moment. spin1 ist schon fertig, und spin2 fängt gerade an.

Diskussion

Faustregel: Sie können eine bestehende CSS-Animation nicht „neu starten“. Stattdessen möchten Sie eine neue Animation hinzufügen und abspielen. Dies kann durch bestätigt werden die W3C-Spezifikation:

Sobald eine Animation gestartet wurde, wird sie fortgesetzt, bis sie endet oder der Animationsname entfernt wird.

Wenn ich nun die letzten beiden Beispiele vergleiche, denke ich, dass das „Klonen von Animationen“ in der Praxis oft besser funktionieren sollte, insbesondere wenn ein CSS-Präprozessor verfügbar ist.

Problem 2: Zeitlupe

Man könnte denken, dass das Verlangsamen einer Animation nur eine Frage der Einstellung eines längeren ist animation-duration:

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

Tatsächlich funktioniert das:

… oder doch?

Mit ein paar Änderungen sollte es einfacher sein, das Problem zu erkennen.

Ja, die Animation wird verlangsamt. Und nein, es sieht nicht gut aus. Der Hund „springt“ (fast) immer, wenn Sie die Checkbox umschalten. Außerdem scheint der Hund eher zu einer zufälligen Position als zu der ursprünglichen zu springen. Woher?

Es wäre einfacher zu verstehen, wenn wir zwei „Schattenelemente“ einführen würden:

Auf beiden Schattenelementen laufen die gleichen Animationen mit unterschiedlichen animation-duration. Und sie sind von der Checkbox nicht betroffen.

Wenn Sie das Kontrollkästchen umschalten, wechselt das Element einfach sofort zwischen den Zuständen von zwei Schattenelementen.

Zitieren die W3C-Spezifikation:

Änderungen an den Werten von Animationseigenschaften während der Ausführung der Animation werden so angewendet, als ob die Animation diese Werte seit Beginn gehabt hätte.

Dies folgt der staatenlos Design, das es Browsern ermöglicht, den animierten Wert einfach zu bestimmen. Die eigentliche Berechnung wird beschrieben hier und hier.

Ein weiterer Versuch

Eine Idee ist, die aktuelle Animation anzuhalten und dann eine langsamere Animation hinzuzufügen, die von dort aus übernimmt:

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

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

Es funktioniert also:

… oder doch?

Es verlangsamt sich, wenn Sie auf „Slowmo!“ Klicken. Aber wenn Sie auf einen vollen Kreis warten, sehen Sie einen „Sprung“. Eigentlich springt es immer an die Stelle, an der „Slowmo!“ angeklickt wird.

Der Grund ist, dass wir keine haben from Keyframe definiert – und das sollten wir nicht. Wenn der Benutzer auf „Slowmo!“ klickt, spin1 an einer Position angehalten wird, und spin2 beginnt genau an der gleichen Stelle. Wir können diese Position einfach nicht vorhersagen … oder doch?

Eine funktionierende Lösung

Wir können! Durch die Verwendung einer benutzerdefinierten Eigenschaft können wir den Winkel in der ersten Animation erfassen und ihn dann an die zweite Animation übergeben:

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

Hinweis: @property wird in diesem Beispiel verwendet, das heißt nicht von allen Browsern unterstützt.

Die „perfekte“ Lösung

Es gibt eine Einschränkung gegenüber der vorherigen Lösung: „Exiting slowmo“ funktioniert nicht gut.

Hier ist eine bessere Lösung:

In dieser Version kann die Zeitlupe nahtlos eingegeben oder verlassen werden. Es wird auch keine experimentelle Funktion verwendet. Also die perfekte Lösung? Ja und nein.

Diese Lösung funktioniert wie „Schalten“ von „Gängen“:

  • Zahnräder: Es gibt zwei <div>s. Einer ist der Elternteil des anderen. Beide haben die spin Animation, aber mit unterschiedlichen animation-duration. Der Endzustand des Elements ist die Akkumulation beider Animationen.
  • Schalten: Am Anfang nur eins <div> hat seine Animation läuft. Der andere pausiert. Wenn das Kontrollkästchen aktiviert ist, tauschen beide Animationen ihre Zustände.

Obwohl ich das Ergebnis wirklich mag, gibt es ein Problem: Es ist ein netter Exploit des spin Animation, was für andere Arten von Animationen im Allgemeinen nicht funktioniert.

Eine praktische Lösung (mit JS)

Für allgemeine Animationen ist es möglich, die Zeitlupenfunktion mit etwas JavaScript zu erreichen:

Eine kurze Erklärung:

  • Eine benutzerdefinierte Eigenschaft wird verwendet, um den Fortschritt der Animation zu verfolgen.
  • Die Animation wird „neu gestartet“, wenn das Kontrollkästchen umgeschaltet wird.
  • Der JS-Code berechnet das Richtige animation-delay um einen nahtlosen Übergang zu gewährleisten. Ich empfehle Dieser Artikel wenn Sie mit negativen Werten von nicht vertraut sind animation-delay.

Sie können diese Lösung als eine Mischung aus „Animation neu starten“ und dem Ansatz „Schalten“ betrachten.

Hier ist es wichtig, den Animationsfortschritt korrekt zu verfolgen. Problemumgehungen sind möglich, wenn @property ist nicht verfügbar. Als Beispiel verwendet diese Version z-index um den Fortschritt zu verfolgen:

Nebenbemerkung: Ursprünglich habe ich auch versucht, eine Nur-CSS-Version zu erstellen, aber es ist mir nicht gelungen. Obwohl ich nicht 100% sicher bin, denke ich, dass es daran liegt animation-delay is nicht animierbar.

Hier ist eine Version mit minimalem JavaScript. Nur „Eingabe von Slowmo“ funktioniert.

Bitte lassen Sie mich wissen, wenn Sie es schaffen, eine funktionierende Nur-CSS-Version zu erstellen!

Beliebige Animation in Zeitlupe (mit JS)

Zu guter Letzt möchte ich eine Lösung vorstellen, die für (fast) jede Animation funktioniert, auch mit mehreren komplizierten @keyframes:

Grundsätzlich müssen Sie einen Animationsfortschritts-Tracker hinzufügen und dann sorgfältig berechnen animation-delay für die neue Animation. Manchmal kann es jedoch schwierig (aber möglich) sein, die richtigen Werte zu erhalten.

Beispielsweise:

  • animation-timing-function ist nicht linear.
  • animation-direction ist nicht normal.
  • mehrere Werte ein animation-name mit unterschiedlichen animation-durationund animation-delay'S.

Auch diese Methode wird beschrieben hier für die Web-Animations-API.

Anerkennungen

Ich habe diesen Weg eingeschlagen, nachdem ich auf reine CSS-Projekte gestoßen bin. Irgendwo filigranes Kunstwerk, und einige waren komplexe Apparaturen. Meine Favoriten sind zum Beispiel solche mit 3D-Objekten Flummi und dies Verpackungswürfel.

Am Anfang hatte ich keine Ahnung, wie diese hergestellt wurden. Später habe ich viel gelesen und gelernt dieses schöne Tutorial von Ana Tudor.

Wie sich herausstellte, ist das Erstellen und Animieren von 3D-Objekten mit CSS nicht viel anders als mit CSS Mixer, nur mit einem etwas anderen Geschmack.

Zusammenfassung

In diesem Artikel haben wir das Verhalten von CSS-Animationen untersucht, wenn eine animate-* Eigentum verändert wird. Insbesondere haben wir Lösungen für „Wiedergabe einer Animation“ und „Animations-Zeitlupe“ erarbeitet.

Ich hoffe, Sie finden diesen Artikel interessant. Bitte teilen Sie mir Ihre Gedanken mit!

Zeitstempel:

Mehr von CSS-Tricks