AngularJS ist ein wichtiger Akteur in der Welt der JavaScript MVW-Frameworks. „In Angular denken“ ist etwas, das Entwicklern, die von jQuery oder anderen DOM-manipulationslastigen Bibliotheken kommen, schwerfallen kann. Es gibt einen „Angular-Weg“, Dinge zu tun, der datengesteuert ist, anstatt DOM-Traversal zu verwenden, um Ansichtsänderungen zu steuern, und das kann schwierig zu visualisieren sein, wenn es um etwas wie Animationen geht. Gemeinsam werden wir genau durchgehen, wie man mit den vom Angular-Team bereitgestellten Werkzeugen animiert.
ngAnimate und der $animate Service
Das Angular-Kernteam hat uns das ngAnimate Modul zur Verfügung gestellt, damit wir unseren Apps eine Möglichkeit geben können, auf eine datengesteuerte „Angular-Art“ zu animieren, und damit wir uns in die Ereignisse einklinken können, die Angular über einige seiner integrierten Direktiven ausgibt.
Angular konzentriert sich im Gegensatz zu jQuery darauf, unsere Ansicht durch die Verwendung von Controllern an ein JavaScript-Objekt zu binden. Dieser Ansatz ermöglicht es uns, Ansichtswerte wie Eingabefelder direkt an einen entsprechenden Wert in einem JavaScript-Objekt zu binden und Ansichtsänderungen durch Datenänderungen oder umgekehrt auszulösen.
Wie können wir dann Animationen in diese Ereignisse einklinken, wenn sie sowohl aus der Ansicht als auch durch die Änderung des entsprechenden Objekts ausgelöst werden könnten?
Zuerst müssen wir ngAnimate zu unserem Projekt hinzufügen.
Angular Animation in unser Projekt einbinden
Seit Version 1.2.0 sind Animationen kein Teil des Angular-Kerns mehr, sondern befinden sich stattdessen in einem eigenen separaten Modul: ngAnimate. Um den $animate Service nutzen zu können, müssen wir die Animationsbibliothek nach Angular in unserer HTML-Datei einbinden, so:
Alternativ können Sie auch das CDN oder bower verwenden, um angular-animate zu installieren.
$ bower install --save angular-animate
Wie auch immer Sie es installieren, stellen Sie sicher, dass Sie es in Ihre Quelldatei einbinden, so:
<script src="js/lib/angular.js"></script>
<script src="js/lib/angular-animate.js"></script>
Als Nächstes müssen wir das ngAnimate Modul als Abhängigkeit zu unserer App hinzufügen. Dies kann geschehen, wenn wir unsere Angular App instanziieren, so:
angular.module('myApp', ['ngAnimate']);
Jetzt, wo ngAnimate in unser Projekt eingebunden ist (und solange wir keinen Injector-Fehler oder ähnliches in unserer Konsole haben), können wir mit dem Erstellen von Animationen mit Angular beginnen!
CSS3-Übergänge
Der einfachste Weg, Animationen in jede Anwendung einzubinden, ist die Verwendung von CSS3-Übergängen. Das liegt daran, dass sie vollständig klassenbasiert sind, was bedeutet, dass die Animation in einer Klasse definiert ist und solange wir diese Klasse in unserem HTML verwenden, funktioniert die Animation im Browser.
CSS-Übergänge sind Animationen, die es einem HTML-Element ermöglichen, sich stetig von einem Stil zu einem anderen zu ändern. Um einen Übergang zu definieren, müssen wir das Element angeben, dem wir einen Effekt hinzufügen möchten, und die Dauer dieses Effekts.
Lassen Sie uns zuerst ein einfaches Beispiel für einen CSS3-Übergang betrachten und dann sehen, wie wir dieses Wissen aus einer datengesteuerten Angular-App nutzen können.
Erstellen wir ein einfaches div innerhalb eines Container-div und wenden darauf zwei Klassen an: eine für die grundlegende Formatierung und eine für unseren Übergang.
<div class="container">
<div class="box rotate"></div>
</div>
Nun können wir Übergänge für den Hover-Zustand oder den statischen Zustand des Elements hinzufügen.
.box {
margin: 50px auto;
background: #5FCF80;
width: 150px;
height: 150px;
}
.box:hover {
transform: rotate(360deg);
background: #9351A6;
border-radius: 50%;
}
.rotate {
transition: all 0.5s ease-in-out;
}
.rotate:hover {
transition: all 1s ease-in-out;
}
Dies wendet zwei Zustände auf unser div an: einen normalen Zustand und einen für den Fall, dass wir mit der Maus über das div fahren. Die Übergänge, die in den Klassen .rotate und .rotate:hover definiert sind, teilen dem Browser mit, wie zwischen diesen beiden Zuständen übergeblendet werden soll, wenn wir die Ereignisse hover und mouseleave auslösen.
Das Ergebnis ist ein Effekt wie dieser.
Grundlegender CSS3-Übergang
Angular datengesteuerte CSS3-Animation
Lassen Sie uns nun sehen, wie wir so etwas in einer Angular-App tun und dieselbe Funktionalität an Daten in unserer Anwendung binden könnten.
Anstatt diesen Übergang bei :hover durchzuführen, können wir eine einfache Animation erstellen, indem wir Übergänge an eine Klasse, .rotate, binden und eine Klasse für sowohl den „Box“- als auch den „Kreis“-Zustand des div erstellen. Dies ermöglicht es uns, zwischen Klassen mit der in Angular integrierten ng-class Direktive zu wechseln.
.box {
margin: 20px auto;
background: #5FCF80;
width: 150px;
height: 150px;
}
.circle {
transform: rotate(360deg);
background: #9351A6;
border-radius: 50%;
margin: 20px auto;
width: 150px;
height: 150px;
}
.rotate {
transition: all 1s ease-in-out;
}
Dazu müssen wir unsere Angular-App einrichten und eine Bedingung in der ng-class Direktive erstellen, um die Klasse basierend auf dem Wert eines Boole'schen Wertes auf dem $scope umzuschalten.
<div ng-app="myApp" ng-controller="MainCtrl">
<div class="container">
<input type="checkbox" ng-model="boxClass" />
<div class="box rotate" ng-class="{'box': boxClass, 'circle': !boxClass} "></div>
</div>
</div>
Lassen Sie uns nun unser JavaScript einrichten.
angular.module('myApp', [])
.controller('MainCtrl', function($scope) {
$scope.boxClass = true;
});
Hier binden wir den booleschen Wert, der an $scope.boxClass angehängt ist, daran, ob das Element die Klasse .box oder .circle haben soll. Wenn der boolesche Wert wahr ist, hat das Element die Klasse .box. Wenn er falsch ist, hat es die Klasse .circle. Dies ermöglicht es uns, einen CSS3-Übergang auszulösen, indem wir den Wert unserer Daten ändern, ohne jegliche DOM-Manipulation.
Dies verwendet nicht den $animate Service, aber ich wollte ein Beispiel für eine Instanz liefern, bei der Sie CSS3 allein verwenden und sich nicht auf $animate und ngAnimate verlassen müssen.
Das Ergebnis ist eine Animation, die ausschließlich durch eine Datenänderung ausgelöst wird, wenn wir das zugrunde liegende boolesche Feld durch Klicken auf das Kontrollkästchen ändern.
Angular datengesteuerter CSS3-Übergang
Übergänge mit $animate
Wenn wir CSS3-Übergänge und den $animate Service nutzen wollen, müssen wir ein paar Dinge darüber wissen, wie $animate hinter den Kulissen funktioniert.
Der $animate Service unterstützt mehrere Direktiven, die in Angular integriert sind. Dies ist ohne weitere Konfiguration verfügbar und ermöglicht es uns, Animationen für unsere Direktiven in einfachem CSS zu erstellen. Um Animationen auf diese Weise zu nutzen, müssen Sie $animate nicht einmal in Ihrem Controller einbinden; binden Sie einfach ngAnimate als Abhängigkeit Ihres Angular-Moduls ein.
Sobald Sie ngAnimate in Ihr Modul einbinden, ändert sich die Art und Weise, wie Angular bestimmte integrierte Direktiven behandelt. Angular beginnt, diese Direktiven zu erfassen und zu überwachen und fügt dem Element beim Auslösen bestimmter Ereignisse spezielle Klassen hinzu. Wenn Sie beispielsweise ein Element zu einem Array hinzufügen, daraus entfernen oder es verschieben, das von der ngRepeat Direktive verwendet wird, fängt Angular dieses Ereignis nun ab und fügt dem Element in der ngRepeat eine Reihe von Klassen hinzu.
Hier sehen Sie die Klassen, die ngAnimate beim Enter-Ereignis einer ngRepeat hinzufügt.

ngRepeat EreignisklassenDie angehängten CSS-Klassen haben die Form ng-{EVENT} und ng-{EVENT}-active für strukturelle Ereignisse wie enter, move oder leave. Bei klassenbasierten Animationen hat es jedoch die Form {CLASS}-add, {CLASS}-add-active, {CLASS}-remove und {CLASS}-remove-active. Ausnahmen von diesen Regeln sind ng-hide und ng-show. Beide Direktiven haben add- und remove-Ereignisse, die ausgelöst werden, genau wie ng-class, aber sie teilen sich beide die Klasse .ng-hide, die nach Bedarf hinzugefügt oder entfernt wird. Sie werden auch sehen, dass ngAnimate einigen dieser Direktiven die Klasse .ng-animate bei Animationen hinzufügt.
Unten ist eine Tabelle, die einige der integrierten Direktiven, die ausgelösten Ereignisse und die Klassen veranschaulicht, die temporär hinzugefügt werden, wenn Sie ngAnimate zu Ihrem Projekt hinzufügen.
$animate-Ereignisse integrierter Direktiven
| Direktive | Ereignis(se) | Klassen |
|---|---|---|
| ngRepeat | enter | ng-enter, ng-enter-active |
| leave | ng-leave, ng-leave-active | |
| move | ng-move, ng-move-active | |
| ngView, ngInclude, ngSwitch, ngIf | enter | ng-enter, ng-enter-active |
| leave | ng-leave, ng-leave-active | |
| ngClass | addieren | ng-add, ng-add-active |
| remove | ng-remove, ng-remove-active | |
| ngShow, ngHide | add, remove | ng-hide |
Angular erkennt automatisch, dass CSS an eine Animation angehängt ist, wenn die Animation ausgelöst wird, und fügt die Klasse .ng-{EVENT}-active hinzu, bis die Animation abgeschlossen ist. Anschließend entfernt es diese Klasse und alle anderen hinzugefügten Klassen aus dem DOM.
Unten sehen Sie ein Beispiel für die Verwendung von CSS3-Übergängen zur Animation einer ngRepeat-Direktive. Darin fügen wir einen Übergang zur Basisklasse hinzu – in diesem Fall .fade – und hängen uns dann an die Klassen an, die ngAnimate den li-Elementen hinzufügt, wenn sie zum Array hinzugefügt oder daraus entfernt werden. Auch hier können wir so datengesteuerte Animationen durchführen – den Angular-Weg.
ngRepeat $animate-gestützte CSS3-Übergänge
Wie wir sehen, gibt uns Angulars ngAnimate die Möglichkeit, einfach auf Ereignisse zuzugreifen und die Leistung von CSS3-Übergängen zu nutzen, um einige wirklich coole, natürliche Animationen auf unseren Direktiven durchzuführen. Dies ist mit Abstand der einfachste Weg, Animationen für unsere Angular-Apps zu erstellen, aber nun werden wir uns einige komplexere Optionen ansehen.
CSS3-Animationen
CSS3-Animationen sind komplizierter als Übergänge, haben aber auf der ngAnimate-Seite eine ähnliche Implementierung. Im CSS werden wir jedoch eine @keyframes Regel verwenden, um unsere Animation zu definieren. Dies geschieht auf ähnliche Weise wie bei unserem grundlegenden Übergang zuvor, außer dass wir das Schlüsselwort animation in unserem CSS verwenden und der Animation einen Namen geben, wie hier:
@keyframes appear {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes disappear {
from {
opacity: 1;
}
to {
opacity: 0;
}
}
Hier haben wir eine appear und disappear Animation erstellt, die durch CSS an der Stelle ausgelöst werden kann, an der sich zuvor unser Übergang befand.
.fade.ng-enter {
animation: 2s appear;
}
.fade.ng-leave {
animation: 1s disappear;
}
Der Unterschied ist diesmal, wie Sie oben sehen können, dass wir nicht mehr .ng-enter-active oder .ng-leave-active verwenden müssen, sondern die Animation an .ng-leave und .ng-active anhängen können, und die Animation wird dank ngAnimate zu den entsprechenden Zeiten ausgelöst. Dies ist keine besonders bessere Methode als unsere obige Übergangsmethode, aber sie veranschaulicht, wie man CSS3-Animationen verwendet, die VIEL mächtiger sein können als dieser einfache Effekt.
Das Endergebnis dieser Animation, wenn sie auf unser vorheriges Grocery-Listen-ngRepeat-Beispiel angewendet wird, sieht ungefähr so aus.
ngRepeat $animate-gestützte CSS3-Animationen
JavaScript-Animationen
Nun zum Elefanten im Raum: JavaScript-Animationen mit AngularJS.
Angular ist mit seinem schicken Two-Way-Data-Binding vollständig datengesteuert – zumindest, solange es das ist. Das ist eine der verwirrendsten Sachen beim Umstieg von jQuery auf Angular. Man sagt uns, wir sollen umdenken und die DOM-Manipulation zugunsten von Bindungen aufgeben, aber dann werfen sie es uns später wieder vor die Füße. Nun, willkommen zu diesem Wendepunkt.
JavaScript-Animation hat einen großen Vorteil – JavaScript ist überall und wird breiter akzeptiert als einige fortgeschrittene CSS3-Animationen. Wenn Sie nur moderne Browser anvisieren, wird dies wahrscheinlich kein Problem für Sie sein, aber wenn Sie Browser unterstützen müssen, die keine CSS-Übergänge unterstützen, können Sie ganz einfach eine JavaScript-Animation bei Angular registrieren und sie immer wieder in Ihren Direktiven verwenden. Im Grunde hat JavaScript mehr Unterstützung in älteren Browsern, und damit auch JavaScript-Animationen.
Wenn Sie ngAnimate als Abhängigkeit Ihres Angular-Moduls einbinden, fügt es der Modul-API die Methode animation hinzu. Das bedeutet, dass Sie sie jetzt verwenden können, um Ihre JavaScript-Animationen zu registrieren und sich in die Angular-Hooks in integrierten Direktiven wie ngRepeat einzuklinken. Diese Methode nimmt zwei Argumente entgegen: className(string) und animationFunction(function).
Der Parameter className ist einfach die Klasse, die Sie anvisieren, und die Animationsfunktion kann eine anonyme Funktion sein, die sowohl die Parameter element als auch done erhält, wenn sie aufgerufen wird. Der Parameter element ist genau das, das Element als jqLite-Objekt, und der Parameter done ist eine Funktion, die Sie aufrufen müssen, wenn Ihre Animation abgeschlossen ist, damit Angular weiterarbeiten kann und weiß, dass das Ereignis abgeschlossen ist.
Das Wichtigste, was Sie hier verstehen müssen, ist jedoch, was von der Animationsfunktion zurückgegeben werden muss. Angular erwartet ein Objekt mit Schlüsseln, die den Namen der Ereignisse entsprechen, auf die Sie Animationen für diese bestimmte Direktive auslösen möchten. Wenn Sie sich nicht sicher sind, was die Direktive unterstützt, schauen Sie einfach in meine obige Tabelle.
Für unser ngRepeat-Beispiel würde das also ungefähr so aussehen:
return {
enter: function(element, done) {
// Animation code goes here
// Use done() in your animation callback
},
move: function(element, done) {
// Animation code goes here
// Use done() in your animation callback
},
leave: function(element, done) {
// Animation code goes here
// Use done() in your animation callback
}
}
Und wenn wir all das mit dem gleichen alten langweiligen (sorry) ngRepeat-Grocery-Listen-Beispiel zusammenführen und jQuery für die eigentlichen Animationen verwenden:
var app = angular.module('myApp', ['ngAnimate'])
.animation('.fade', function() {
return {
enter: function(element, done) {
element.css('display', 'none');
$(element).fadeIn(1000, function() {
done();
});
},
leave: function(element, done) {
$(element).fadeOut(1000, function() {
done();
});
},
move: function(element, done) {
element.css('display', 'none');
$(element).slideDown(500, function() {
done();
});
}
}
})
Nun, lassen Sie mich aufschlüsseln, was passiert.
Wir können jegliches CSS entfernen, das wir zuvor für die Klasse .fade hatten, aber wir brauchen immer noch eine Art Klasse, um die Animation darauf zu registrieren. Aus Gründen der Kontinuität habe ich also einfach die gute alte .fade-Klasse verwendet.
Im Grunde passiert hier Folgendes: Angular registriert Ihre Animationsfunktionen und ruft sie auf diesem spezifischen Element auf, wenn dieses Ereignis bei dieser Direktive eintritt. Zum Beispiel wird Ihre enter-Animationsfunktion aufgerufen, wenn ein neues Element in eine ngRepeat eintritt.
Dies sind alles sehr grundlegende jQuery-Animationen und ich werde hier nicht darauf eingehen, aber es ist erwähnenswert, dass ngRepeat das neue Element automatisch zum DOM hinzufügt, wenn es zum Array hinzugefügt wird, und dass dieses Element sofort sichtbar ist. Wenn Sie also einen Fade-In-Effekt mit JavaScript erzielen möchten, müssen Sie die Anzeige sofort auf none setzen, bevor Sie sie einblenden. Das ist etwas, das Sie mit CSS-Animationen und Übergängen vermeiden könnten.
Lassen Sie uns alles zusammenfügen und sehen, was wir bekommen.
ngRepeat $animate-gestützte JavaScript-Animationen
Fazit
Das Modul ngAnimate ist irreführend benannt.
Zugegeben, ich könnte keinen besseren Namen finden, aber es führt tatsächlich KEINE Animationen durch. Vielmehr gibt es Ihnen Zugang zur Ereignisschleife von Angular, damit Sie Ihre eigene DOM-Manipulation oder CSS3-Animationen zum richtigen, datengesteuerten Zeitpunkt durchführen können. Das ist an sich schon mächtig, weil wir es „den Angular-Weg“ machen, anstatt zu versuchen, unsere eigene Logik und unser eigenes Timing auf ein sehr bestimmtes Framework zu erzwingen.
Ein weiterer Vorteil der Animationen mit ngAnimate ist, dass Sie, sobald Sie Ihre Animationen für diese Direktive geschrieben haben, diese schön verpacken und relativ einfach auf andere Projekte übertragen können. Das ist meiner Meinung nach immer eine gute Sache.
Nennen Sie mich einen Pedanten, aber ich möchte keine Animation, wenn ich eine Website ansehe. Ich brauche nicht, dass das Menü nach unten gleitet, wenn ich auf „Erweitern“ klicke; es soll sofort da sein. Ich mag Geschwindigkeit und Effizienz.
Ich denke, ich stimme Ihnen zu, Animation sollte nicht um der Animation willen sein und die Navigation einer Benutzeroberfläche nicht verlangsamen. Das bedeutet nicht, dass ich denke, dass Animationen keinen Platz in der Benutzeroberfläche haben.
Ich denke, der Löschübergang in diesem Beispiel ist ein gutes Beispiel für unnötige Animation. Er ist langsam und trägt nicht wirklich zur Benutzererfahrung bei, da der Benutzer direkt mit dem Element interagiert, das gelöscht wird.
Das Beispiel für das Verschieben des untersten Elements nach oben ist jedoch ein gutes Beispiel für Animationen, die die Benutzererfahrung verbessern. Wenn Sie auf die Schaltfläche geklickt hätten und das unterste Element plötzlich am unteren Ende der Liste verschwunden und oben wieder aufgetaucht wäre, wäre die Änderung nicht so bemerkbar wie bei der subtilen Animation (die meiner Meinung nach etwas schneller sein könnte). Wenn ich Animationen in der Benutzeroberfläche verwende, stelle ich standardmäßig 0,1 s ein, da dies gerade genug Zeit ist, um einen Übergang anzuzeigen, während immer noch dieses „sofortige“ Gefühl erhalten bleibt. Als Konvention lege ich die maximale Animationsdauer von UI-Elementen auf 0,25 s fest. Länger als das wartet der Benutzer.
Ich glaube, Neil hat auch für die meisten Teile Recht. Wenn Sie jemals das Gefühl haben „Igitt, Animation“, hätte diese Animation nicht da sein dürfen. Wenn Sie sie nicht bemerken oder bis zu dem Punkt bemerken, dass sie erfreulich ist oder Ihnen hilft, etwas zu verstehen, das gerade passiert ist, dann war sie wahrscheinlich lohnenswert.
Ziemlich gute Beispiele, finde ich: https://css-tricks.de/transitional-interfaces-coded/
Deshalb überschreiten meine Animationen oder Übergänge nie die Dauer von 200 ms (selbst das ist schon viel). Subtilität ist meiner Meinung nach der Schlüssel. Der Benutzer wird nicht das Gefühl haben, auf ein UI-Element zu warten, um damit zu interagieren, aber er wird immer noch etwas Angenehmes sehen, bevor er damit interagiert.
Neil, du bist ein Pedant!!! ;)
Während fast jeder zustimmen wird, dass zu viel Animation nicht gut ist, sind aussagekräftige Animationen ein unglaublich nützliches Werkzeug im modernen Webdesign.
Null Animation bedeutet statische Schnitte zwischen UI-Zuständen.
http://www.google.com/design/spec/animation/authentic-motion.html
Ich bin immer noch nicht sicher, warum man so etwas Kompliziertes benutzen sollte – was ist das Problem mit jQuery (ich meine, abgesehen von der Tatsache, dass es ein überbewertetes Framework namens Angular gibt, also müssen wir heutzutage Angular benutzen)?!??
Ich bin sicher, dass dasselbe über jQuery in seinen frühen Tagen gesagt wurde :D
Der einzige Grund, Angular Animations auf Ihrer Website zu verwenden, wäre, dass Sie bereits AngularJS verwenden und es nicht mit jQuery mischen möchten.
Es gibt kein Problem mit jQuery. Dies ist, wenn Sie AngularJS verwenden.
Ich habe ein seltsames Problem… Jedes Mal, wenn ich versuche, diesen JS-Code auf meinem Server auszuführen, funktioniert er perfekt, bis ich versuche, den ‚Senf‘-Artikel nach oben zu verschieben…
Der Button „Element nach oben verschieben“ funktioniert in den meisten Szenarien wie erwartet, aber wenn es um Senf geht, scheint er jedes andere Element durch Senf zu ersetzen, bis alles Senf ist, und der Senf verschwindet nie. Ich wollte nicht so viel Senf, was habe ich falsch gemacht?
Beispielcode
Man kann nie zu viel Senf haben.
Es gibt nur wenige Komponenten, bei denen Animationen eine tatsächliche Verbesserung darstellen – und in den meisten Fällen sind diese an Ereignisse gebunden, die Pseudoklassen wie :hover oder :focus auslösen, sodass Sie sie ohne Angular machen können.
Meiner Erfahrung nach liegt die längste machbare Dauer einer Animation zwischen 0,1 und 0,3 Sekunden, je nach Größe, sonst fühlt sie sich träge an. Wenn Sie sich nicht die Zeit nehmen wollen, die Animation fein abzustimmen, verwenden Sie lieber eine kürzere Dauer und kommen Sie später darauf zurück, wenn Sie Zeit haben.
Nun, manchmal lockt Animation auch „andere Leute“ an. Vielleicht ein bisschen Animation, aber nicht „zu“ viel.
Tolle Anleitung! Sie beschreibt sehr gut, dass ngAnimate *tatsächlich nichts animiert*. Mann, ich liebe AngularJS.
Ich mag diese Art von Animationen, wenn sie schnell und nicht zu aggressiv ist. Danke für die Erklärungen, Ben.
Wir haben gerade begonnen, mit Googles Material Design Angular-Integration für unsere Animationen zu experimentieren. Es scheint, dass es einige Vorteile bei der Implementierung von Angular/Material gibt. Bisher scheint es eine einfachere Methode zur Modifizierung von Animationen zu sein.
Ihr CodePen für die @keyframes-Animation scheint dasselbe zu sein wie im vorherigen Beispiel.
Vielleicht fallen Sie dem Chome Back Button iframe swapping bug zum Opfer?
Hallo Chris – liebe deine Seite übrigens, bei weitem eine der nützlichsten für dieses Thema.
Ich habe noch nie von diesem Fehler gehört, aber da ich ihn im Neuen Internet Explorer (aka Safari) betrachtete und er sich in neuen Tabs öffnete, bezweifle ich, dass dies der Grund war.
Ich gehe davon aus, dass Sie die @keyframes in CodePen sehen können und dass ich es irgendwie vermasselt habe. Ich habe CodePen lokal mit @keyframes neu erstellt und es funktioniert perfekt, also kein Problem.
@Ben – tolles Tutorial. Danke dafür und für das andere, das Sie (zumindest glaube ich, dass es Sie waren) über Datenvisualisierung mit Angular gemacht haben. Gute Arbeit!