Animationen: der Angular Weg

Avatar of Benjamin Simmons
Benjamin Simmons am

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

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 Ereignisklassen

Die 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.