Important : Le package @angular/animations est désormais déprécié. L'équipe Angular recommande d'utiliser du CSS natif avec animate.enter et animate.leave pour les animations dans tout nouveau code. Pour en savoir plus, consultez le nouveau guide des animations d'entrée et de sortie. Consultez aussi Migrer depuis le package Animations d'Angular pour découvrir comment commencer à migrer vers des animations en CSS pur dans vos applications.
Jusqu'ici, nous avons vu des animations simples d'éléments HTML uniques. Angular vous permet aussi d'animer des séquences coordonnées, comme une grille entière ou une liste d'éléments lorsqu'ils entrent dans une page ou en sortent. Vous pouvez choisir d'exécuter plusieurs animations en parallèle, ou d'exécuter des animations distinctes de manière séquentielle, l'une après l'autre.
Les fonctions qui contrôlent les séquences d'animation complexes sont :
| Fonctions | Détails |
|---|---|
query() |
Trouve un ou plusieurs éléments HTML internes. |
stagger() |
Applique un délai en cascade aux animations de plusieurs éléments. |
group() |
Exécute plusieurs étapes d'animation en parallèle. |
sequence() |
Exécute des étapes d'animation l'une après l'autre. |
La fonction query()
La plupart des animations complexes s'appuient sur la fonction query() pour trouver des éléments enfants et leur appliquer des animations. Voici quelques exemples de base :
| Exemples | Détails |
|---|---|
query() suivi de animate() |
Permet de cibler des éléments HTML simples et de leur appliquer directement des animations. |
query() suivi de animateChild() |
Permet de cibler des éléments enfants, qui ont eux-mêmes des métadonnées d'animation, et de déclencher ces animations (qui seraient sinon bloquées par l'animation de l'élément courant/parent). |
Le premier argument de query() est une chaîne de selector CSS qui peut aussi contenir les jetons spécifiques à Angular suivants :
| Jetons | Détails |
|---|---|
:enter :leave |
Pour les éléments qui entrent/sortent. |
:animating |
Pour les éléments en cours d'animation. |
@* @triggerName |
Pour les éléments ayant un déclencheur quelconque ou un déclencheur précis. |
:self |
L'élément animé lui-même. |
Éléments qui entrent et qui sortent
Tous les éléments enfants ne sont pas réellement considérés comme entrants/sortants ; cela peut parfois être contre-intuitif et déroutant. Consultez la documentation de l'API query pour plus d'informations.
Vous pouvez aussi en voir une illustration dans l'exemple d'animations (présenté dans la section d'introduction des animations), sous l'onglet Querying.
Animer plusieurs éléments avec les fonctions query() et stagger()
Après avoir ciblé des éléments enfants avec query(), la fonction stagger() vous permet de définir un intervalle de temps entre chaque élément, pour les animer avec un délai entre eux.
L'exemple suivant montre comment utiliser les fonctions query() et stagger() pour animer une liste (de héros) en ajoutant chaque élément l'un après l'autre, avec un léger délai, de haut en bas.
Utilisez
query()pour rechercher un élément entrant dans la page qui répond à certains critèresPour chacun de ces éléments, utilisez
style()pour lui appliquer le même style initial. Rendez-le transparent et utiliseztransformpour le déplacer hors de sa position afin qu'il puisse glisser jusqu'à sa place.Utilisez
stagger()pour retarder chaque animation de 30 millisecondesAnimez chaque élément à l'écran pendant 0,5 seconde avec une courbe d'easing personnalisée, en le faisant apparaître en fondu et en annulant sa transformation simultanément
hero-list-page.ts
animations: [
trigger('pageAnimations', [
transition(':enter', [
query('.hero', [
style({opacity: 0, transform: 'translateY(-100px)'}),
stagger(30, [
animate('500ms cubic-bezier(0.35, 0, 0.25, 1)', style({opacity: 1, transform: 'none'})),
]),
]),
]),
]),
Animation parallèle avec la fonction group()
Vous avez vu comment ajouter un délai entre chaque animation successive.
Mais vous pouvez aussi vouloir configurer des animations qui se déroulent en parallèle.
Par exemple, vous pouvez vouloir animer deux propriétés CSS du même élément en utilisant une fonction d'easing différente pour chacune.
Pour cela, vous pouvez utiliser la fonction d'animation group().
Utile : La fonction group() sert à regrouper des étapes d'animation, et non des éléments animés.
L'exemple suivant utilise des group() sur :enter et sur :leave pour deux configurations de timing différentes, appliquant ainsi deux animations indépendantes au même élément en parallèle.
hero-list-groups.ts (extrait)
animations: [
trigger('flyInOut', [
state(
'in',
style({
width: '*',
transform: 'translateX(0)',
opacity: 1,
}),
),
transition(':enter', [
style({width: 10, transform: 'translateX(50px)', opacity: 0}),
group([
animate(
'0.3s 0.1s ease',
style({
transform: 'translateX(0)',
width: '*',
}),
),
animate(
'0.3s ease',
style({
opacity: 1,
}),
),
]),
]),
transition(':leave', [
group([
animate(
'0.3s ease',
style({
transform: 'translateX(50px)',
width: 10,
}),
),
animate(
'0.3s 0.2s ease',
style({
opacity: 0,
}),
),
]),
]),
]),
],
Animations séquentielles ou parallèles
Les animations complexes peuvent impliquer beaucoup de choses en même temps.
Mais que faire si vous voulez créer une animation composée de plusieurs animations qui se succèdent ? Plus haut, vous avez utilisé group() pour exécuter plusieurs animations en même temps, en parallèle.
Une deuxième fonction, sequence(), vous permet d'exécuter ces mêmes animations l'une après l'autre.
Dans sequence(), les étapes d'animation sont des appels aux fonctions style() ou animate().
- Utilisez
style()pour appliquer immédiatement les données de style fournies. - Utilisez
animate()pour appliquer les données de style sur un intervalle de temps donné.
Exemple d'animation de filtre
Regardez une autre animation de la page d'exemple.
Sous l'onglet Filter/Stagger, saisissez du texte dans la zone de texte Search Heroes, par exemple Magnet ou tornado.
Le filtre fonctionne en temps réel pendant la saisie. Des éléments quittent la page à chaque nouvelle lettre saisie, à mesure que le filtre devient plus strict. La liste des héros réapparaît progressivement dans la page à mesure que vous effacez chaque lettre de la zone de filtre.
Le template HTML contient un déclencheur nommé filterAnimation.
hero-list-page.html
<label for="search">Search heroes: </label>
<input
type="text"
id="search"
#criteria
(input)="updateCriteria(criteria.value)"
placeholder="Search heroes"
/>
<ul class="heroes" [@filterAnimation]="heroesTotal">
@for (hero of heroes; track hero) {
<li class="hero">
<div class="inner">
<span class="badge">{{ hero.id }}</span>
<span class="name">{{ hero.name }}</span>
</div>
</li>
}
</ul>
Le filterAnimation du décorateur du composant contient trois transitions.
hero-list-page.ts
@Component({
animations: [
trigger('filterAnimation', [
transition(':enter, * => 0, * => -1', []),
transition(':increment', [
query(
':enter',
[
style({opacity: 0, width: 0}),
stagger(50, [animate('300ms ease-out', style({opacity: 1, width: '*'}))]),
],
{optional: true},
),
]),
transition(':decrement', [
query(':leave', [stagger(50, [animate('300ms ease-out', style({opacity: 0, width: 0}))])]),
]),
]),
],
})
export class HeroListPage implements OnInit {
heroesTotal = -1;
get heroes() {
return this._heroes;
}
private _heroes: Hero[] = [];
ngOnInit() {
this._heroes = HEROES;
}
updateCriteria(criteria: string) {
criteria = criteria ? criteria.trim() : '';
this._heroes = HEROES.filter((hero) =>
hero.name.toLowerCase().includes(criteria.toLowerCase()),
);
const newTotal = this.heroes.length;
if (this.heroesTotal !== newTotal) {
this.heroesTotal = newTotal;
} else if (!criteria) {
this.heroesTotal = -1;
}
}
}
Le code de cet exemple effectue les tâches suivantes :
- Ignore les animations quand l'utilisateur ouvre cette page pour la première fois ou y navigue (l'animation de filtre réduit ce qui est déjà présent, elle ne fonctionne donc que sur des éléments qui existent déjà dans le DOM)
- Filtre les héros selon la valeur du champ de recherche
Pour chaque changement :
Masque un élément qui quitte le DOM en mettant son opacité et sa largeur à 0
Anime un élément qui entre dans le DOM pendant 300 millisecondes. Pendant l'animation, l'élément reprend sa largeur et son opacité par défaut.
S'il y a plusieurs éléments qui entrent dans le DOM ou en sortent, échelonne chaque animation en commençant par le haut de la page, avec un délai de 50 millisecondes entre chaque élément
Animer les éléments d'une liste réordonnée
Bien qu'Angular anime correctement les éléments d'une liste *ngFor par défaut, il n'en est plus capable si leur ordre change.
En effet, il perd la trace de quel élément est lequel, ce qui donne des animations cassées.
La seule façon d'aider Angular à suivre ces éléments est d'assigner une TrackByFunction à la directive NgForOf.
Ainsi, Angular sait toujours quel élément est lequel, ce qui lui permet d'appliquer à tout moment les bonnes animations aux bons éléments.
Important : Si vous devez animer les éléments d'une liste *ngFor et que l'ordre de ces éléments risque de changer à l'exécution, utilisez toujours une TrackByFunction.
Animations et encapsulation des vues des composants
Les animations Angular reposent sur la structure DOM des composants et ne tiennent pas directement compte de l'encapsulation des vues : les composants utilisant ViewEncapsulation.Emulated se comportent donc exactement comme s'ils utilisaient ViewEncapsulation.None (ViewEncapsulation.ShadowDom et ViewEncapsulation.ExperimentalIsolatedShadowDom se comportent différemment, comme nous le verrons plus loin).
Par exemple, si la fonction query() (que vous verrez davantage dans la suite du guide des animations) était appliquée au sommet d'un arbre de composants utilisant l'encapsulation des vues émulée, cette query pourrait identifier (et donc animer) des éléments DOM à n'importe quelle profondeur de l'arbre.
En revanche, ViewEncapsulation.ShadowDom et ViewEncapsulation.ExperimentalIsolatedShadowDom modifient la structure DOM du composant en « masquant » des éléments DOM dans des éléments ShadowRoot. Ces manipulations du DOM empêchent une partie de l'implémentation des animations de fonctionner correctement, car elle repose sur des structures DOM simples et ne tient pas compte des éléments ShadowRoot. Il est donc conseillé d'éviter d'appliquer des animations à des vues qui incluent des composants utilisant l'encapsulation des vues ShadowDom.
Récapitulatif des séquences d'animation
Les fonctions Angular servant à animer plusieurs éléments commencent par query() pour trouver des éléments internes ; par exemple, rassembler toutes les images d'une <div>.
Les autres fonctions, stagger(), group() et sequence(), appliquent des cascades ou vous permettent de contrôler la façon dont plusieurs étapes d'animation sont appliquées.
En savoir plus sur les animations Angular
Ceci pourrait aussi vous intéresser :