Passer au contenu

Transition

Vue propose deux composants natifs qui peuvent aider à travailler avec des transitions et des animations en réponse à un changement d'état :

  • <Transition>pour appliquer des animations lorsqu'un élément ou un composant entre et sort du DOM. Ceci est couvert sur cette page.

  • <TransitionGroup> pour appliquer des animations lorsqu'un élément ou un composant est inséré, supprimé ou déplacé dans une liste v-for. Ceci est couvert dans le chapitre suivant.

Outre ces deux composants, nous pouvons également appliquer des animations dans Vue en utilisant d'autres techniques telles que le basculement des classes CSS ou des animations basées sur l'état via des liaisons de style. Ces techniques supplémentaires sont traitées dans le chapitre Techniques d'animation.

Le composant <Transition>

<Transition> est un composant intégré : cela signifie qu'il est disponible dans n'importe quel template de composant sans avoir à l'enregistrer. Il peut être utilisé pour appliquer des animations d'entrée et de sortie sur des éléments ou des composants qui lui sont transmis via son slot par défaut. L'entrée ou la sortie peut être déclenchée par l'une des actions suivantes :

  • Rendu conditionnel via v-if
  • Affichage conditionnel via v-show
  • Basculement des composants dynamiques via l'élément spécial <component>
  • Changer l'attribut spécial key

Voici un exemple de l'utilisation la plus basique :

template
<button @click="show = !show">Toggle</button>
<Transition>
  <p v-if="show">hello</p>
</Transition>
css
/* nous vous expliquerons ensuite ce que font ces classes ! */
.v-enter-active,
.v-leave-active {
  transition: opacity 0.5s ease;
}

.v-enter-from,
.v-leave-to {
  opacity: 0;
}

hello

TIP

<Transition> ne prend en charge qu'un seul élément ou composant comme contenu du slot. Si le contenu est un composant, le composant doit également avoir un seul élément racine.

Lorsqu'un élément d'un composant <Transition> est inséré ou supprimé, voici ce qui se passe :

  1. Vue détectera automatiquement si l'élément cible a des transitions CSS ou des animations appliquées. Si c'est le cas, un certain nombre de classes de transition CSS seront ajoutées / supprimées aux moments appropriés.

  2. S'il existe des écouteurs pour les hooks JavaScript, ces hooks seront appelés aux moments appropriés.

  3. Si aucune transition / animation CSS n'est détectée et qu'aucun hook JavaScript n'est fourni, les opérations DOM d'insertion et / ou de suppression seront exécutées lors du prochain rafraîchissement d'animation du navigateur.

Transitions basées sur le CSS

Classes de transition

Six classes sont appliquées pour les transitions entrée / sortie.

Diagramme de transition

  1. v-enter-from : état de départ pour l'entrée. Ajoutée avant l'insertion de l'élément, supprimée une frame après l'insertion de l'élément.

  2. v-enter-active : état actif pour l'entrée. Appliquée pendant toute la phase d'entrée. Ajoutée avant l'insertion de l'élément, supprimée à la fin de la transition / animation. Cette classe peut être utilisée pour définir la durée, le retard et la courbe d'accélération de la transition entrante.

  3. v-enter-to : état de fin pour l'entrée. Ajoutée une frame après l'insertion de l'élément (en même temps v-enter-from est supprimée), supprimée lorsque la transition / animation se termine.

  4. v-leave-from : état de départ pour la sortie. Ajoutée immédiatement lorsqu'une transition de sortie est déclenchée, supprimée après une frame.

  5. v-leave-active : état actif pour la sortie. Appliquée pendant toute la phase de sortie. Ajoutée immédiatement lorsqu'une transition de sortie est déclenchée, supprimée lorsque la transition/animation se termine. Cette classe peut être utilisée pour définir la durée, le retard et la courbe d'accélération de la transition de sortie.

  6. v-leave-to : état de fin pour la sortie. Ajoutée une frame après le déclenchement d'une transition de sortie (en même temps v-leave-from est supprimée), supprimée lorsque la transition/animation se termine.

v-enter-active et v-leave-active nous donnent la possibilité de spécifier différentes courbes d'accélération pour les transitions entrée/sortie, dont nous verrons un exemple dans les sections suivantes.

Transitions nommées

Une transition peut être nommée via la prop name :

template
<Transition name="fade">
  ...
</Transition>

Pour une transition nommée, ses classes de transition seront préfixées par son nom au lieu de v. Par exemple, la classe appliquée pour la transition ci-dessus sera fade-enter-active au lieu de v-enter-active. Le CSS pour la transition de fondu devrait ressembler à ceci :

css
.fade-enter-active,
.fade-leave-active {
  transition: opacity 0.5s ease;
}

.fade-enter-from,
.fade-leave-to {
  opacity: 0;
}

Transitions CSS

<Transition> est le plus souvent utilisé en combinaison avec les transitions CSS natives, comme on le voit dans l'exemple basique ci-dessus. La propriété CSS transition est un raccourci qui nous permet de spécifier plusieurs aspects d'une transition, y compris les propriétés qui doivent être animées, la durée de la transition et les courbes d'accélération.

Voici un exemple plus avancé qui effectue la transition de plusieurs propriétés, avec différentes durées et courbes d'accélération pour l'entrée et la sortie :

template
<Transition name="slide-fade">
  <p v-if="show">hello</p>
</Transition>
css
/*
  Les animations d'entrée et de sortie peuvent utiliser différentes
  durées et fonctions de temporisation.
*/
.slide-fade-enter-active {
  transition: all 0.3s ease-out;
}

.slide-fade-leave-active {
  transition: all 0.8s cubic-bezier(1, 0.5, 0.8, 1);
}

.slide-fade-enter-from,
.slide-fade-leave-to {
  transform: translateX(20px);
  opacity: 0;
}

hello

Animations CSS

Les animations CSS natives sont appliquées de la même manière que les transitions CSS, à la différence que *-enter-from n'est pas supprimée immédiatement après l'insertion de l'élément, mais lors d'un événement animationend.

Pour la plupart des animations CSS, nous pouvons simplement les déclarer sous les classes *-enter-active et *-leave-active. Voici un exemple :

template
<Transition name="bounce">
  <p v-if="show" style="text-align: center;">
    Hello here is some bouncy text!
  </p>
</Transition>
css
.bounce-enter-active {
  animation: bounce-in 0.5s;
}
.bounce-leave-active {
  animation: bounce-in 0.5s reverse;
}
@keyframes bounce-in {
  0% {
    transform: scale(0);
  }
  50% {
    transform: scale(1.25);
  }
  100% {
    transform: scale(1);
  }
}

Bonjour voici un texte plein d'entrain !

Classes de transition personnalisées

Vous pouvez également spécifier des classes de transition personnalisées en transmettant les props suivantes à <Transition> :

  • enter-from-class
  • enter-active-class
  • enter-to-class
  • leave-from-class
  • leave-active-class
  • leave-to-class

Celles-ci remplaceront les noms de classes conventionnels. Ceci est particulièrement utile lorsque vous souhaitez combiner le système de transition de Vue avec une bibliothèque d'animation CSS existante, telle que Animate.css :

template
<!-- en supposant que Animate.css est inclus sur la page -->
<Transition
  name="custom-classes"
  enter-active-class="animate__animated animate__tada"
  leave-active-class="animate__animated animate__bounceOutRight"
>
  <p v-if="show">hello</p>
</Transition>

Utiliser les transitions et les animations ensemble

Vue doit attacher des écouteurs d'événements afin de savoir quand une transition est terminée. Cela peut être transitionend ou animationend, selon le type de règles CSS appliquées. Si vous n'utilisez que l'un ou l'autre, Vue peut automatiquement détecter le bon type.

Cependant, dans certains cas, vous voudrez peut-être avoir les deux sur le même élément, par exemple avoir une animation CSS déclenchée par Vue, ainsi qu'un effet de transition CSS au survol. Dans ces cas, vous devrez déclarer explicitement le type dont vous voulez que Vue se soucie en passant la prop type, avec une valeur de animation ou transition :

template
<Transition type="animation">...</Transition>

Transitions imbriquées et durées de transition explicites

Bien que les classes de transition ne soient appliquées qu'à l'élément enfant direct dans <Transition>, nous pouvons effectuer la transition d'éléments imbriqués à l'aide de sélecteurs CSS imbriqués :

template
<Transition name="nested">
  <div v-if="show" class="outer">
    <div class="inner">
      Hello
    </div>
  </div>
</Transition>
css
/* règles qui ciblent les éléments imbriqués */
.nested-enter-active .inner,
.nested-leave-active .inner {
  transition: all 0.3s ease-in-out;
}

.nested-enter-from .inner,
.nested-leave-to .inner {
  transform: translateX(30px);
  opacity: 0;
}

/* ... autre CSS nécessaire omis */

Nous pouvons même ajouter un délai de transition à l'élément imbriqué lors de l'entrée, ce qui crée une séquence d'animation d'entrée décalée :

css
/* retarde l'entrée de l'élément imbriqué pour un effet décalé */
.nested-enter-active .inner {
  transition-delay: 0.25s;
}

Cependant, cela crée un petit problème. Par défaut, le composant <Transition> tente de déterminer automatiquement quand la transition est terminée en écoutant le premier événement transitionend ou animationend sur l'élément de transition racine. Avec une transition imbriquée, le comportement souhaité doit attendre que les transitions de tous les éléments internes soient terminées.

Dans de tels cas, vous pouvez spécifier une durée de transition explicite (en millisecondes) à l'aide de la prop duration sur le composant <Transition>. La durée totale doit correspondre au délai plus la durée de transition de l'élément interne :

template
<Transition :duration="550">...</Transition>
Hello

Essayer en ligne

Si nécessaire, vous pouvez également spécifier des valeurs distinctes pour les durées d'entrée et de sortie à l'aide d'un objet :

template
<Transition :duration="{ enter: 500, leave: 800 }">...</Transition>

Considérations relatives aux performances

Vous remarquerez peut-être que les animations présentées ci-dessus utilisent principalement des propriétés telles que "transform" et "opacity". Ces propriétés sont efficaces pour animer car :

  1. Elles n'affectent pas la mise en page du document pendant l'animation, elles ne déclenchent donc pas de calculs de mise en page CSS coûteux sur chaque image d'animation.

  2. La plupart des navigateurs modernes peuvent tirer parti de l'accélération matérielle GPU lors de l'animation de transform.

En comparaison, des propriétés telles que height ou margin déclencheront la mise en page CSS, elles sont donc beaucoup plus coûteuses à animer et doivent être utilisées avec prudence. Nous pouvons consulter des ressources comme CSS-Triggers pour voir quelles propriétés déclencheront la mise en page si nous les animons.

Hooks JavaScript

Vous pouvez vous connecter au processus de transition avec JavaScript en écoutant les événements sur le composant <Transition> :

html
<Transition
  @before-enter="onBeforeEnter"
  @enter="onEnter"
  @after-enter="onAfterEnter"
  @enter-cancelled="onEnterCancelled"
  @before-leave="onBeforeLeave"
  @leave="onLeave"
  @after-leave="onAfterLeave"
  @leave-cancelled="onLeaveCancelled"
>
  <!-- ... -->
</Transition>
js
// appelée avant que l'élément ne soit inséré dans le DOM.
// utilisez ceci pour définir l'état "enter-from" de l'élément.
function onBeforeEnter(el) {}

// appelée une frame après l'insertion de l'élément.
// utilisez ceci pour démarrer l'animation d'entrée.
function onEnter(el, done) {
  // appelle la fonction de rappel done pour indiquer la fin de la transition
  // facultative si utilisée en combinaison avec CSS
  done()
}

// appelée lorsque la transition enter est terminée.
function onAfterEnter(el) {}

// appelée lorsque la transition enter est annulée avant la fin.
function onEnterCancelled(el) {}

// appelée avant le hook de sortie.
// la plupart du temps, vous devez simplement utiliser le hook de sortie.
function onBeforeLeave(el) {}

// appelée lorsque la transition de sortie démarre.
// utilisez ceci pour démarrer l'animation de sortie.
function onLeave(el, done) {
  // appelle la fonction de rappel done pour indiquer la fin de la transition
  // facultative si utilisée en combinaison avec CSS
  done()
}

// appelée lorsque la transition de sortie est terminée et que
// l'élément a été supprimé du DOM.
function onAfterLeave(el) {}

// uniquement disponible avec les transitions v-show
function onLeaveCancelled(el) {}
js
export default {
  // ...
  methods: {
    // appelée avant que l'élément ne soit inséré dans le DOM.
    // utilisez ceci pour définir l'état "enter-from" de l'élément
    onBeforeEnter(el) {},

    // appelée une frame après l'insertion de l'élément.
    // utilisez ceci pour démarrer l'animation d'entrée.
    onEnter(el, done) {
      // appelle la fonction de rappel done pour indiquer la fin de la transition.
      // facultative si utilisée en combinaison avec CSS
      done()
    },

    // appelée lorsque la transition enter est terminée.
    onAfterEnter(el) {},

    // appelée lorsque la transition d'entrée est annulée avant d'être achevée.
    onEnterCancelled(el) {},

    // appelée avant le hook de sortie.
    // la plupart du temps, vous devez simplement utiliser le hook de sortie.
    onBeforeLeave(el) {},

    // appelée lorsque la transition de sortie démarre.
    // utilisez ceci pour démarrer l'animation de sortie.
    onLeave(el, done) {
      // appelle la fonction de rappel done pour indiquer la fin de la transition
      // facultative si utilisée en combinaison avec CSS
      done()
    },

    // appelée lorsque la transition de sortie est terminée et que
    // l'élément a été supprimé du DOM.
    onAfterLeave(el) {},

    // uniquement disponible avec les transitions v-show
    onLeaveCancelled(el) {}
  }
}

Ces hooks peuvent être utilisés en combinaison avec des transitions / animations CSS ou seuls.

Lors de l'utilisation de transitions JavaScript uniquement, il est généralement judicieux d'ajouter la prop :css="false". Cela indique explicitement à Vue d'ignorer la détection automatique des transitions CSS. En plus d'être légèrement plus performant, cela empêche également les règles CSS d'interférer accidentellement avec la transition :

template
<Transition
  ...
  :css="false"
>
  ...
</Transition>

Avec :css="false", nous sommes également entièrement responsables du contrôle de la fin de la transition. Dans ce cas, les rappels done sont requis pour les hooks @enter et @leave. Sinon, les hooks seront appelés de manière synchrone et la transition se terminera immédiatement.

Voici une démo utilisant la bibliothèque GSAP pour réaliser les animations. Vous pouvez bien sûr utiliser toute autre bibliothèque d'animation de votre choix, par exemple Anime.js ou Motion One :

Réutiliser les transitions

Les transitions peuvent être réutilisées via le système de composants de Vue. Pour créer une transition réutilisable, nous pouvons créer un composant qui encapsule le composant <Transition> et transmet le contenu de l'emplacement :

vue
<!-- MyTransition.vue -->
<script>
// Logique du hook JavaScript...
</script>

<template>
  <!-- envelopper le composant de transition natif -->
  <Transition
    name="my-transition"
    @enter="onEnter"
    @leave="onLeave">
    <slot></slot> <!-- passer le contenu du slot -->
  </Transition>
</template>

<style>
/*
  CSS nécessaire...
  Remarque : évitez d'utiliser <style scoped> ici car il
  ne s'applique pas au contenu des slots.
*/
</style>

Désormais, MyTransition peut être importé et utilisé comme la version native :

template
<MyTransition>
  <div v-if="show">Hello</div>
</MyTransition>

Transition à l'apparition

Si vous souhaitez également appliquer une transition sur le rendu initial d'un nœud, vous pouvez ajouter la prop appear :

template
<Transition appear>
  ...
</Transition>

Transition entre élements

En plus de basculer un élément avec v-if /v-show, nous pouvons également faire la transition entre deux éléments en utilisant v-if /v-else /v-else-if, tant que nous nous assurons qu'un seul élément est affiché à tout moment :

template
<Transition>
  <button v-if="docState === 'saved'">Edit</button>
  <button v-else-if="docState === 'edited'">Save</button>
  <button v-else-if="docState === 'editing'">Cancel</button>
</Transition>
Cliquez pour parcourir les différents états :

Essayer en ligne

Modes des transitions

Dans l'exemple précédent, les éléments d'entrée et de sortie sont animés en même temps, et nous avons dû les appliquer position: absolute pour éviter le problème de mise en page lorsque les deux éléments sont présents dans le DOM.

Cependant, dans certains cas, ce n'est pas une option, ou ce n'est tout simplement pas le comportement souhaité. Nous pouvons vouloir que l'élément sortant soit animé en premier, et que l'élément entrant ne soit inséré qu'après que l'animation de départ soit terminée. Orchestrer manuellement de telles animations serait très compliqué - heureusement, nous pouvons activer ce comportement en passant à <Transition> la prop mode :

template
<Transition mode="out-in">
  ...
</Transition>

Voici la démo précédente avec mode="out-in" :

Cliquez pour parcourir les différents états :

<Transition> prend également en charge mode="in-out", bien qu'il soit beaucoup moins fréquemment utilisé.

Transition entre composants

<Transition> peut également être utilisé autour des composants dynamiques :

template
<Transition name="fade" mode="out-in">
  <component :is="activeComponent"></component>
</Transition>
Component A

Transitions dynamiques

Les props de <Transition> comme name peuvent aussi être dynamiques ! Il nous permet d'appliquer dynamiquement différentes transitions en fonction du changement d'état :

template
<Transition :name="transitionName">
  <!-- ... -->
</Transition>

Cela peut être utile lorsque vous avez défini des transitions/animations CSS à l'aide des conventions de classes de transition de Vue et que vous souhaitez basculer entre elles.

Vous pouvez également appliquer un comportement différent avec les hooks de transition JavaScript en fonction de l'état actuel de votre composant. Enfin, le moyen ultime de créer des transitions dynamiques consiste à utiliser des composants de transition réutilisables qui acceptent des props pour modifier la nature de la ou des transitions à utiliser. Cela peut sembler ringard, mais la seule limite est vraiment votre imagination.

Transitions avec l'attribut key

Parfois, vous devez forcer le nouveau rendu d'un élément DOM pour qu'une transition se produise.

Prenons par exemple ce composant de compteur :

vue
<script setup>
import { ref } from 'vue';
const count = ref(0);

setInterval(() => count.value++, 1000);
</script>

<template>
  <Transition>
    <span :key="count">{{ count }}</span>
  </Transition>
</template>
vue
<script>
export default {
  data() {
    return {
      count: 1,
      interval: null
    }
  },
  mounted() {
    this.interval = setInterval(() => {
      this.count++;
    }, 1000)
  },
  beforeDestroy() {
    clearInterval(this.interval)
  }
}
</script>
<template>
  <Transition>
    <span :key="count">{{ count }}</span>
  </Transition>
</template>

Si nous avions exclu l'attribut key, seul le nœud de texte serait mis à jour et donc aucune transition ne serait produit. Cependant, avec l'attribut key, Vue sait créer un nouvel élément span chaque fois que count change et donc le composant Transition a 2 éléments différents entre lesquels faire la transition.


Référence

Transitiona chargé