10 sie 2023
5 min

Programowanie animacji w Angularze

Animacje odgrywają ważną rolę w obszarze user experience. Nie tylko usprawniają warstwę wizualną, ale posiadają także praktyczne zastosowania. Są one dość powszechne, więc każdy Angular Developer powinien być z nimi zaznajomiony. 

W artykule wyjaśnię jak programistycznie kontrolować animacje w celu usprawnienia doświadczeń użytkownika.

Jaki jest cel korzystania z animacji?

Wiele firm wykorzystuje animacje aby wspomóc użytkownika w poruszaniu się po aplikacji, ułatwić proces przekazywania informacji oraz wyjaśnić proste procesy użytkownikowi.

Do najczęstszych przykładów wykorzystania animacji można zaliczyć hover oraz click. Oprócz zmiany kursora w pointera, dodanie animacji hover do klikalnych elementów jest szybką metodą zakomunikowania użytkownikowi, że dany element jest klikalny. Z drugiej strony, animacja kliknięcia (np. efekt ripple na buttonach Angular Materiala) zapewnia użytkownikowi natychmiastową informację o tym, że aplikacja zarejestrowała wykonaną przez niego akcję. 

Jak dodać animację do aplikacji angularowej?

Jest wiele sposobów na to, aby dodać animację do aplikacji, łącznie z wbudowanym modułem animacji, który może być wykorzystany bez konieczności instalowania dodatkowych zależności. 

Animacje w angularze są zazwyczaj używane z wykorzystaniem tablicy `animations` w dekoratorze `Component`. Animacje te są następnie uruchamiane są poprzez zastosowanie dyrektyw animacji (z prefiksem `@`, takie jak `@slideIn` czy `@fadeOut`) na docelowym elemencie. 

import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { trigger, style, animate, transition } from '@angular/animations';

@Component({
  standalone: true,
  imports: [CommonModule],
  selector: 'app-root'
  template: `
    <div class="card-container">
        <div *ngIf="cardIsDisplayed" @fadeSlideInOut class="demo-card"></div>
    </div>
  `,
  animations: [
    trigger('fadeSlideInOut', [
      transition(':enter', [
        style({ opacity: 0, transform: 'translateY(10px)' }),
        animate('500ms', style({ opacity: 1, transform: 'translateY(0)' })),
      ]),
      transition(':leave', [
        animate('500ms', style({ opacity: 0, transform: 'translateY(10px)' })),
      ]),
    ]),
  ],
})
export class AppComponent {
  cardIsDisplayed = true;

  toggleCard(): void {
    this.cardIsDisplayed = !this.cardIsDisplayed;
  }
}

To podejście pokrywa większość przypadków użycia oraz jest wygodnym sposobem na dodanie animacji do twojej aplikacji angularowej.

Jak edytować animacje w aplikacji angularowej?

Angular Animations zawiera klasę `AnimationBuilder`, dzięki której można tworzyć animacje programistycznie.

Oprócz podstawowych funkcji Angular Animations, zaprogramowane animacje za pośrednictwem `AnimationPlayer` zawierają dodatkowe funkcje do kontrolowania animacji, które nie są możliwe przy użyciu podejścia szablonowego. Funkcje te obejmują: 

  • `pause` – wstrzymuje animacje
  • `finish` – kończy animacje
  • `restart` – wznawia wstrzymaną animację
  • `destroy`- niszczy animacje
  • `reset` – resetuje animacje do stanu pierwotnego

Klasa `AnimationPlayer` także dostarcza callbacki dla kluczowych eventów animacji, takich jak: 

  • `onStart` – wywołana podczas startu animacji
  • `onDone` – wywołana przy zakończeniu animacji (automatycznie bądź przez wywołanie funkcji `finish` na uruchomionej aplikacji)
  • `onDestroy` – wywołana po zniszczeniu animacji (kiedy wywoływana jest funkcja `destroy`)

Możesz również przekazać callback do funkcji `beforeDestroy` jeżeli chcesz wykonać kod przed zniszczeniem animacji.

Podstawowe wykorzystanie

Spójrzmy na to, jak możemy wykorzystać `AnimationBuilder` oraz `AnimationPlayer` w celu uruchomienia animacji.

Zacznijmy od stworzenia docelowego elementu oraz kilku przycisków do kontrolowania animacji.

```html
<!-- app.component.html –>

<div class="animation-demo-container">
  <div class="card-container">
      <div #demoCard class="demo-card"></div>
  </div>
  <div class="demo-controller-container">
    <button class="demo-button" (click)="playAnimation()">Play</button>
    <button class="demo-button" (click)="pauseAnimation()">Pause</button>
    <button class="demo-button" (click)="stopAnimation()">Stop</button>
    <button class="demo-button" (click)="resetAnimation()">Reset</button>
  </div>
</div>
```

Dodajmy kilka styli do naszego komponentu: 

```scss
// app.component.scss

.animation-demo-container {
    border-radius: 0.5rem;
    border-width: 1px;
    border-color: rgb(14 116 144);
    background-color: rgb(30 41 59);
    display: flex;
    flex-direction: column;
    min-height: 350px;
    overflow: auto;
    .card-container {
        flex-grow: 1;
        padding: 1.25rem;
        .demo-card {
            width: 10rem;
            height: 10rem;
            border-radius: 0.5rem;
            background-color: rgb(125 211 252);
        }
    }
    .demo-controller-container {
        display: flex;
        flex-direction: row;
        padding: 1rem 1.25rem;
        border-radius: 0 0 0.5rem 0.5rem;
        gap: 0.75rem;
        .demo-button {
            background-color: rgb(255, 255, 255);
            color: rgb(15 23 42);
            border-radius: 0.25rem;
            padding: 0.5rem 0.75rem;
            font-weight: 500;
        }
    }
  }
```

Przejdźmy teraz do animacji. Najpierw będziemy musieli uzyskać referencję do animowanego elementu. W tym celu użyjemy dekoratora `ViewChild` i przekażemy identyfikator elementu (`#demoCard`), który zdefiniowaliśmy wcześniej, aby uzyskać do niego dostęp.

```typescript
// app.component.ts

import { Component, ElementRef, ViewChild } from '@angular/core';
import { CommonModule } from '@angular/common';

@Component({
  standalone: true,
  imports: [CommonModule],
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: [‘./app.component.scss’]
})
export class AppComponent {
  @ViewChild('demoCard') demoCard: ElementRef | undefined;
}
```

Dodaj klasę `AnimationBuilder` i utwórz prywatną funkcję, aby zbudować animację i utworzyć instancję `AnimationPlayer`, której będziemy używać do uruchamiania naszych animacji.

```typescript
// app.component.ts

import { Component, ElementRef, ViewChild, inject } from '@angular/core';
import { CommonModule } from '@angular/common';
import { style, animate, AnimationBuilder, AnimationPlayer } from '@angular/animations';

@Component({
  standalone: true,
  imports: [CommonModule],
  selector: 'app-root,
  templateUrl: './app.component.html',
  styleUrls: [‘./app.component.scss’]
})
export class AppComponent {
  @ViewChild('demoCard') demoCard: ElementRef | undefined;
  
  private animationBuilder = inject(AnimationBuilder);
  private animationPlayer: AnimationPlayer | undefined;
  private getAnimationPlayer(): AnimationPlayer | undefined {
    if (!this.demoCard?.nativeElement) {
      return;
    }
    if (!this.animationPlayer) {
      const factory = this.animationBuilder.build(
        [
          style({ transform: 'rotate(0deg)' }),
          animate('1000ms cubic-bezier(0.175, 0.885, 0.32, 1.275)', style({ transform: 'rotate(360deg)' }))
        ]
      );
      this.animationPlayer = factory.create(this.demoCard.nativeElement);
    }
    return this.animationPlayer;
  }
}
```

Na koniec stwórzmy funkcje play, stop, pause i reset. Funkcje te będa pobierać `AnimationPlayer` poprzez wywołanie `getAnimationPlayer()`, a następnie będą wykonywać odpowiednie metody.

```typescript
// app.component.ts

import { Component, ElementRef, ViewChild, inject } from '@angular/core';
import { CommonModule } from '@angular/common';
import { style, animate, AnimationBuilder, AnimationPlayer } from '@angular/animations';

@Component({
  standalone: true,
  imports: [CommonModule],
  selector: 'app-root,
  templateUrl: './app.component.html',
  styleUrls: [‘./app.component.scss’]
})
export class AppComponent {
  @ViewChild('demoCard') demoCard: ElementRef | undefined;
  
  private animationBuilder = inject(AnimationBuilder);
  private animationPlayer: AnimationPlayer | undefined;
  
  playAnimation(): void { 
    const player = this.getAnimationPlayer();
    if (!player) {
      return;
    }
    player.play();
  }

  pauseAnimation(): void {
    const player = this.getAnimationPlayer();
    if (!player || !player.hasStarted) {
      return;
    }
    player.pause();
  }

  stopAnimation(): void {
    const player = this.getAnimationPlayer();
    if (!player || !player.hasStarted) {
      return;
    }
    player.finish();
  }

  resetAnimation(): void {
    const player = this.getAnimationPlayer();
    if (!player || !player.hasStarted) {
      return;
    }
    player.reset();
  }

  private getAnimationPlayer(): AnimationPlayer | undefined {
    if (!this.demoCard?.nativeElement) {
      return;
    }
    if (!this.animationPlayer) {
      const factory = this.animationBuilder.build(
        [
          style({ transform: 'rotate(0deg)' }),
          animate('1000ms cubic-bezier(0.175, 0.885, 0.32, 1.275)', style({ transform: 'rotate(360deg)' }))
        ]
      );
      this.animationPlayer = factory.create(this.demoCard.nativeElement);
    }
    return this.animationPlayer;
  }
}
```

Jeżeli postępowałeś zgodnie z instrukcjami, powinieneś uzyskać następujące animacje:

Możesz odwiedzić Angular Animations Explorer aby zobaczyć live demo.

Podsumowując

Istnieje wiele sposobów na korzystanie z Angular Animations, z których każdy ma swoje idealne przypadki użycia. Jeżeli chcesz jedynie wywołać animacje, wybierz prostsze podejście szablonowe. Jeśli jednak potrzebujesz bardziej szczegółowej kontroli, użyj podejścia programistycznego, używając `AnimationBuilder` oraz `AnimationPlayer`. Możesz również sprawdzić Angular Animations Explorer, aby zobaczyć inne podejścia do dodawania animacji w Angularze.

Jeżeli interesują cie takie treści lub masz jakieś pytania, zostaw komentarz bądź skontaktuj się ze mną na Twitterze @williamjuan27.

Podziel się artykułem

Zapisz się na nasz newsletter

Dołącz do community Angular.love i bądź na bieżąco z trendami.