• 12 heures
  • Difficile

Ce cours est visible gratuitement en ligne.

course.header.alt.is_video

course.header.alt.is_certifying

J'ai tout compris !

Mis à jour le 20/06/2022

Ajoutez des animations

Pour fournir une expérience moderne et intuitive à vos utilisateurs, les animations sont un outil indispensable. Il y a plein de "moments" où une animation bien conçue facilite l'utilisation de votre application :

  • du feedback visuel pour valider une action de l'utilisateur ;

  • montrer un chargement ou une progression ;

  • attirer l'attention de l'utilisateur sur la prochaine étape d'une suite ;

  • lisser des transitions, ou des arrivées/départs d'éléments de l'écran.

Et encore bien d'autres !

Les animations CSS sont puissantes, mais ne peuvent pas tout faire – pas facilement, du moins ! Si, par exemple, une animation dépend d'une valeur dans votre component, ou si elle doit être déclenchée par un autre événement, ça devient de plus en plus difficile à gérer en CSS pur !

Les animations Angular sont un outil qui permet de répondre à ces besoins, comme vous le découvrirez au cours des prochains chapitres.

Commençons d'abord par créer une animation !

Ajoutez un trigger

Dans ce cours, BrowserAnimationsModule a été importé dans AppModule par Material. Profitons-en pour le déplacer dans les  imports  de CoreModule (car il s'agit d'un module importé une seule fois pour toute l'application) :

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { HeaderComponent } from './components/header/header.component';
import { SharedModule } from '../shared/shared.module';
import { RouterModule } from '@angular/router';
import { HttpClientModule } from '@angular/common/http';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';

@NgModule({
  declarations: [
    HeaderComponent
  ],
  imports: [
    CommonModule,
    SharedModule,
    RouterModule,
    HttpClientModule,
    BrowserAnimationsModule
  ],
  exports: [
    HeaderComponent
  ]
})
export class CoreModule { }

Je vous propose d'ajouter des animations aux commentaires des utilisateurs dans CommentsComponent.

Pour ajouter des animations dans un component, ça se passe dans l'objet de configuration que vous passez à son décorateur  @Component  . Ajoutez un tableau  animations  à CommentsComponent : 

// ...
import { trigger } from '@angular/animations';

@Component({
  selector: 'app-comments',
  templateUrl: './comments.component.html',
  styleUrls: ['./comments.component.scss'],
  animations: [
    trigger('listItem', [

    ])
  ]
})
export class CommentsComponent implements OnInit {
    // ...
}

Le  trigger  que vous ajoutez ici est ce qui permet d'attribuer une ou plusieurs animations aux éléments HTML dans votre template avec la syntaxe  @  :

<!-- ... -->
<h2 matSubheader>Commentaires</h2>
<mat-list-item *ngFor="let comment of comments" @listItem>
<!-- ... -->

Bien sûr, dans l'application, il ne se passe rien encore, car vous n'avez pas encore ajouté d'animation ! Changeons ça tout de suite !

Définissez des états

Le plus souvent, les animations que vous ajoutez à vos applications sont des animations de transition entre différents états.

Ce sera le cas ici lorsque vous voudrez animer les commentaires des utilisateurs quand la souris passe dessus : vous animez la transition entre un état "par défaut" et un état "actif" ou "survolé".

Les animations Angular de ce type sont définies avec  state()  ,  style()  ,  transition()  , et  animate()  :

trigger('listItem', [
  state('default', style({
    transform: 'scale(1)',
    'background-color': 'white',
    'z-index': 1
  })),
  state('active', style({
    transform: 'scale(1.05)',
    'background-color': 'rgb(201, 157, 242)',
    'z-index': 2
  })),
  transition('default => active', [
    animate('100ms ease-in-out')
  ]),
  transition('active => default', [
    animate('500ms ease-in-out')
  ]),
])

Dans cet extrait :

  • vous définissez un état  default  comme état par défaut avec  state , en lui passant le nom de l'état et un appel à  style .  À  style , vous passez les styles qui correspondent à cet état : il est essentiel de "réinitialiser" les styles qui seront modifiés lors du changement d'état ;

  • vous définissez l'état lorsque la souris passera sur l'élément, avec le nom  active  et les styles correspondants ;

  • vous configurez les transitions dans les deux sens en décrivant la  transition  à configurer et en y attribuant un tableau contenant la définition de l'animation avec  animate  – de default à active, ça ira beaucoup plus vite que de active à default.

Tout est prêt, mais votre  trigger ne sait pas encore quand ni comment passer d'un état à l'autre !

Changez d'état

Pour assigner un  state  à un  trigger  dans le template, vous allez utiliser l'attribute binding, avec une variable côté TypeScript qui contiendra soit  'default' , soit  'active'  :

Dans le TypeScript :

listItemAnimationState: 'default' | 'active' = 'default';

Dans le template :

<mat-list-item *ngFor="let comment of comments" [@listItem]="listItemAnimationState">

Du coup, il suffit de modifier la valeur de  listItemAnimationState  lorsque la souris entre et sort des MatListItem :

Dans le TypeScript :

onListItemMouseEnter() {
    this.listItemAnimationState = 'active';
}

onListItemMouseLeave() {
    this.listItemAnimationState = 'default';
}

Et dans le template :

<mat-list-item *ngFor="let comment of comments"
               [@listItem]="listItemAnimationState"
               (mouseenter)="onListItemMouseEnter()"
               (mouseleave)="onListItemMouseLeave()">

On a un seul problème : l'animation a bien lieu, mais sur tous les commentaires à la fois !

Mais c'est normal ! On n'a pas créé de système permettant au component d'assigner des états différents aux différents MatListItem !

Faites la différence

À vous de jouer !

À vous de jouer

Je vous propose un challenge un peu costaud, pour lequel il y a énormément de solutions possibles !

Il faut trouver une façon de gérer les états différents de tous les commentaires dans chaque instance de CommentsComponent.

Chaque itération (c'est un indice !) de  <mat-list-item>  devra pouvoir lire et modifier son état via l'attribute binding du trigger et les méthodes.

Il y a plein d'approches possibles : ci-dessous, je vous donne la mienne. Essayez par vous-même, puis suivez-moi !

Suivez ma solution

Je vous propose de créer un dictionnaire qui associe l'index du commentaire dans le tableau comments à son état :

animationStates: { [key: number]: 'default' | 'active' } = {};

Comme ça, le  trigger  du template peut récupérer son état grâce à l' index  mis à disposition par  *ngFor  :

<mat-list-item *ngFor="let comment of comments; let i = index"
               [@listItem]="animationStates[i]">

Puisque cet objet est vide par défaut, il faut le peupler dans  ngOnInit  :

for (let index in this.comments) {
    this.animationStates[index] = 'default';
}

Pour chaque élément du tableau  comments  , vous associez  'default'  à son index dans l'objet  animationStates . Par exemple, s'il y a trois commentaires,  animationStates  finira comme ça :

animationStates = {
    0: 'default',
    1: 'default',
    2: 'default'
};

Il ne reste plus qu'à modifier les empreintes et implémentations de vos deux méthodes d'événement pour qu'elles interagissent avec le dictionnaire :

onListItemMouseEnter(index: number) {
    this.animationStates[index] = 'active';
}

onListItemMouseLeave(index: number) {
    this.animationStates[index] = 'default';
}

Et leur passer l'index du commentaire depuis le template :

<mat-list-item *ngFor="let comment of comments; let i = index"
               [@listItem]="animationStates[i]"
               (mouseenter)="onListItemMouseEnter(i)"
               (mouseleave)="onListItemMouseLeave(i)"
>

Avec ça, vos commentaires s'animent correctement !

Un commentaire sélectionnée
Un commentaire sélectionné

En résumé

  • Les animations d'un component s'enregistrent au niveau du décorateur  @Component  .

  • On utilise  trigger  pour définir un regroupement d'états et de transitions à assigner aux différents éléments.

  • Les différents états d'un élément animé se définissent avec  state  et les styles passés à  style  .

  • Définir les transitions entre états se fait avec  transition  et  animate  .

  • On assigne un trigger à un élément du template avec  @ , par exemple  @fadeIn  .

  • On peut assigner le state d'un trigger avec l'attribute binding, par exemple [@fadeIn]="faded"  .

Dans le chapitre suivant, vous découvrirez un autre type de transition – vous êtes prêt ?

Exemple de certificat de réussite
Exemple de certificat de réussite