Steuerung von CSS-Animationen und -Übergängen mit JavaScript

Avatar of Zach Saucier
Zach Saucier am

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

Der folgende Text ist ein Gastbeitrag von Zach Saucier. Zach schrieb mir und teilte mit, dass er als Stammgast auf Coding-Foren wie Stack Overflow immer wieder Fragen zur Steuerung von CSS-Animationen mit JavaScript sieht und dies mit einer Reihe von Links belegte. Ich hatte dies schon viel zu lange auf meiner Liste, um darüber zu schreiben, daher war ich glücklich, Zach die Möglichkeit zu geben, sich damit zu beschäftigen und dieses umfassende Tutorial zu verfassen.

Webdesigner glauben manchmal, dass Animationen in CSS schwieriger sind als Animationen in JavaScript. Während CSS-Animationen einige Einschränkungen haben, sind sie meistens leistungsfähiger, als wir ihnen zugestehen! Ganz zu schweigen davon, dass sie in der Regel performanter sind.

Gepaart mit einem Hauch von JavaScript können CSS-Animationen und -Übergänge Hardware-beschleunigte Animationen und Interaktionen effizienter realisieren als die meisten JavaScript-Bibliotheken.

Legen wir direkt los!

Kurzer Hinweis: Animationen und Übergänge sind unterschiedlich

Übergänge in CSS werden auf ein Element angewendet und legen fest, dass eine Eigenschaft, wenn sie sich ändert, über einen bestimmten Zeitraum allmählich übergeht. Animationen sind anders. Wenn sie angewendet werden, laufen sie einfach ab und tun ihr Ding. Sie bieten eine feinkörnigere Kontrolle, da Sie verschiedene Stopps der Animationen steuern können.

In diesem Artikel werden wir jeden einzeln behandeln.

CSS-Übergänge manipulieren

Es gibt unzählige Fragen in Coding-Foren bezüglich des Auslösens und Pausierens eines Übergangs eines Elements. Die Lösung ist mit JavaScript eigentlich ganz einfach.

Um den Übergang eines Elements auszulösen, schalten Sie einen Klassennamen auf diesem Element um, der ihn auslöst.

Um den Übergang eines Elements zu pausieren, verwenden Sie getComputedStyle und getPropertyValue an dem Punkt des Übergangs, an dem Sie ihn pausieren möchten. Setzen Sie dann diese CSS-Eigenschaften dieses Elements gleich den gerade erhaltenen Werten.

Das Folgende ist ein Beispiel für diesen Ansatz.

Die gleiche Technik kann auf fortgeschrittenere Weise eingesetzt werden. Das folgende Beispiel löst ebenfalls einen Übergang aus, indem ein Klassenname geändert wird, aber dieses Mal verfolgt eine Variable die aktuelle Zoomrate.

Beachten Sie, dass wir dieses Mal die Hintergrundgröße ändern. Es gibt viele verschiedene CSS-Eigenschaften, die überblendet oder animiert werden können, typischerweise solche mit numerischen Werten oder Farbwerten. Rodney Rehm schrieb auch einen besonders hilfreichen und informativen Artikel über CSS-Übergänge der hier zu finden ist.

CSS-"Callback"-Funktionen verwenden

Einige der nützlichsten, aber wenig bekannten JavaScript-Tricks zur Manipulation von CSS-Übergängen und -Animationen sind die DOM-Ereignisse, die sie auslösen. Wie: animationend, animationstart und animationiteration für Animationen und transitionend für Übergänge. Sie können sich wahrscheinlich vorstellen, was sie tun. Diese Animationsereignisse werden ausgelöst, wenn die Animation eines Elements endet, beginnt oder eine Iteration abschließt.

Diese Ereignisse müssen zu diesem Zeitpunkt herstellerspezifisch (vendor-prefixed) sein, daher verwenden wir in dieser Demo eine von Craig Buckler entwickelte Funktion namens PrefixedEvent, die die Parameter element, type und callback hat, um diese Ereignisse browserübergreifend zu machen. Hier ist sein nützlicher Artikel über das Erfassen von CSS-Animationen mit JavaScript. Und hier ist ein weiterer, der bestimmt, für welche Animation (Name) das Ereignis ausgelöst wird.

Die Idee in dieser Demo ist, das Herz zu vergrößern und die Animation zu stoppen, wenn darüber gehovert wird.

Die reine CSS-Version ist ruckelig. Wenn Sie nicht im perfekten Moment darüber hoovern, springt sie in einen bestimmten Zustand, bevor sie sich zum endgültigen Hover-Zustand vergrößert. Die JavaScript-Version ist viel flüssiger. Sie entfernt das Springen, indem sie die Animation abschließen lässt, bevor der neue Zustand angewendet wird.

CSS-Animationen manipulieren

Wie wir gerade gelernt haben, können wir Elemente beobachten und auf animationsbezogene Ereignisse reagieren: animationStart, animationIteration und animationEnd. Aber was passiert, wenn Sie eine CSS-Animation mitten in der Ausführung ändern möchten? Dies erfordert ein wenig Tricksen!

Die animation-play-state-Eigenschaft

Die animation-play-state-Eigenschaft von CSS ist unglaublich hilfreich, wenn Sie eine Animation einfach nur pausieren und möglicherweise später fortsetzen möchten. Sie können dieses CSS über JavaScript wie folgt ändern (achten Sie auf Ihre Präfixe)

element.style.webkitAnimationPlayState = "paused";
element.style.webkitAnimationPlayState = "running";

Wenn eine CSS-Animation jedoch mit animation-play-state pausiert wird, wird das Element daran gehindert, sich auf die gleiche Weise zu transformieren wie bei einer laufenden Animation. Sie können sie nicht pausieren, transformieren, fortsetzen und erwarten, dass sie flüssig aus dem neuen transformierten Zustand läuft. Um das zu erreichen, müssen wir etwas tiefer eintauchen.

Ermitteln des aktuellen Schlüsselwert-Prozentsatzes

Leider gibt es derzeit keine Möglichkeit, den genauen aktuellen "Prozentsatz des Fortschritts" einer CSS-Keyframe-Animation zu ermitteln. Die beste Methode, ihn anzunähern, ist die Verwendung einer setInterval-Funktion, die 100 Mal während der Animation iteriert, was im Wesentlichen ist: Animationsdauer in ms / 100. Wenn die Animation beispielsweise 4 Sekunden lang dauert, muss das setInterval alle 40 Millisekunden ausgeführt werden (4000/100).

var showPercent = window.setInterval(function() {
  if (currentPercent < 100) {
    currentPercent += 1;
  } else {
    currentPercent = 0;
  }
  // Updates a div that displays the current percent
  result.innerHTML = currentPercent;
}, 40);

Dieser Ansatz ist alles andere als ideal, da die Funktion tatsächlich seltener als alle 40 Millisekunden ausgeführt wird. Ich stelle fest, dass das Einstellen auf 39 Millisekunden genauer ist, aber sich darauf zu verlassen, ist schlechte Praxis, da es wahrscheinlich je nach Browser variiert und in keinem Browser perfekt passt.

Ermitteln der aktuellen CSS-Eigenschaftswerte der Animation

In einer perfekten Welt könnten wir ein Element auswählen, das eine CSS-Animation verwendet, diese Animation entfernen und ihm eine neue geben. Es würde dann die neue Animation starten, beginnend von seinem aktuellen Zustand. Wir leben nicht in dieser perfekten Welt, daher ist es etwas komplexer.

Unten haben wir eine Demo, um eine Technik zum Ermitteln und Ändern einer CSS-Animation "mittendrin" zu testen. Die Animation bewegt ein Element auf einem kreisförmigen Pfad, wobei die Startposition oben in der Mitte ("zwölf Uhr", wenn Sie so wollen) liegt. Wenn der Knopf gedrückt wird, sollte er die Startposition der Animation auf den aktuellen Ort des Elements ändern. Es durchläuft den gleichen Pfad, nur "beginnt" jetzt an der Stelle, an der es sich befand, als Sie den Knopf drückten. Diese Änderung des Ursprungs und damit der Animation wird durch die Farbänderung des Elements im ersten Keyframe angezeigt.

Wir müssen ziemlich tief eintauchen, um das zu schaffen! Wir werden uns in das Stylesheet selbst graben müssen, um die ursprüngliche Animation zu finden.

Sie können auf die mit einer Seite verbundenen Stylesheets zugreifen, indem Sie document.styleSheets verwenden und diese mit einer for-Schleife durchlaufen. Folgendes zeigt, wie Sie mit JavaScript die Werte einer bestimmten Animation in einem CSSKeyFrameRules-Objekt finden können.

function findKeyframesRule(rule) {
  var ss = document.styleSheets;
  for (var i = 0; i < ss.length; ++i) {
    for (var j = 0; j < ss[i].cssRules.length; ++j) {
      if (ss[i].cssRules[j].type == window.CSSRule.WEBKIT_KEYFRAMES_RULE && 
      ss[i].cssRules[j].name == rule) { 
        return ss[i].cssRules[j]; }
    }
  }
  return null;
}

Sobald wir die obige Funktion aufrufen (z. B. var keyframes = findKeyframesRule(anim)), können Sie die Animationslänge des Objekts (die Gesamtzahl, wie viele Keyframes in dieser Animation vorhanden sind) mit keyframes.cssRules.length abrufen. Dann müssen wir das "%" aus jedem der Keyframes entfernen, damit es nur noch Zahlen sind und JavaScript sie als Zahlen verwenden kann. Dazu verwenden wir das Folgende, das die .map-Methode von JavaScript verwendet.

// Makes an array of the current percent values
// in the animation
var keyframeString = [];  
for(var i = 0; i < length; i ++)
{
  keyframeString.push(keyframes[i].keyText); 
}
  
// Removes all the % values from the array so
// the getClosest function can perform calculations
var keys = keyframeString.map(function(str) {
  return str.replace('%', '');
});

Zu diesem Zeitpunkt ist keys ein Array aller Keyframes der Animation in numerischem Format.

Die eigentliche Animation ändern (endlich!)

Im Fall unserer Demo der Kreisbewegung benötigen wir zwei Variablen: eine, um zu verfolgen, wie viele Grad sich der Kreis seit seiner letzten Startposition bewegt hat, und eine weitere, um zu verfolgen, wie viele Grad er sich seit der ursprünglichen Startposition bewegt hat. Wir können die erste Variable mit unserer setInterval-Funktion ändern (unter Verwendung der verstrichenen Zeit und der Grade eines Kreises). Dann können wir den folgenden Code verwenden, um die zweite Variable zu aktualisieren, wenn der Knopf gedrückt wird.

totalCurrentPercent += currentPercent;
// Since it's in percent it shouldn't ever be over 100
if (totalCurrentPercent > 100) {
  totalCurrentPercent -= 100;
}

Dann können wir die folgende Funktion verwenden, um herauszufinden, welcher Keyframe der Animation dem gesamten aktuellen Prozentsatz am nächsten kommt, basierend auf dem Array der möglichen Keyframe-Prozentsätze, das wir oben erhalten haben.

function getClosest(keyframe) {
  // curr stands for current keyframe
  var curr = keyframe[0];
  var diff = Math.abs (totalCurrentPercent - curr);
  for (var val = 0, j = keyframe.length; val < j; val++) {
    var newdiff = Math.abs(totalCurrentPercent - keyframe[val]);
    // If the difference between the current percent and the iterated 
    // keyframe is smaller, take the new difference and keyframe
    if (newdiff < diff) {
      diff = newdiff;
      curr = keyframe[val];
     }
  }
  return curr;
}

Um den Wert des ersten Keyframes der neuen Animation zu erhalten, den wir später für Berechnungen verwenden können, können wir die .indexOf-Methode von JavaScript verwenden. Dann löschen wir die ursprünglichen Keyframes, damit wir neue erstellen können.

for (var i = 0, j = keyframeString.length; i < j; i ++) {
  keyframes.deleteRule(keyframeString[i]);
}

Als Nächstes müssen wir den % in einen Grad des Kreises umwandeln. Dies können wir tun, indem wir einfach den neuen ersten Prozentsatz mit 3,6 multiplizieren (da 100 * 3,6 = 360).

Schließlich erstellen wir die neuen Regeln basierend auf den oben ermittelten Variablen. Die 45-Grad-Differenz zwischen jeder Regel ergibt sich daraus, dass wir 8 verschiedene Keyframes haben, die sich um den Kreis bewegen. 360 (Grad in einem Kreis) geteilt durch 8 ist 45.

// Prefix here as needed

keyframes.insertRule("0% { 
  -webkit-transform: translate(100px, 100px) rotate(" + (multiplier + 0) + "deg) 
                     translate(-100px, -100px) rotate(" + (multiplier + 0) + "deg);
  background-color: red; 
}");
keyframes.insertRule("13% { 
  -webkit-transform: translate(100px, 100px) rotate(" + (multiplier + 45) + "deg)
                     translate(-100px, -100px) rotate(" + (multiplier + 45) + "deg); 
}");

// ...continued...

Dann setzen wir den aktuellen Prozent-setInterval zurück, damit er erneut ausgeführt werden kann. Beachten Sie, dass das Obige WebKit-präfixiert ist. Um es besser browserübergreifend kompatibel zu machen, könnten Sie möglicherweise eine UA-Erkennung durchführen, um zu erraten, welche Präfixe benötigt werden.

// Gets the browser prefix
var browserPrefix;
navigator.sayswho= (function(){
  var N = navigator.appName, ua = navigator.userAgent, tem;
  var M = ua.match(/(opera|chrome|safari|firefox|msie)\/?\s*(\.?\d+(\.\d+)*)/i);
  if(M && (tem = ua.match(/version\/([\.\d]+)/i))!= null) M[2] = tem[1];
  M = M? [M[1], M[2]]: [N, navigator.appVersion,'-?'];
  M = M[0];
  if(M == "Chrome") { browserPrefix = "webkit"; }
  if(M == "Firefox") { browserPrefix = "moz"; }
  if(M == "Safari") { browserPrefix = "webkit"; }
  if(M == "MSIE") { browserPrefix = "ms"; }
})();

Wenn Sie weiter recherchieren möchten, ist Russells Uresti's Antwort in diesem StackOverflow-Post und das zugehörige Beispiel hilfreich.

Animationen in Übergänge umwandeln

Wie wir gesehen haben, kann die Manipulation von CSS-Übergängen mit JavaScript vereinfacht werden. Wenn Sie mit CSS-Animationen nicht die gewünschten Ergebnisse erzielen, können Sie versuchen, sie in einen Übergang umzuwandeln und auf diese Weise damit zu arbeiten. Sie sind von etwa gleicher Schwierigkeit zu programmieren, aber sie können leichter eingestellt und bearbeitet werden.

Das größte Problem bei der Umwandlung von CSS-Animationen in Übergänge besteht darin, wenn wir animation-iteration in den entsprechenden transition-Befehl umwandeln. Übergänge haben kein direktes Äquivalent, weshalb sie überhaupt unterschiedliche Dinge sind.

Bezogen auf unsere Rotationsdemo ist ein kleiner Trick, sowohl die transition-duration als auch die rotation mit x zu multiplizieren. Dann müssen Sie eine Klasse haben/anwenden, um die Animation auszulösen, denn wenn Sie die geänderten Eigenschaften direkt auf das Element anwenden, gibt es nicht viel Übergang. Um den Übergang (Fake-Animation) zu starten, wenden Sie die Klasse auf das Element an.

In unserem Beispiel machen wir das beim Laden der Seite.

CSS-Matrizen manipulieren

CSS-Animationen können auch durch die Verwendung einer CSSMatrix manipuliert werden. Zum Beispiel

var translated3D = 
  new WebKitCSSMatrix(window.getComputedStyle(elem, null).webkitTransform); 

Aber der Prozess kann verwirrend werden, besonders für diejenigen, die gerade erst mit CSS-Animationen beginnen.

Für weitere Informationen über CSS-Matrizen siehe die Dokumentation (obwohl zugegebenermaßen nicht sehr hilfreich), dieses Tool, mit dem Sie mit Matrixwerten experimentieren können, oder Artikel zu diesem Thema, wie den hier.

CSS-Animationen zurücksetzen

Der Trick, um dies auf die richtige Weise zu tun, finden Sie hier bei CSS Tricks. Der Trick besteht im Wesentlichen darin, (wenn möglich) die Klasse zu entfernen, die die Animation gestartet hat, einen Reflow auszulösen und sie dann erneut anzuwenden. Wenn alles andere fehlschlägt, reißen Sie das Element von der Seite und setzen Sie es wieder ein.

Nutzen Sie Ihren Kopf

Bevor Sie mit dem Programmieren beginnen, ist das Nachdenken und Planen, wie ein Übergang oder eine Animation ablaufen soll, der beste Weg, um Ihre Probleme zu minimieren und den gewünschten Effekt zu erzielen. Noch besser als später nach Lösungen zu googeln! Die in diesem Artikel vorgestellten Techniken und Tricks sind möglicherweise nicht immer der beste Weg, um die Animation zu erstellen, die Ihr Projekt erfordert.

Hier ist ein kleines Beispiel, wo die clevere Nutzung von HTML und CSS allein ein Problem lösen kann, bei dem Sie vielleicht gedacht hätten, Sie müssten JavaScript verwenden.

Sagen wir, wir möchten, dass sich eine Grafik kontinuierlich dreht und dann beim Hovern die Drehrichtung wechselt. Wenn Sie lernen, was in diesem Artikel behandelt wurde, möchten Sie vielleicht anfangen und ein animationIteration-Ereignis verwenden, um die Animation zu ändern. Eine effizientere und performantere Lösung finden Sie jedoch mit CSS und einem zusätzlichen Container-Element.

Der Trick wäre, die Spirale mit einer Geschwindigkeit von x in eine Richtung drehen zu lassen und beim Hovern das Elternelement mit einer Geschwindigkeit von 2x in die entgegengesetzte Richtung drehen zu lassen (beginnend an der gleichen Position). Die beiden gegeneinander arbeitenden Rotationen erzeugen den Nettoeffekt, dass sich die Spirale in die entgegengesetzte Richtung dreht.

Das gleiche Konzept wurde in diesem Beispiel für eine StackOverflow-Frage verwendet.

Verwandte Dinge, die Sie vielleicht interessant finden.

Zusammenfassung

  • getComputedStyle ist hilfreich bei der Manipulation von CSS-Übergängen.
  • transitionend und seine verwandten Ereignisse sind bei der Manipulation von CSS-Übergängen und -Animationen mit JavaScript sehr hilfreich.
  • Das Ändern einer CSS-Animation von ihren aktuellen Werten kann durch das Abrufen der Stylesheets in JavaScript erfolgen, ist aber recht aufwendig.
  • In JavaScript sind CSS-Übergänge im Allgemeinen einfacher zu handhaben als CSS-Animationen.
  • CSS-Matrizen sind im Allgemeinen eine Plage, besonders für Anfänger.
  • Überlegen und Planen, wie eine Animation oder ein Übergang erstellt werden soll, ist entscheidend für das Programmieren von Animationen.