Gekrümmten Pfades in CSS

CSS-Animationen und -Übergänge eignen sich hervorragend zum Animieren von Elementen von Punkt A nach B. Wenn Sie also auf einem geraden Pfad animieren möchten. Unabhängig davon , wie stark Sie Ihre Bézier-Kurven biegen , können Sie keine Bewegung entlang eines gekrümmten Pfads ausführen , indem Sie ein animationoder ein transitionauf ein Objekt anwenden . Sie können mit benutzerdefinierten Timing-Funktionen übersteuern und federähnliche Effekte erzielen. Die relative Bewegung entlang der X- und Y-Achse ist jedoch immer identisch.

Anstatt JavaScript zu verwenden, um eine natürlichere Bewegung zu erzielen, können Sie diese Einschränkung auf einfache Weise umgehen: Ebenenanimation. Durch die Verwendung von zwei oder mehr Objekten zum Ansteuern einer Animation erhalten wir eine detaillierte Kontrolle über den Pfad eines Objekts und können eine Zeitsteuerungsfunktion für die Bewegung entlang der X-Achse und eine andere für die Y-Achse anwenden.

Das Problem

Regelmäßiger Pfad
Was wir wollen

Bevor wir uns mit der Lösung befassen, schauen wir uns das Problem genauer an. CSS animationsund transitionsbeschränken uns auf das Animieren auf geraden Pfaden. Wie? Indem wir immer den kürzesten Weg von Punkt A nach Punkt B nehmen. Das ist großartig - das ist es, was wir in den meisten Fällen wollen -, aber es fehlt uns eine Möglichkeit, CSS zu sagen, dass sie "einen schöneren Weg" anstatt "den kürzesten Weg" nehmen sollen. .

Die einfachste Methode zum Animieren zwischen zwei Punkten in CSS (mit Hardwarebeschleunigung) ist die Verwendung transformfür translateein Objekt im Laufe der Zeit. Dies erzeugt eine Bewegung entlang einer linearen Bahn. In diesem @keyframesBlock bewegen wir uns zwischen (0,0) und (100, -100) hin und her, wie im obigen Beispiel gezeigt:

@keyframes straightLine {
  50% {
    transform: translate3D(100px, -100px, 0);
  }
}

.dot {
  animation: straightLine 2.5s infinite linear;
}

Das ist nicht kompliziert, aber lassen Sie uns einen Moment innehalten. Um die Lösung des Problems zu verstehen, müssen wir die Animation zumindest visuell zerlegen.

An 0%fangen wir bei (0,0) an und bewegen 50%uns translate3D(100px, -100px, 0)mit zu (100, -100) und dann wieder zurück. Anders ausgedrückt, wir bewegen das Objekt 100pxnach rechts und 100pxnach oben, und diese beiden Übersetzungen zusammen bewegen das Objekt in einem Winkel.

X Bewegung
Y Bewegung
Kombiniert

Die Lösung: Eine Zeitfunktion pro Achse

Wie erstellen wir also einen gekrümmten Pfad wie im vorherigen Beispiel? Um einen Pfad zu erstellen, der nicht in einer geraden Linie verläuft, soll die Bewegungsgeschwindigkeit entlang der X- und Y-Achse nicht synchron sein .

In den vorherigen Beispielen wurden alle linearZeitfunktionen verwendet. Wenn Sie jedoch einen Container um das zu animierende Objekt hinzufügen, können Sie eine Zeitfunktion für die X-Achse und eine andere für die Y-Achse anwenden. Unten verwenden wir ease-infür die X-Achse und ease-outfür die Y-Achse:

X: Leichtigkeit
Y: Lockerheit
Kombiniert

Die Implementierung: Ein Objekt pro Achse

Leider können wir nicht nur transformAnimationen stapeln . Es würde nur die letzte zu deklarierende Animation ausgeführt. Wie kombinieren wir eigentlich zwei Animationen? Wir fügen ein Objekt in ein anderes ein und führen eine Animation für das Containerelement und eine andere für das untergeordnete Element aus.

In allen obigen Beispielen, in denen sich der Punkt auf einem gekrümmten Pfad bewegt, wurden zwei separate Elemente animiert, der Container war jedoch vollständig transparent. Um deutlich zu sehen, wie die beiden Objekte in den gekrümmten Beispielen interagieren, können Sie den Container so ändern, dass er eine umrandete Box darstellt:

X: Behälter
Y: Objekt
Kombiniert

Der Punkt befindet sich in der umrandeten Box und folgt der Bewegung der Box entlang der X-Achse, während er sich entlang der Y-Achse auf und ab bewegt. Entfernen Sie den Rand der Box, und wir haben unseren gekrümmten Pfad. Anstatt jedoch zwei Objekte in unseren HTML-Code einzufügen, können wir ein Pseudoelement erzeugen. Wenn wir dies in unserem HTML haben:

<div class="dot"></div>

Wir können ein Pseudoelement wie folgt hinzufügen:

.dot {
  /* Container. Animate along the X-axis */
}

.dot::after {
  /* Render dot, and animate along Y-axis */
}

Dann brauchen wir zwei separate Animationsblöcke: einen für die X-Achse und einen für die Y-Achse. Beachten Sie, wie einer verwendet ease-in, während der andere verwendet ease-out:

.dot {
  /* Some layout code… */
  animation: xAxis 2.5s infinite ease-in;
}

.dot::after {
  /* Render dot */
  animation: yAxis 2.5s infinite ease-out;
}

@keyframes xAxis {
  50% {
    animation-timing-function: ease-in;
    transform: translateX(100px);
  }
}

@keyframes yAxis {
  50% {
    animation-timing-function: ease-out;
    transform: translateY(-100px);
  }
}

Mit einigen benutzerdefinierten Bezier-Kurven, die über die angegebenen Werte hinausgehen, können wir das am Anfang des Beitrags gezeigte Beispiel erstellen:

.demo-dot {
  animation: xAxis 2.5s infinite cubic-bezier(0.02, 0.01, 0.21, 1);
}

.demo-dot::after {
  content: '';
  display: block;
  width: 20px;
  height: 20px;
  border-radius: 20px;
  background-color: #fff;
  animation: yAxis 2.5s infinite cubic-bezier(0.3, 0.27, 0.07, 1.64);
}

@keyframes yAxis {
  50% {
    animation-timing-function: cubic-bezier(0.02, 0.01, 0.21, 1);
    transform: translateY(-100px);
  }
}

@keyframes xAxis {
  50% {
    animation-timing-function: cubic-bezier(0.3, 0.27, 0.07, 1.64);
    transform: translateX(100px);
  }
}

Was uns dorthin zurückbringt, wo wir begonnen haben:

X Bewegung
Y Bewegung
Gebogener Pfad

Sie haben vielleicht bemerkt, dass wir bisher @keyframes in allen Beispielen Blöcke verwendet haben, aber das liegt nur daran, dass wir zufällig mehrere Keyframes benötigen, um den Punkt hin und her zu bewegen. Ebenenanimation funktioniert mit der transition Eigenschaft genauso gut , wenn Sie nur von Punkt A nach Punkt B animieren müssen.

Wenn Sie ein absolut positioniertes Element haben , können Sie eine gekrümmte Bahn erreichen , indem die Animieren left und bottom Eigenschaften eines einzelnen Elements, und die Notwendigkeit eines Containerelement zu vermeiden. Es gibt jedoch einen guten Grund, dies zu vermeiden: Die Leistung ist erheblich schlechter und es wird ein Neuzeichnen für jedes Bild der Animation ausgelöst. Wenn Sie eine Ebenenanimation mit einem Pseudoelement verwenden und die hardwarebeschleunigte translate Eigenschaft animieren, wird eine ansprechende Animation erstellt, bei der sichergestellt wird, dass auch eine gute Leistung erzielt wird.