Neuigkeiten von trion.
Immer gut informiert.

Angular SVG Animationen

Nachdem in diesem Beitrag zu SVG Animationen die Grundlagen von SVG Animationen erläutert wurden, geht es in diesem Beitrag darum, wie SVG Animationen mit Angular umgesetzt werden können.

Angular Animations

Um interaktive Web-Anwendungen erstellen zu können, ist JavaScript heutzutage unerlässlich. Auch interaktive SVG-Grafiken können per JavaScript umgesetzt, animiert und in Web-Anwendungen eingebaut werden. In Angular-Anwendungen kann über die nativen Möglichkeiten hinaus das Angular Animations-System verwendet werden. Das Angular Animations-System basiert auf CSS und CSS-Animationen, sodass mit diesem System alle Eigenschaften animierbar sind, welche auch mit CSS animierbar sind. Die Angular Animationen sind sehr gut mit der allgemeinen Angular-Logik integriert. So lässt sich zum Beispiel durch Einsatz von Animations-Triggern das Ein- und Ausblenden von DOM-Elementen mit Angular leicht animieren. Darüber hinaus bietet Angular die Möglichkeit, sich per Callback an den Start und das Ende einer Animation zu hängen - etwa um eine weitere Animation zu starten.
Im folgenden Beispiel wollen wir uns eine Angular-Komponente anschauen, in dem nach Click auf einen Button per Angular-Animation ein SVG-Rechteck eingeblendet wird. Im zweiten Schritt werden wir TypeScript-Logik hinzufügen, um das Rechteck rotieren zu lassen, sobald darauf geclickt wird.

Angular Animationen sind an sogenannte Trigger gebunden, welche in Angular-Templates mit einem @ gekennzeichet werden. In folgendem Template binden wir die Einblend-Animation der SVG-Grafik an den Trigger showSvg. Mit @showSvg.done ist der Animations-Callback, der nach Ende der Animation aufgerufen wird. In diesem Fall wird direkt nach Ende der Einblend-Animation die Funktion toggleAnimation() aufgerufen, die die Rotation des Rechtecks startet.

Angular-Template mit zu animierenden Elementen (svg-demo.component.html).
<div>
  <button (click)="svgVisible = !svgVisible">Toggle Square</button>
  <svg *ngIf="svgVisible" @showSvg (@showSvg.done)="toggleAnimation()" height="250" width="600">
    <rect (click)="toggleAnimation()"
          #rect
          x="175"
          y="50"
          width="150"
          height="150"
          fill="lightgreen"
          stroke="darkgreen">
    </rect>
  </svg>
</div>

In folgendem Code-Ausschnitt sind die zugehörigen Komponenten-Metadaten dargestellt. Das Property animations enthält die Definition des showSvg-Trigger, den wir oben im Template verwendet haben. Hier wird eine weitere wichtige Eigenschaft der Angular-Animationen deutlich: Sie beruhen auf "Transitions" von einem Zustand in einen anderen. Hier werden zwei besondere Transitions zu Animation verwendet: :enter ist die Transition, die ausgeführt wird, wenn ein Element neu in den DOM eingehangen wird. Wird das SVG also (hier per *ngIf) in den DOM eingehangen, so Ist es zunächst unsichtbar (opacity: 0) und wird dann innerhalb einer Sekunde langsam sichtbar werden (opacity: 1). Die :leave-Transition wird ausgeführt, kurz bevor ein Element aus dem DOM entfernt wird. Hier wird das SVG also innerhlab von 0.5 Sekunden langsam durchsichtig, bevor es aus dem DOM genommen wird.

Angular Komponenten-Metadaten mit Animationsdefinitionen
@Component({
  selector: 'app-svg-demo',
  templateUrl: './svg-demo.component.html',
  styleUrls: ['./svg-demo.component.css'],
  animations: [
    trigger('showSvg', [
      transition(':enter', [
        style({opacity: 0}),
        animate('1s', style({opacity: 1})),
      ]),
      transition(':leave', [
        animate('0.5s', style({opacity: 0}))
      ])
    ])
  ]
})
export class SvgDemoComponent {
  svgVisible = false;
  //...
}

Per Styling werden Button und SVG nebeneinander angezeigt. Da das Rechteck sich um sein Zentrum drehen soll und nicht um den Nullpunkt der SVG-Grafik (dieser liegt in der linken oberen Ecke des SVG), muss der transform-origin des Rechtecks gesetzt werden. In diesem Fall liegt das Zentrum des Rechteck bei x=250px und y=125px.

Angular Komponenten-Styling (svg-demo.component.css)
div {
  display: flex;
  align-items: center
}

rect {
  transform-origin: 250px 125px;
}

Um die Rotation des Rechtecks zu starten ist nun vor allem die Funktion toggleAnimation bzw. die davion aufgerufene Funktion startAnimation wichtig. Innerhalb von startAnimation() ist der Animations-Callback rotationAnimation definiert. In diesem wird jeweils der momentane Rotationswinkel des Rechtecks um 1 erhöht (Innerhalb des Intervalls 0 bis 359 Grad). Dieser Winkel wird dann mit Hilfe der CSS-rotate() Funktion in das transform-Attribut des SVG-Elementes geschrieben. Die Rotation wird dann über rekursive Aufrufe des rotationAnimation-Callbacks erreicht, wobei dafür jedesmal ein Browser-AnimationFrame durch die Funktion requestAnimationFrame angefragt werden muss. Um eine möglichst flüßige Animation zu gewährleisten, sollte Overhead - etwa durch Angular-Logik wie die Change-Detection - vermieden werden. Da Angular nicht direkt an der Rotations-Animation beteiligt ist, kann und sollte die Animation außerhalb des Angular-Ausführungskontexts erfolgen. Dies geschieht durch den Funktionsaufruf this.zone.runOutsideAngular(), von dem aus die Animation letztlich gestartet wird.

Beispiel für in Angular/TypeScript geschriebene Rotationsanimation
export class SvgDemoComponent {
  svgVisible = false;

  @ViewChild('rect') rectangle: ElementRef<SVGElement>;

  private angle = 0;
  private animationId: number | undefined;

  constructor(private zone: NgZone) {}

  toggleAnimation() {
    if (this.animationId) {
      this.cancelAnimation();
      return;
    }
    this.startAnimation();
  }

  private startAnimation() {
    const rotationAnimation = () => {
      if (!this.rectangle) {
        this.cancelAnimation();
        return;
      }
      this.angle = (this.angle + 1) % 360;
      this.rectangle.nativeElement
        .setAttribute('transform', `rotate(${this.angle})`);
      this.animationId = requestAnimationFrame(rotationAnimation);
    };

    this.zone.runOutsideAngular(() => {
      this.animationId = requestAnimationFrame(rotationAnimation);
    });
  }

  private cancelAnimation() {
    cancelAnimationFrame(this.animationId);
    this.animationId = undefined;
  }
}

Die Funktion cancelAnimation() wird aufgerufen, wenn bei Click auf das Rechteck schon eine Animation läuft. In diesem Fall wird per cancelAnimationFrame die Animation einfach abgebrochen.

Fazit

In dieser Artikelserie haben wir gesehen, welche Möglichkeiten es für die Animation von SVG-Grafiken gibt und wann sie angewendet werden sollten. Speziell für Animationen in interaktiven Anwendungen wird in Zukunft noch eine weitere Möglichkeit durch die Browser bereitgestellt werden: Die "Web Animations API" (https://caniuse.com/#feat=web-animation) soll im Zusammenspiel mit den hier vorgestellten CSS-Animations einen Standard bilden, mit dem Animationen in Web Anwendungen einfach und effizient entwickelt werden können.

In einem Folgeartikel betrachten wir, wie Touch und Swipe Gesten in Kombination mit SVG Elementen und Angular umgesetzt werden können.




Zu den Themen Angular, Architektur von SPA Anwendungen und TypeScript bieten wir sowohl Beratung, Entwicklungsunterstützung als auch passende Schulungen an.

Auch für Ihren individuellen Bedarf können wir Workshops und Schulungen anbieten. Sprechen Sie uns gerne an.

Feedback oder Fragen zu einem Artikel - per Twitter @triondevelop oder E-Mail freuen wir uns auf eine Kontaktaufnahme!

Zur Desktop Version des Artikels