Cours Vue.js · Module 13: Transitions et animations

Transitions nommées et modes de transition

6 min de lecture
Dans cette leçon4

Au NOVA LAB, nous avons de nombreux panneaux - le panneau de diagnostic du réacteur glisse depuis le côté, le panneau d'alertes apparaît avec un effet de fondu, et le panneau de navigation descend depuis le haut. Vue permet de nommer les Transitions, ce qui change les préfixes des classes CSS et permet de définir de nombreuses animations différentes dans une seule application.

Dans la leçon précédente, chaque animation utilisait des classes avec le préfixe v-. Cela fonctionne tant qu'il n'y a qu'une seule animation. Dès que tu en ajoutes une deuxième, les règles de v-enter-from et v-leave-to s'appliquent aux deux panneaux à la fois, et au lieu de glisser depuis le côté, le réacteur se met à apparaître en fondu comme l'alerte. Un deuxième problème surgit quand tu échanges des panneaux : l'ancien est encore en train de sortir alors que le nouveau entre déjà, si bien que pendant un instant les deux occupent de la place et la mise en page saute. Dans cette leçon, tu élimineras ces deux pannes de la Holographic Chamber grâce à deux attributs.

Transitions nommées

Par défaut, <Transition> utilise le préfixe v- pour les classes CSS. L'attribut name change ce préfixe pour le nom choisi :

1<Transition name="slide">
2  <div v-if="visible">...</div>
3</Transition>

Les classes deviennent alors : slide-enter-from, slide-enter-active, slide-enter-to, slide-leave-from, slide-leave-active, slide-leave-to. Le nom n'ajoute à lui seul aucun effet - il change seulement les noms des classes, et c'est toujours en CSS que tu décris l'apparence de l'animation :

1.slide-enter-active,
2.slide-leave-active {
3  transition: all 0.3s ease;
4}
5.slide-enter-from {
6  opacity: 0;
7  transform: translateX(30px);
8}
9.slide-leave-to {
10  opacity: 0;
11  transform: translateX(-30px);
12}

Les classes qui se terminent par -active définissent la durée de la transition et sa courbe, tandis que enter-from et leave-to indiquent d'où l'élément arrive et où il repart. Ce panneau entre par la droite et sort par la gauche.

Grâce aux transitions nommées, tu peux avoir plusieurs types d'animations en même temps :

1<template>
2  <!-- Panneau du réacteur - glisse depuis la droite -->
3  <Transition name="slide-right">
4    <div v-if="showReactor">Reactor Panel</div>
5  </Transition>
6
7  <!-- Alerte - apparaît avec un effet de fondu -->
8  <Transition name="fade">
9    <div v-if="showAlert">Alert!</div>
10  </Transition>
11
12  <!-- Menu - descend depuis le haut -->
13  <Transition name="slide-down">
14    <nav v-if="showMenu">Navigation</nav>
15  </Transition>
16</template>

Chaque <Transition> ne cherche que les classes portant son propre préfixe, donc les styles des trois panneaux ne se gênent pas :

1/* Fade */
2.fade-enter-active, .fade-leave-active { transition: opacity 0.3s; }
3.fade-enter-from, .fade-leave-to { opacity: 0; }
4
5/* Glissement depuis la droite */
6.slide-right-enter-active, .slide-right-leave-active { transition: all 0.4s ease; }
7.slide-right-enter-from { transform: translateX(100%); opacity: 0; }
8.slide-right-leave-to { transform: translateX(100%); opacity: 0; }
9
10/* Glissement depuis le haut */
11.slide-down-enter-active, .slide-down-leave-active { transition: all 0.3s ease; }
12.slide-down-enter-from { transform: translateY(-100%); opacity: 0; }
13.slide-down-leave-to { transform: translateY(-100%); opacity: 0; }

Remarque que le template ne diffère que par la valeur de l'attribut name - toute la différence entre glisser, apparaître en fondu et descendre se trouve dans la feuille de style.

Modes de transition (mode)

Quand tu bascules entre deux éléments dans un seul <Transition>, par défaut les deux s'animent simultanément - l'ancien sort pendant que le nouveau entre. Cela paraît souvent chaotique. L'attribut mode contrôle l'ordre :

  • mode="out-in" - l'ancien élément sort et ce n'est qu'après la fin de son animation que le nouveau entre. C'est le mode le plus utilisé - il évite les « sauts » de mise en page.
  • mode="in-out" - le nouvel élément entre, et l'ancien ne sort qu'ensuite. Rarement utilisé, mais utile pour les effets de superposition.
  • Aucun mode - les deux éléments s'animent simultanément. Peut causer des problèmes de mise en page.

Voici à quoi ressemble un sélecteur de vues du centre de contrôle avec le mode out-in :

1<template>
2  <div class="view-switcher">
3    <button @click="currentView = currentView === 'status' ? 'alerts' : 'status'">
4      Switch View
5    </button>
6
7    <Transition name="fade" mode="out-in">
8      <div v-if="currentView === 'status'" key="status" class="panel">
9        System Status: All nominal
10      </div>
11      <div v-else key="alerts" class="panel">
12        Active Alerts: 3
13      </div>
14    </Transition>
15  </div>
16</template>
17
18<script setup>
19import { ref } from 'vue'
20
21const currentView = ref('status')
22</script>

Après un clic, le panneau d'état disparaît d'abord complètement, et c'est seulement ensuite que le panneau d'alertes apparaît, donc à aucun moment deux panneaux ne se trouvent à l'écran en même temps.

Important : Dans Vue 3, les branches v-if et v-else reçoivent automatiquement des clés uniques, donc le basculement entre deux <div> fonctionne même sans attribut key - une key explicite, comme dans l'exemple, ne fait pas de mal et exprime bien l'intention. Une clé devient indispensable quand le même élément reste en place et que seul son contenu change, par exemple <span :key="count">{{ count }}</span>. Sans :key, Vue remplacerait seulement le texte dans l'élément existant et la transition ne se déclencherait pas du tout.

Basculer entre des composants

mode="out-in" fonctionne très bien avec les composants dynamiques. L'élément <component :is> affiche le composant indiqué dans une variable, et shallowRef le conserve sans réactivité profonde, car seul compte le remplacement du composant entier :

1<template>
2  <Transition name="slide" mode="out-in">
3    <component :is="currentTab" />
4  </Transition>
5</template>
6
7<script setup>
8import { shallowRef } from 'vue'
9import StatusPanel from './StatusPanel.vue'
10import AlertsPanel from './AlertsPanel.vue'
11
12const currentTab = shallowRef(StatusPanel)
13</script>

À chaque changement de currentTab, Vue joue d'abord l'animation de sortie de l'ancien panneau, puis seulement l'animation d'entrée du nouveau. StatusPanel et AlertsPanel sont deux définitions de composants différentes, donc l'animation se lance sans clé supplémentaire.

Astuce pratique

Pour de simples basculements entre des vues, mode="out-in" avec une animation de fondu est la solution la plus élégante. Évite mode="in-out" sauf si tu as une raison précise (par exemple un effet de glissement superposé). L'absence de mode convient surtout quand les deux éléments ont position: absolute et n'influencent pas la mise en page.

Mon conseil : nomme les transitions d'après le mouvement, et non d'après l'endroit où elles servent - slide-right et fade te seront utiles dans dix panneaux, alors que reactor-animation ne servira que dans un seul. La même paire name et mode reviendra dans la leçon sur les transitions entre les routes, où tu en envelopperas tout le <RouterView>.

Retiens : name donne à une animation son propre jeu de classes, et mode fixe la file d'attente - d'abord un panneau disparaît, et seulement ensuite l'autre apparaît.

Code de cette leçon : App.vue
1<script setup>
2import { ref } from 'vue'
3
4const currentView = ref('dashboard')
5const views = ['dashboard', 'sensors', 'crew']
6</script>
7
8<template>
9  <div class="nova-lab">
10    <h1>Transitions nommées et modes</h1>
11
12    <div class="nav">
13      <button
14        v-for="view in views"
15        :key="view"
16        @click="currentView = view"
17        :class="{ active: currentView === view }"
18        class="nav-btn"
19      >
20        {{ view }}
21      </button>
22    </div>
23
24    <!-- Transition avec classes nommées et mode out-in -->
25    <Transition name="slide" mode="out-in">
26      <div :key="currentView" class="panel">
27        <h2>{{ currentView.toUpperCase() }}</h2>
28        <p v-if="currentView === 'dashboard'">Panneau principal de la station NOVA LAB</p>
29        <p v-else-if="currentView === 'sensors'">Lectures des capteurs atmosphériques</p>
30        <p v-else>Liste des membres d'équipage</p>
31      </div>
32    </Transition>
33  </div>
34</template>
35
36<style scoped>
37.nova-lab {
38  background: #0a0e27;
39  color: #00ff88;
40  min-height: 100vh;
41  padding: 20px;
42  font-family: monospace;
43}
44h1 { color: #00ff88; }
45
46.nav { display: flex; gap: 10px; margin: 15px 0; }
47.nav-btn {
48  background: rgba(0, 180, 216, 0.2);
49  color: #00b4d8;
50  border: 1px solid #00b4d8;
51  padding: 8px 16px;
52  border-radius: 6px;
53  cursor: pointer;
54}
55.nav-btn.active {
56  background: #00b4d8;
57  color: white;
58}
59
60.panel {
61  background: rgba(0, 255, 136, 0.05);
62  border: 2px solid #00ff88;
63  padding: 20px;
64  border-radius: 12px;
65}
66
67/* classes slide- (name="slide") */
68.slide-enter-active,
69.slide-leave-active {
70  transition: all 0.3s ease;
71}
72.slide-enter-from {
73  opacity: 0;
74  transform: translateX(30px);
75}
76.slide-leave-to {
77  opacity: 0;
78  transform: translateX(-30px);
79}
80</style>

Tu as repéré une erreur dans cette leçon ?

Teste tes connaissances

Réponds aux questions de cette leçon. Choisis une réponse pour voir tout de suite si elle est correcte.

  1. 1. Que se passe-t-il quand tu ajoutes l'attribut name='slide' à <Transition> ?

  2. 2. Que fait mode='out-in' dans <Transition> ?

Voici 2 des 3 questions de cette leçon. Réponds aux autres dans le jeu.

Tâches pratiques dans le jeu

  • Éditeur de code

    Ajoute <Transition name='slide'> et les classes CSS slide-* correspondantes.

  • Tri horizontal

    Range la syntaxe correcte de Transition avec le mode out-in :

  • Tri vertical

    Range les classes CSS dans l'ordre où elles apparaissent pendant l'animation de sortie de l'élément :

  • Éditeur de code

    Utilise <Transition mode='out-in'> pour un basculement fluide entre composants.