Animation im Web mit GreenSock

Avatar of Sarah Drasner
Sarah Drasner am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

Es gibt wirklich Tausende von Möglichkeiten, Animationen im Web zu erstellen. Wir haben hier zuvor einen Vergleich verschiedener Animationstechnologien behandelt. Heute tauchen wir in eine Schritt-für-Schritt-Anleitung zu einer meiner Lieblingsmethoden ein: die Verwendung von GreenSock.

(Sie bezahlen mich nicht oder so, ich genieße es einfach sehr, sie zu benutzen.)

Warum bevorzuge ich GreenSock gegenüber anderen Methoden? Technisch gesehen ist es oft das beste Werkzeug für die Aufgabe. Seine Verwendung ist extrem einfach, selbst für komplexe Bewegungen. Hier sind ein paar weitere Gründe, warum ich es bevorzuge

  • Sie können sie auf DOM-Elemente sowie in WebGL/Canvas/Three.js-Kontexten anwenden.
  • Die Easing-Effekte sind sehr ausgefeilt. CSS-Animationen sind auf zwei Bézier-Punkte für die Easing beschränkt. Das bedeutet, wenn Sie zum Beispiel einen Bounce-Effekt wünschen, müssten Sie für jeden Durchgang Keyframes auf und ab und auf und ab erstellen. GreenSock ermöglicht mehrere Bézier-Punkte, um fortgeschrittene Effekte zu erstellen. Ein Bounce ist eine einzelne Codezeile. Sie können sehen, was ich meine, indem Sie sich ihren Ease-Visualizer ansehen.
  • Sie können Bewegungen auf einer Zeitachse sequenzieren. CSS-Animationen können etwas chaotisch werden, wenn Sie mehrere Dinge gleichzeitig koordinieren müssen. GreenSock bleibt sehr leserlich und ermöglicht es Ihnen, die Zeitachse selbst zu steuern. Sie können sogar Ihre Animationen animieren! 🤯
  • Es führt einige Berechnungen im Hintergrund durch, um seltsames Cross-Browser-Verhalten zu verhindern, sowie Dinge, die laut Spezifikation wahr sein sollten, aber nicht sind – wie die Art und Weise, wie Stacking-Transforms funktioniert.
  • Es bietet eine Menge fortgeschrittener Funktionalitäten in Form von Plugins, die Sie nutzen können, wenn Sie Ihre Arbeit einen Schritt weiter bringen möchten – Dinge wie Morphing von SVG-Formen, das Zeichnen von SVG-Pfaden, Ziehen und Ablegen, Trägheit und mehr.

Manche Leute fragen mich, warum ich diese spezielle Bibliothek gegenüber all den anderen Auswahlmöglichkeiten verwenden würde. Sie ist weiter fortgeschritten als die meisten anderen – sie existiert schon, seit Flash noch ein Ding war. Diese Demo-Reel ist ziemlich inspirierend und vermittelt auch den Punkt, dass ernsthafte Web-Animatoren dazu neigen, zu diesem Werkzeug zu greifen

Was folgt, ist eine Aufschlüsselung, wie man Bewegungen im Web erstellt, destilliert auf die kleinsten Einheiten, die ich machen konnte. Los geht's!

DOM-Element animieren

Betrachten wir einen Ball, den wir mit einem <div> erstellen, der mit einem border-radius von 50% gestylt ist. So würden wir ihn mit GreenSock skalieren und bewegen

<div class="ball"></div>
gsap.to('.ball', {
  duration: 1,
  x: 200,
  scale: 2
})

In diesem Fall weisen wir GreenSock (gsap) an, das Element mit der Klasse .ball zu nehmen und es .to() ein paar verschiedenen Eigenschaften zu bewegen. Wir haben die CSS-Eigenschaften transform: translateX(200px) zu einem optimierten x: 200 verkürzt (beachten Sie, dass die Einheiten nicht benötigt werden, aber Sie können sie als String übergeben). Wir schreiben auch nicht transform: scale(2). Hier ist eine Referenz der Transformationen, die Sie möglicherweise mit Animationen verwenden möchten, und ihre entsprechenden CSS-Syntaxen

x: 100 // transform: translateX(100px)
y: 100 // transform: translateY(100px)
z: 100 // transform: translateZ(100px)
// you do not need the null transform hack or hardware acceleration, it comes baked in with
// force3d:true. If you want to unset this, force3d:false
scale: 2 // transform: scale(2)
scaleX: 2 // transform: scaleX(2)
scaleY: 2 // transform: scaleY(2)
scaleZ: 2 // transform: scaleZ(2)
skew: 15 // transform: skew(15deg)
skewX: 15 // transform: skewX(15deg)
skewY: 15 // transform: skewY(15deg)
rotation: 180 // transform: rotate(180deg)
rotationX: 180 // transform: rotateX(180deg)
rotationY: 180 // transform: rotateY(180deg)
rotationZ: 180 // transform: rotateZ(180deg)
perspective: 1000 // transform: perspective(1000px)
transformOrigin: '50% 50%' // transform-origin: 50% 50%

Dauer ist, was Sie vielleicht denken: Es ist eine Sekunde Zeit.

Also, okay, wie würden wir zum Beispiel ein SVG animieren? Betrachten wir den gleichen Code von oben als SVG

<svg viewBox="0 0 500 400">
  <circle class="ball" cx="80" cy="80" r="80" />
</svg>
gsap.to('.ball', {
  duration: 1,
  x: 200,
  scale: 2
})

Aus Sicht der Animation ist es tatsächlich genau gleich. Es erfasst das Element mit der Klasse .ball und übersetzt diese Eigenschaften. Da SVGs buchstäblich DOM-Elemente sind, können wir ihnen eine Klasse zuweisen und sie genauso animieren!

Großartig! Wir sind auf dem richtigen Weg.

Eases

Ich habe zuvor erwähnt, dass Easing-Effekte eine meiner Lieblingsfunktionen sind. Schauen wir uns an, wie wir sie verwenden würden.

Nehmen wir den ursprünglichen Ball. Vielleicht möchten wir einen dieser ausgefalleneren Bounce-Easing-Effekte ausprobieren. Das würde so aussehen

gsap.to('.ball', {
  duration: 1.5,
  x: 200,
  scale: 2,
  ease: 'bounce'
})

Das ist alles! Diese Version von GreenSock geht davon aus, dass Sie ease-out Timing verwenden möchten (was für Eintritte besser ist), also wendet es dies als Standard an. Alles, was Sie tun müssen, ist "bounce" als String anzugeben und Sie sind startklar.

Sie haben vielleicht bemerkt, dass wir auch die Dauer etwas verlängert haben. Das liegt daran, dass der Ball zwischen dem Anfangs- und dem Endzustand mehr "Arbeit" leisten muss. Eine Dauer von einer Sekunde, obwohl schön für lineare oder sinusförmige Easing-Effekte, ist für einen Bounce- oder Elastic-Easing-Effekt etwas zu schnell.

Verzögerungen und Zeitachsen

Ich erwähnte, dass die Standard-ease-out-Timing-Funktion gut für Eintritte ist. Was ist mit einem ease-in oder ease-in-out-Ausgang? Machen wir das auch.

gsap.to('.ball', {
  duration: 1.5,
  x: 200,
  scale: 2,
  ease: 'bounce'
})

gsap.to('.ball', {
  duration: 1.5,
  delay: 1.5,
  x: 0,
  scale: 1,
  ease: 'back.inOut(3)'
})

Sie haben vielleicht ein paar Dinge bemerkt. Zum Beispiel haben wir in der vorletzten Zeile (ease: 'back.inOut(3)') nicht bounce.in verwendet. Stattdessen haben wir einen anderen Ease namens back für ease-in-out verwendet. Wir haben auch eine Konfigurationsoption übergeben, denn wie Sie mit dem Ease-Visualizer-Tool von Greensock sehen können, sind wir nicht auf die Standardkonfiguration für diesen Ease beschränkt. Wir können ihn an unsere Bedürfnisse anpassen. Nett!

Sie haben vielleicht auch bemerkt, dass wir die Animationen mit einer Verzögerung verkettet haben. Wir haben die Dauer der ersten Animation genommen und sichergestellt, dass die nächste eine entsprechende Verzögerung hat. Das funktioniert hier, ist aber ziemlich fehleranfällig. Was ist, wenn wir die Länge der ersten ändern wollen? Nun, dann müssen wir zurückgehen und die nachfolgende Verzögerung ändern. Und was ist, wenn danach noch eine weitere Animation kommt? Und danach noch eine? Nun, wir müssten zurückgehen und alle anderen Verzögerungen in der Kette berechnen. Das ist viel manuelle Arbeit.

Diese Arbeit können wir dem Computer überlassen. Einige meiner komplexeren Animationen bestehen aus Hunderten von verketteten Animationen! Wenn ich meine Arbeit beende und etwas am Anfang anpassen möchte, möchte ich nicht alles durchgehen müssen. Hier kommen Zeitachsen ins Spiel

gsap
  .timeline()
  .to('.ball', {
    duration: 1.5,
    x: 200,
    scale: 2,
    ease: "bounce"
  })
  .to('.ball', {
    duration: 1.5,
    x: 0,
    scale: 1,
    ease: "back.inOut(3)"
  });

Dies instanziiert eine Zeitachse und verkettet dann die beiden Animationen daran.

Aber wir haben immer noch etwas Wiederholung, da wir in jeder Animation dieselbe Dauer verwenden. Erstellen wir einen Standardwert dafür als Option, die an die Zeitachse übergeben wird.

gsap
  .timeline({
    defaults: {
      duration: 1.5
    }
  })
  .to('.ball', {
    x: 200,
    scale: 2,
    ease: "bounce"
  })
  .to('.ball', {
    x: 0,
    scale: 1,
    ease: "back.inOut(3)"
  });

Das ist ziemlich cool! Okay, Sie beginnen wahrscheinlich zu sehen, wie die Dinge auf diese Weise aufgebaut werden. Auch wenn es bei einer so einfachen Animation vielleicht keine große Rolle spielt, können Standardwerte und Zeitachsen bei wirklich komplexen Animationen den Code wirklich wartbar halten.

Nun, was ist, wenn wir diese Bewegung in die entgegengesetzte Richtung mit dem Ball spiegeln und sie einfach... weiterlaufen lassen wollen? Anders ausgedrückt, was ist, wenn wir eine Schleife wollen? Dann fügen wir repeat: -1 hinzu, was entweder auf eine einzelne Animation oder auf die gesamte Zeitachse angewendet werden kann.

gsap
  .timeline({
    repeat: -1,
    defaults: {
      duration: 1.5
    }
  })
  .to('.ball', {
    x: 200,
    scale: 2,
    ease: "bounce"
  })
  .to('.ball', {
    x: 0,
    scale: 1,
    ease: "back.inOut(3)"
  })
  .to('.ball', {
    x: -200,
    scale: 2,
    ease: "bounce"
  })
  .to('.ball', {
    x: 0,
    scale: 1,
    ease: "back.inOut(3)"
  });

Wir könnten sie nicht nur wiederholen, sondern auch wiederholen und vorwärts und rückwärts abspielen, wie ein Jojo. Deshalb nennen wir es yoyo: true. Um den Punkt klarzumachen, zeigen wir dies nur mit der ersten Animation. Sie sehen, sie spielt vorwärts und dann rückwärts.

gsap
  .timeline({
    repeat: -1,
    yoyo: true,
    defaults: {
      duration: 1.5
    }
  })
  .to('.ball', {
    x: 200,
    scale: 2,
    ease: "bounce"
  })

Überlappungen und Labels

Es ist großartig, dass wir Animationen einfach verketten können, aber reale Bewegungen funktionieren nicht genau so. Wenn Sie durch den Raum gehen, um eine Tasse Wasser zu holen, gehen Sie nicht. Dann stoppen Sie. Dann nehmen Sie das Wasser. Dann trinken Sie es. Wahrscheinlicher tun Sie die Dinge in einer kontinuierlichen Bewegung. Lassen Sie uns also kurz darüber sprechen, wie Bewegungen überlappt und Dinge gleichzeitig ausgelöst werden.

Wenn wir sicherstellen wollen, dass Dinge auf einer Zeitachse etwas vor und nach einander ausgelöst werden, können wir einen Inkrementierer oder Dekrementierer verwenden. Wenn wir das folgende Beispiel nehmen, das drei Bälle nacheinander animiert, wirkt es etwas steif.

gsap
  .timeline({
    defaults: {
      duration: 1.5
    }
  })
  .to('.ball', {
    x: 300,
    scale: 2,
    ease: "bounce"
  })
  .to('.ball2', {
    x: 300,
    scale: 2,
    ease: "bounce"
  })
  .to('.ball3', {
    x: 300,
    scale: 2,
    ease: "bounce"
  })

Die Dinge werden flüssiger, wenn wir die Bewegung leicht überlappen, indem wir diese Dekrementierer als Strings verwenden

gsap
  .timeline({
    defaults: {
      duration: 1.5
    }
  })
  .to('.ball', {
    x: 300,
    scale: 2,
    ease: "bounce"
  })
  .to('.ball2', {
    x: 300,
    scale: 2,
    ease: "bounce"
  }, '-=1')
  .to('.ball3', {
    x: 300,
    scale: 2,
    ease: "bounce"
  }, '-=1')

Eine andere Möglichkeit, dies zu tun, ist die Verwendung von etwas namens Label. Labels stellen sicher, dass Dinge zu einem bestimmten Zeitpunkt im Playhead der Animation ausgelöst werden. Es sieht so aus:.add('labelName')

gsap
  .timeline({
    defaults: {
      duration: 1.5
    }
  })
  .add('start')
  .to('.ball', {
    x: 300,
    scale: 2,
    ease: "bounce"
  }, 'start')
  .to('.ball2', {
    x: 300,
    scale: 2,
    ease: "bounce"
  }, 'start')
  .to('.ball3', {
    x: 300,
    scale: 2,
    ease: "bounce"
  }, 'start') 

Wir können sogar von einem Label aus inkrementieren und dekrementieren. Das mache ich tatsächlich oft in meinen Animationen. Es sieht so aus:'start+=0.25'.

gsap
  .timeline({
    defaults: {
      duration: 1.5
    }
  })
  .add('start')
  .to('.ball', {
    x: 300,
    scale: 2,
    ease: "bounce"
  }, 'start')
  .to('.ball2', {
    x: 300,
    scale: 2,
    ease: "bounce"
  }, 'start+=0.25')
  .to('.ball3', {
    x: 300,
    scale: 2,
    ease: "bounce"
  }, 'start+=0.5') 
 

Puh! Wir können damit so viel machen! Hier ist ein Beispiel für eine Animation, die viele dieser Prämissen zusammenfasst, mit etwas Interaktion mit Vanilla JavaScript. Klicken Sie unbedingt auf die Glocke.

Wenn Sie eher an Framework-basierter Animation mit GreenSock interessiert sind, hier ist ein Artikel, den ich über Vue geschrieben habe, und ein Vortrag, den ich gehalten habe, der sich mit React beschäftigt – er ist ein paar Jahre alt, aber die grundlegenden Prämissen gelten immer noch.

Aber es gibt noch so viel, das wir nicht behandelt haben, einschließlich Staggers, SVG-Morphing, SVG-Zeichnen, Dinge über den Bildschirm werfen, Dinge entlang eines Pfades bewegen, Text animieren... Sie nennen es! Ich empfehle Ihnen, sich die GreenSock-Dokumentation für diese Details anzusehen. Ich habe auch einen Kurs auf Frontend Masters, der all diese Dinge im Detail behandelt, und die Materialien sind Open Source auf meinem GitHub. Ich habe auch viele Pens, die Open Source sind, damit Sie sie forken und damit spielen können.

Ich hoffe, das hilft Ihnen beim Einstieg in die Animation im Web! Ich kann es kaum erwarten zu sehen, was Sie erschaffen werden!