Bei komplexen CSS-Animationen gibt es eine Tendenz, umfangreiche @keyframes mit vielen Deklarationen zu erstellen. Es gibt jedoch ein paar Tricks, über die ich sprechen möchte, die Dinge erleichtern können, während wir bei Vanilla CSS bleiben.
- Mehrere Animationen
- Timing-Funktionen
Der erste Trick ist weiter verbreitet und vertrauter, der zweite ist weniger gebräuchlich. Dafür könnte es gute Gründe geben – das Verketten von Animationen mit Kommas ist relativ einfacher, als die verschiedenen verfügbaren Timing-Funktionen und ihre Wirkungsweise zu verstehen. Es gibt eine besonders clevere Timing-Funktion, die uns die volle Kontrolle gibt, um benutzerdefinierte Timing-Funktionen zu erstellen. Das wäre cubic-bezier(), und in diesem Beitrag zeige ich Ihnen die Leistungsfähigkeit davon und wie sie verwendet werden kann, um schicke Animationen ohne zu viel Komplexität zu erstellen.
Beginnen wir mit einem einfachen Beispiel, das zeigt, wie wir einen Ball in interessanten Richtungen bewegen können, wie eine Unendlichkeitsform (∞).
Wie Sie sehen, gibt es keinen komplexen Code – nur zwei Keyframes und eine cubic-bezier() Funktion. Und doch erhalten wir eine ziemlich komplex aussehende finale Unendlichkeitsform-Animation.
Cool, oder? Tauchen wir ein!
Die cubic-bezier() Funktion
Beginnen wir mit der offiziellen Definition
Eine kubische Bézier-Easing-Funktion ist eine Art von Easing-Funktion, die durch vier reelle Zahlen definiert wird, die die beiden Kontrollpunkte, P1 und P2, einer kubischen Bézier-Kurve spezifizieren, deren Endpunkte P0 und P3 fest bei (0, 0) bzw. (1, 1) liegen. Die x-Koordinaten von P1 und P2 sind auf den Bereich [0, 1] beschränkt.
Die obige Kurve definiert, wie das Ergebnis (y-Achse) in Bezug auf die Zeit (x-Achse) verläuft. Jede Achse hat einen Bereich von [0, 1] (oder [0% 100%]). Wenn wir eine Animation haben, die zwei Sekunden (2s) dauert, dann
0 (0%) = 0s
1 (100%) = 2s
Wenn wir left von 5px auf 20px animieren wollen, dann
0 (0%) = 5px
1 (100%) = 20px
X, die Zeit, ist immer auf [0 1] beschränkt; Y, das Ergebnis, kann jedoch über [0 1] hinausgehen.
Mein Ziel ist es, P1 und P2 anzupassen, um die folgenden Kurven zu erstellen


Sie denken vielleicht, dass dies unmöglich zu erreichen ist, denn wie in der Definition angegeben, sind P0 und P3 bei (0,0) und (1,1) fixiert, was bedeutet, dass sie nicht auf derselben Achse liegen können. Das ist wahr, und wir werden einige mathematische Tricks verwenden, um sie zu "approximieren".
Parabel-Kurve
Beginnen wir mit der folgenden Definition: cubic-bezier(0,1.5,1,1.5). Das ergibt uns die folgende Kurve

cubic-bezier(0,1.5,1,1.5)Unser Ziel ist es, (1,1) zu verschieben und es auf (0,1) zu setzen, was technisch nicht möglich ist. Also werden wir versuchen, es vorzutäuschen.
Wir haben bereits gesagt, dass unser Bereich [0 1] (oder [0% 100%]) ist. Stellen wir uns also den Fall vor, wenn 0% sehr nahe an 100% liegt. Wenn wir zum Beispiel top von 20px (0%) auf 20.1px (100%) animieren wollen, dann können wir sagen, dass sowohl der Anfangs- als auch der Endzustand gleich sind.
Hm, aber unser Element wird sich gar nicht bewegen, oder?
Nun, es wird sich ein wenig bewegen, weil der Y-Wert 20.1px (100%) überschreitet. Aber das reicht nicht für eine wahrnehmbare Bewegung.
Aktualisieren wir die Kurve und verwenden stattdessen cubic-bezier(0,4,1,4). Beachten Sie, wie unsere Kurve viel höher ist als zuvor.

Aber immer noch keine Bewegung – auch wenn der Top-Wert über 3 (oder 300%) liegt. Versuchen wir cubic-bezier(0,20,1,20).

Ja! Es begann sich ein wenig zu bewegen. Haben Sie die Entwicklung der Kurve jedes Mal bemerkt, wenn wir den Wert erhöhen? Sie bringt unseren Punkt (1,1) "visuell" näher an (0,1), wenn wir herauszoomen, um die gesamte Kurve zu sehen, und das ist der Trick.
Durch die Verwendung von cubic-bezier(0,V,1,V), wobei V ein sehr großer Wert ist und sowohl der Anfangs- als auch der Endzustand sehr nahe beieinander liegen (oder fast gleich sind), können wir die Parabel-Kurve simulieren.
Ein Beispiel ist tausend Worte wert.
Ich habe die "magische" Cubic-Bezier-Funktion dort auf die top-Animation angewendet, plus eine lineare, die auf left angewendet wird. Das gibt uns die gewünschte Kurve.
Die Mathematik im Detail
Für diejenigen unter Ihnen, die mathematisch veranlagt sind, können wir diese Erklärung weiter aufschlüsseln. Eine kubische Bézier-Kurve kann mit der folgenden Formel definiert werden:
P = (1−t)³P0 + 3(1−t)²tP1 + 3(1−t)t²P2 + t³P3
Jeder Punkt ist wie folgt definiert: P0 = (0,0), P1 = (0,V), P2 = (1,V) und P3 = (1,1).
Dies ergibt uns die beiden Funktionen für x- und y-Koordinaten:
X(t) = 3(1−t)t² + t³ = 3t² - 2t³Y(t) = 3(1−t)²tV +3(1−t)t²V + t³ = t³ - 3Vt² + 3Vt
V ist unser großer Wert und t liegt im Bereich [0 1]. Wenn wir unser vorheriges Beispiel betrachten, gibt uns Y(t) den Wert von top, während X(t) den Fortschritt der Zeit darstellt. Die Punkte (X(t),Y(t)) definieren dann unsere Kurve.
Finden wir den Maximalwert von Y(t). Dazu müssen wir den Wert von t finden, der Y'(t) = 0 ergibt (wenn die Ableitung gleich 0 ist).
Y'(t) = 3t² - 6Vt + 3V
Y'(t) = 0 ist eine quadratische Gleichung. Ich werde den langweiligen Teil überspringen und Ihnen das Ergebnis geben, das lautet t = V - sqrt(V² - V).
Wenn V ein großer Wert ist, ist t gleich 0.5. Also ist Y(0.5) = Max und X(0.5) ist gleich 0.5. Das bedeutet, wir erreichen den Maximalwert auf halbem Weg der Animation, was der gewünschten Parabel-Kurve entspricht.
Außerdem ergibt Y(0.5) (1 + 6V)/8, und das ermöglicht uns, den Maximalwert basierend auf V zu finden. Da wir für V immer einen großen Wert verwenden, können wir vereinfachen zu 6V/8 = 0.75V.
Wir haben im letzten Beispiel V = 500 verwendet, also wäre der Maximalwert dort 375 (oder 37500%) und wir erhalten Folgendes:
- Anfangszustand (
0):top: 200px - Endzustand (
1):top: 199.5px
Es gibt einen Unterschied von -0.5px zwischen 0 und 1. Nennen wir es den Inkrement. Für 375 (oder 37500%) haben wir eine Gleichung von 375*-0.5px = -187.5px. Unser animiertes Element erreicht top: 12.5px (200px - 187.5px) und ergibt die folgende Animation.
top: 200px (at 0% of the time ) → top: 12.5px (at 50% of the time) → top: 199.5px (at 100% of the time)
Oder anders ausgedrückt:
top: 200px (at 0%) → top: 12.5px (at 50%) → top: 200px (at 100%)
Machen wir die umgekehrte Logik. Welchen Wert von V sollten wir verwenden, um unser Element top: 0px erreichen zu lassen? Die Animation ist 200px → 0px → 199.5px, also brauchen wir -200px, um 0px zu erreichen. Unser Inkrement ist immer gleich -0.5px. Der Maximalwert ist gleich 200/0.5 = 400, also 0.75V = 400, was bedeutet V = 533.33.
Unser Element berührt die Oberseite!
Hier ist eine Grafik, die diese gerade durchgeführte Mathematik zusammenfasst.

Sinus-Kurve
Wir werden fast den gleichen Trick verwenden, um eine Sinus-Kurve zu erstellen, aber mit einer anderen Formel. Diesmal verwenden wir cubic-bezier(0.5,V,0.5,-V).
Wie zuvor, sehen wir uns an, wie sich die Kurve entwickelt, wenn wir den Wert erhöhen.

Ich denke, Sie haben die Idee jetzt wahrscheinlich verstanden. Die Verwendung eines großen Wertes für V bringt uns nahe an eine Sinus-Kurve.
Hier ist eine weitere mit einer kontinuierlichen Animation – eine echte Sinus-Animation!
Die Mathematik
Lassen Sie uns in die Mathematik für diese einsteigen! Gemäß derselben Formel wie zuvor erhalten wir die folgenden Funktionen:
X(t) = 3/2(1−t)²t + 3/2(1−t)t² + t³ = (3/2)t - (3/2)t² + t³Y(t) = 3(1−t)²tV - 3(1−t)t²V + t³ = (6V + 1)t³ - 9Vt² + 3Vt
Diesmal müssen wir die Minimal- und Maximalwerte für Y(t) finden. Y'(t) = 0 ergibt zwei Lösungen. Nach der Lösung erhalten wir
Y'(t) = 3(6V + 1)t² - 18Vt + 3V = 0
…erhalten wir
t' = (3V + sqrt(3V² - V))/(6V + 1)t''= (3V - sqrt(3V² - V))/(6V + 1)
Für einen großen Wert von V haben wir t'=0.211 und t"=0.789. Das bedeutet, dass Y(0.211) = Max und Y(0.789) = Min. Das bedeutet auch, dass X(0.211) = 0.26 und X(0.789) = 0.74. Mit anderen Worten, wir erreichen das Maximum nach 26% der Zeit und das Minimum nach 74% der Zeit.
Y(0.211) ist gleich 0.289V und Y(0.789) gleich -0.289V. Diese Werte haben wir mit einigen Rundungen erhalten, da V sehr groß ist.
Unsere Sinus-Kurve sollte auch die x-Achse (oder Y(t) = 0) zur Hälfte der Zeit (oder X(t) = 0.5) schneiden. Um dies zu beweisen, verwenden wir die zweite Ableitung von Y(t) – die gleich 0 sein sollte –, also Y''(t) = 0.
Y''(t) = 6(6V + 1)t - 18V = 0
Die Lösung ist 3V/(6V + 1), und für einen großen V-Wert ist die Lösung 0.5. Das ergibt Y(0.5) = 0 und X(0.5) = 0.5, was bestätigt, dass unsere Kurve den Punkt (0.5,0) schneidet.
Betrachten wir nun das vorherige Beispiel und versuchen wir, den Wert von V zu finden, der uns wieder zu top: 0% bringt. Wir haben
- Anfangszustand (
0):top: 50% - Endzustand (
1):top: 49.9% - Inkrement:
-0.1%
Wir brauchen -50%, um top: 0% zu erreichen, also 0.289V*-0.1% = -50%, was V = 1730.10 ergibt.
Wie Sie sehen, berührt unser Element die Oberseite und verschwindet am unteren Rand, da wir die folgende Animation haben.
top: 50% → top: 0% → top: 50% → top: 100% → top: 50% → and so on ...
Eine Grafik zur Zusammenfassung der Berechnung.

Und ein Beispiel, das alle Kurven zusammen illustriert.
Ja, Sie sehen vier Kurven! Wenn Sie genau hinschauen, werden Sie feststellen, dass ich zwei verschiedene Animationen verwende, eine, die zu 49.9% geht (ein Inkrement von -0.01%) und eine andere, die zu 50.1% geht (ein Inkrement von +0.01%). Durch Ändern des Vorzeichens des Inkrements steuern wir die Richtung der Kurve. Wir können auch die anderen Parameter der kubischen Bézier-Kurve (nicht die V, die ein großer Wert bleiben sollte) ändern, um weitere Variationen aus denselben Kurven zu erstellen.
Und unten eine interaktive Demo.
Zurück zu unserem Beispiel
Kehren wir zu unserem ursprünglichen Beispiel eines Balls zurück, der sich in Form eines Unendlichkeitssymbols bewegt. Ich habe einfach zwei Sinus-Animationen kombiniert, um es funktionieren zu lassen.
Wenn wir das, was wir zuvor getan haben, mit dem Konzept mehrerer Animationen kombinieren, können wir erstaunliche Ergebnisse erzielen. Hier ist noch einmal das ursprüngliche Beispiel, diesmal als interaktive Demo. Ändern Sie die Werte und sehen Sie den Zauber.
Gehen wir weiter und fügen ein wenig CSS Houdini hinzu. Wir können eine komplexe Transform-Deklaration dank @property animieren (aber CSS Houdini wird derzeit nur in Chrome und Edge unterstützt).
Welche Art von Zeichnungen können Sie damit erstellen? Hier sind einige, die ich erstellen konnte.

Und hier ist eine Spirograph-Animation.
Und eine Version ohne CSS Houdini.
Aus diesen Beispielen lassen sich einige Dinge ableiten:
- Jeder Keyframe wird mit nur einer Deklaration definiert, die den Inkrement enthält.
- Die Position des Elements und die Animation sind unabhängig. Wir können das Element problemlos überall platzieren, ohne die Animation anpassen zu müssen.
- Wir haben keine Berechnungen durchgeführt. Es gibt nicht tonnenweise Winkel oder Pixelwerte. Wir benötigen nur einen winzigen Wert innerhalb des Keyframes und einen großen Wert innerhalb der
cubic-bezier()Funktion. - Die gesamte Animation kann allein durch Anpassung des Dauerwerts gesteuert werden.
Was ist mit Übergängen?
Die gleiche Technik kann auch mit der CSS-Eigenschaft transition verwendet werden, da sie der gleichen Logik in Bezug auf Timing-Funktionen folgt. Das ist großartig, denn wir können Keyframes vermeiden, wenn wir einige komplexe Hover-Effekte erstellen.
Hier ist, was ich ohne Keyframes gemacht habe.
Mario springt dank der Parabel-Kurve. Wir brauchten überhaupt keine Keyframes, um diese Shake-Animation beim Hover zu erstellen. Die Sinus-Kurve ist perfekt in der Lage, die gesamte Arbeit zu leisten.
Hier ist eine weitere Version von Mario, diesmal mit CSS Houdini. Und ja, er springt immer noch dank der Parabel-Kurve.
Zur Sicherheit hier noch weitere schicke Hover-Effekte ohne Keyframes (wiederum nur Chrome und Edge).
Das ist alles!
Jetzt haben Sie einige magische cubic-bezier() Kurven und die dahinterliegende Mathematik. Der Vorteil ist natürlich, dass benutzerdefinierte Timing-Funktionen wie diese uns schicke Animationen ermöglichen, ohne die komplexen Keyframes, die wir normalerweise verwenden.
Ich verstehe, dass nicht jeder mathematisch veranlagt ist, und das ist in Ordnung. Es gibt Werkzeuge, die helfen, wie Matthew Leins Ceaser, mit dem Sie die Kurvenpunkte verschieben können, um das zu bekommen, was Sie brauchen. Und wenn Sie es noch nicht als Lesezeichen haben, ist cubic-bezier.com eine weitere Option. Wenn Sie mit kubischen Bézier-Kurven außerhalb der CSS-Welt experimentieren möchten, empfehle ich desmos, wo Sie einige mathematische Formeln sehen können.
Unabhängig davon, wie Sie Ihre cubic-bezier() Werte erhalten, hoffe ich, dass Sie jetzt ein Gefühl für ihre Kräfte haben und wie sie zu einem schöneren Code im Prozess beitragen können.
Ein tiefgründiger Artikel über CSS-Animationen.
Vielen DankEinfach nur wow
Das sind episch!
Ich glaube, mein Gehirn explodiert, wenn ich versuche, die Gleichungen zu knacken, aber… ich denke, diese Methode könnte ideal sein, um einen EKG/EKG-Herzmonitor zu erstellen?
Ja, durch einfaches Anpassen der Sinus-Animation können Sie einen Herzmonitor simulieren. Hier ist eine schnelle Demo: https://codepen.io/t_afif/pen/jOBQvpr. Sie können sie mit der kontinuierlichen Sinus-Animation im Artikel vergleichen und Sie werden feststellen, dass ich nur ein paar Variablen angepasst habe.
Ich muss nur noch die Werte, den Hintergrund und das Timing ausarbeiten. Das Gleiche gilt für Ventrikelflimmern, ventrikuläre Tachykardie und Asystolie.
Es sollte einfach genug sein, Dinge wie eine invertierte T-Welle zu machen oder QRS zu verlängern.
Ich war früher bei einer Such- und Rettungseinheit und zum Notfallsanitäter ausgebildet. Kann es also sowohl aus medizinischer als auch aus Design-Sicht angehen.
Wow! Ich dachte, es gäbe alles Wissenswerte über kubische Bézier-Kurven und dann lese ich das!
Es eröffnet eine ganz neue Reihe von Möglichkeiten für Endlosschleifen-Animationen!
Verdammt, meine Demos von vor 25 Jahren laufen nicht mal mehr.
https://moshplant.com/direct-or/bezier/
Hallo,
Was kann ich tun, wenn ich genau Ihre erste Animation, die liegende Acht, aber viel langsamer haben möchte?
Wie kann ich die Geschwindigkeit des Balls verlangsamen?
Beste Grüße – Joerg
Sie müssen die Dauer erhöhen (die 1s und .5s), aber immer eine Relation von 2x zwischen beiden beibehalten.