Maison  >  Article  >  interface Web  >  Angular LAB : animer des listes et utiliser AnimationBuilder pour les animations impératives

Angular LAB : animer des listes et utiliser AnimationBuilder pour les animations impératives

Barbara Streisand
Barbara Streisandoriginal
2024-10-10 06:18:29780parcourir

Angular LAB: animating lists and using AnimationBuilder for imperative animations

Saviez-vous qu'Angular inclut un système d'animation complexe ? Je trouve cela particulièrement utile lorsque je souhaite animer des éléments lorsqu'ils entrent dans l'écran ou lorsqu'ils sont détruits !

De plus, vous pouvez utiliser AnimationBuilder afin de impérativement lire, mettre en pause ou arrêter certaines animations personnalisées ! Voyons comment c'est fait.

Créer une liste

Dans cet exercice, nous commençons par créer une liste, quelque chose comme ceci :

@Component({
  selector: 'app-root',
  standalone: true,
  template: `
    <button (click)="addUser()">Add user</button>
    <ul>
    @for (user of users(); track user.id) {
      <li>{{ user.name }}</li>
    }
    </ul>
  `,
})
export class AppComponent {
  users = signal<User[]>([
    { id: Math.random(), name: 'Michele' }
  ]);

  addUser() {
    this.users.update(users => [...users, { id: Math.random(), name: 'New user' }]);
  }
}

Remarquez que nous avons inclus un bouton qui ajoute un utilisateur à la liste !

Animer la liste

Maintenant, et si nous voulons animer le nouvel utilisateur qui sera ajouté ? Tout d'abord, nous voulons dire à Angular que nous souhaitons utiliser son système d'animation en le fournissant dans votre configuration principale :

import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';

bootstrapApplication(AppComponent, {
  providers: [
    provideAnimationsAsync(),
  ]
});

Ensuite, nous pouvons créer notre animation :

import { trigger, transition, style, animate } from '@angular/animations';

const fadeInAnimation = trigger('fadeIn', [
  transition(':enter', [
    style({ transform: 'scale(0.5)', opacity: 0 }),
    animate(
      '.3s cubic-bezier(.8, -0.6, 0.2, 1.5)', 
      style({ transform: 'scale(1)', opacity: 1 })
    )
  ])
])

Avec ces aides nous :

  • Création d'un déclencheur d'animation appelé fadeIn
  • Ajout d'une transition lorsque l'élément entre dans l'écran
  • Appliqué un style initial
  • Démarrage immédiat de l'animation qui se traduira par un nouveau style appliqué à l'élément

Pour plus d'informations sur comment écrire des animations, vous pouvez vous référer au guide officiel qui est génial !

Appliquons maintenant cette animation à chacun de nos éléments de la liste :

@Component({
  ...,
  template: `
    <button (click)="addUser()">Add user</button>
    <ul>
    @for (user of users(); track user.id) {
      <li @fadeIn>{{ user.name }}</li> <!-- Notice here -->
    }
    </ul>
  `,
  // Also, add the animation to the metadata of the component
  animations: [fadeInAnimation]
}) 

Maintenant, lorsqu'un nouvel élément est ajouté, il s'anime ! Notre premier pas est fait.

Notez que pour que notre animation fonctionne correctement, Angular doit suivre chaque élément dans notre for, car sinon il pourrait finir par recréer les mêmes éléments lors de la mise à jour du modèle, ce qui entraînerait des erreurs indésirables animations. Ceci est fourni gratuitement avec la nouvelle syntaxe Control Flow car la propriété track est obligatoire, mais si vous utilisez d'anciennes versions d'Angular avec la directive *ngFor, vous devez utiliser l'option trackBy comme ceci :

<li
  *ngFor="let user of users; trackBy: trackByUserId"
  @fadeIn
>{{ user.name }}</li>
// A class method in your component:
trackByUserId(index, user: User) {
  return user.id;
}

Maintenant, ajoutons un autre type d'animation à notre liste.

AnimationBuilder

Ajoutons un bouton à chaque élément de notre liste :

<li @fadeIn>
  {{ user.name }}
  <button>Make me blink</button>
</li>

Imaginez ceci : nous voulons faire clignoter l'élément lorsque nous appuyons sur le bouton. Ce serait cool ! C'est là qu'intervient le service AnimationBuilder.

Tout d'abord, créons une directive qui sera appliquée à chaque élément. Dans cette directive, nous injecterons à la fois ElementRef et AnimationBuilder :

import { AnimationBuilder, style, animate } from '@angular/animations';

@Directive({
  selector: '[blink]',
  exportAs: 'blink', // <--- Notice
  standalone: true
})
export class BlinkDirective {

  private animationBuilder = inject(AnimationBuilder);
  private el = inject(ElementRef);
}

Remarquez que nous avons exporté la directive : nous en verrons la raison dans quelques secondes.

Ensuite, nous pouvons créer une animation personnalisée comme celle-ci :

export class BlinkDirective {

  ...

  private animation = this.animationBuilder.build([
    style({ transform: 'scale(1)', opacity: 1 }),
    animate(150, style({ transform: 'scale(1.1)', opacity: .5 })),
    animate(150, style({ transform: 'scale(1)', opacity: 1 }))
  ]);
}

Nous utilisons les mêmes fonctions que celles utilisées dans l'animation précédente, juste avec des styles différents.

Maintenant nous souhaitons créer un player qui réalisera l'animation sur notre élément :

export class BlinkDirective {

  ...

  private player = this.animation.create(this.el.nativeElement);
}

Et maintenant, exposons une méthode qui démarrera réellement l'animation !

export class BlinkDirective {

  ...

  start() {
    this.player.play();
  }
}

Il ne reste plus qu'une étape : il faut importer la directive, l'appliquer à nos éléments, la récupérer avec une variable modèle et appeler la méthode lorsque le bouton est enfoncé !

@Component({
  selector: 'app-root',
  standalone: true,
  template: `
    <button (click)="addUser()">Add user</button>
    <ul>
    @for (user of users(); track user.id) {
      <li @fadeIn blink #blinkDir="blink">
        {{ user.name }}
        <button (click)="blinkDir.start()">Make me blink</button>
      </li>
    }
    </ul>
  `,
  imports: [BlinkDirective],
  animations: [
    fadeInAnimation
  ]
})

Nous pouvons récupérer l'instance de la directive et la mettre dans une variable locale car nous avons précédemment exporté la directive avec exportAs. C'est l'élément clé !

Maintenant, essayez de cliquer sur le bouton : l'élément devrait être animé correctement !

L'exercice est terminé, mais ce n'est que la pointe de l'iceberg ! AnimationPlayer dispose de nombreuses commandes que vous pouvez utiliser pour arrêter, mettre en pause et reprendre l'animation. Très cool !

interface AnimationPlayer {
  onDone(fn: () => void): void;
  onStart(fn: () => void): void;
  onDestroy(fn: () => void): void;
  init(): void;
  hasStarted(): boolean;
  play(): void;
  pause(): void;
  restart(): void;
  finish(): void;
  destroy(): void;
  reset(): void;
  setPosition(position: number): void;
  getPosition(): number;
  parentPlayer: AnimationPlayer;
  readonly totalTime: number;
  beforeDestroy?: () => any;
}

Voici notre exemple complet si vous voulez jouer avec : mettez-le simplement dans votre fichier main.ts et voyez-le en action !

import { Component, signal, Directive, ElementRef, inject } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import { trigger, transition, style, animate, AnimationBuilder } from '@angular/animations';
import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';

interface User {
  id: number;
  name: string;
}

@Directive({
  selector: '[blink]',
  exportAs: 'blink',
  standalone: true
})
export class BlinkDirective {

  private animationBuilder = inject(AnimationBuilder);
  private el = inject(ElementRef);

  private animation = this.animationBuilder.build([
    style({ transform: 'scale(1)', opacity: 1 }),
    animate(150, style({ transform: 'scale(1.1)', opacity: .5 })),
    animate(150, style({ transform: 'scale(1)', opacity: 1 }))
  ]);

  private player = this.animation.create(this.el.nativeElement);

  start() {
    this.player.play();
  }
}

const fadeInAnimation = trigger('fadeIn', [
  transition(':enter', [
    style({ transform: 'scale(0.5)', opacity: 0 }),
    animate(
      '.3s cubic-bezier(.8, -0.6, 0.2, 1.5)', 
      style({ transform: 'scale(1)', opacity: 1 })
    )
  ])
])

@Component({
  selector: 'app-root',
  standalone: true,
  template: `
    <button (click)="addUser()">Add user</button>
    <ul>
    @for (user of users(); track user.id) {
      <li @fadeIn blink #blinkDir="blink">
        {{ user.name }}
        <button (click)="blinkDir.start()">Make me blink</button>
      </li>
    }
    </ul>
  `,
  imports: [BlinkDirective],
  animations: [
    fadeInAnimation
  ]
})
export class App {
  users = signal([
    { id: Math.random(), name: 'Michele' }
  ]);

  addUser() {
    this.users.update(users => [...users, { id: Math.random(), name: 'New user' }]);
  }
}

bootstrapApplication(App, {
  providers: [
    provideAnimationsAsync()
  ]
});

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn