Passa al contenuto

Transition

Vue offre due componenti integrati che possono aiutare a lavorare con le transizioni e le animazioni in risposta a cambiamenti di stato:

  • <Transition> per applicare animazioni quando un elemento, o un componente, sta entrando o uscendo dal DOM. Questo è l' argomento trattato in questa pagina.

  • <TransitionGroup> per applicare animazioni quando un elemento, o un componente, viene inserito, rimosso o spostato all'interno di un elenco v-for. Questo argomento è trattato nel prossimo capitolo.

Oltre a questi due componenti, possiamo anche applicare animazioni in Vue utilizzando altre tecniche, come l'alternanza di classi CSS (toggling) o le animazioni guidate dallo stato tramite binding degli stili. Queste tecniche aggiuntive sono trattate nel capitolo Tecniche di Animazione.

Il Componente <Transition>

<Transition> è un componente integrato: ciò significa che è disponibile nel template di qualsiasi componente senza doverlo registrare. Può essere utilizzato per applicare animazioni di entrata e uscita su elementi, o componenti, passati attraverso il suo slot predefinito. L'entrata o l'uscita possono essere innescate da uno dei seguenti casi:

  • Rendering condizionale tramite v-if
  • Visualizzazione condizionale tramite v-show
  • Toggling di componenti dinamici tramite l'elemento speciale <component>
  • Modificando l'attributo speciale key

Questo è un esempio dell'utilizzo più basilare:

template
<button @click="show = !show">Toggle</button>
<Transition>
  <p v-if="show">ciao</p>
</Transition>
css
/* spiegheremo cosa fanno queste classi in seguito!*/
.v-enter-active,
.v-leave-active {
  transition: opacity 0.5s ease;
}

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

ciao

TIP

<Transition> supporta solo un singolo elemento, o componente, come contenuto del suo slot. Se il contenuto è un componente, anche il componente deve avere un solo elemento radice.

Quando un elemento in un componente <Transition> viene inserito o rimosso, ecco cosa succede:

  1. Vue rileverà automaticamente se l'elemento target ha transizioni o animazioni CSS applicate. Se si, un numero di classi di transizione CSS saranno aggiunte/rimosse nei momenti appropriati.

  2. Se ci sono listener per gli hook JavaScript, questi hook verranno chiamati nei momenti appropriati.

  3. Se non vengono rilevate transizioni/animazioni CSS e non vengono forniti hook JavaScript, le operazioni DOM per l'inserimento e/o la rimozione verranno eseguite nel prossimo frame di animazione del browser.

Transizioni basate su CSS

Classi di Transizione

Ci sono sei classi applicate per le transizioni di entrata / uscita.

Diagramma Transizioni

  1. v-enter-from: Stato iniziale per l'entrata. Aggiunta prima che l'elemento venga inserito, rimossa un frame dopo che l'elemento è stato inserito.

  2. v-enter-active: Stato attivo per l'entrata. Applicata durante l'intera fase di entrata. Aggiunto prima che l'elemento venga inserito, rimosso quando la transizione/animazione finisce. Questa classe può essere utilizzata per definire la durata, il ritardo e la curva di easing per la transizione di entrata.

  3. v-enter-to: Stato finale per l'entrata. Viene aggiunta un frame dopo che l'elemento è inserito (nello stesso istante in cui v-enter-from viene rimosso), rimossa quando la transizione/animazione finisce.

  4. v-leave-from: Stato iniziale per l'uscita. Aggiunta immediatamente quando viene innescata una transizione di uscita, rimossa dopo un frame.

  5. v-leave-active: Stato attivo per l'uscita. Applicata durante l'intera fase di uscita. Aggiunta immediatamente quando inizia una transizione di uscita, rimossa quando la transizione/animazione finisce. Questa classe può essere utilizzata per definire la durata, il ritardo e la curva di easing per la transizione di uscita.

  6. v-leave-to: Stato finale per l'uscita. Aggiunta un frame dopo che viene innescata una transizione di uscita (nello stesso istante in cui v-leave-from viene rimossa), rimossa quando la transizione/animazione finisce.

v-enter-active e v-leave-active ci danno la possibilità di specificare curve di easing diverse per le transizioni di entrata / uscita, come vedremo in un esempio nelle sezioni seguenti.

Named Transitions

Si può assegnare un nome ad una transizione tramite la prop name:

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

In una transizione con nome (named transition), le classi di transizione verranno prefissate con il nome assegnato al posto di v. Ad esempio, la classe applicata per la transizione sopra sarà fade-enter-active invece di v-enter-active. Il CSS per la transizione fade dovrebbe apparire così:

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

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

Transizioni CSS

<Transition> è comunemente usato in combinazione con le transizioni CSS native, come visto nell'esempio base sopra. La proprietà CSS transition è una scorciatoia che ci permette di specificare molti aspetti di una transizione, incluse le proprietà che dovrebbero essere animate, la durata della transizione e le curve di easing.

Ecco un esempio più avanzato che usa le transizioni per molteplici proprietà, con diverse durate e curve di easing per l'entrata e l'uscita:

template
<Transition name="slide-fade">
  <p v-if="show">ciao</p>
</Transition>
css
/*
  Le animazioni di entrata e uscita possono utilizzare
  durate e funzioni di tempistica diverse.
*/
.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;
}

ciao

Animazioni CSS

Le Animazioni CSS native sono applicate nello stesso modo delle transizioni CSS, con la differenza che *-enter-from non viene rimosso immediatamente dopo che l'elemento è inserito, ma con un evento animationend.

La maggior parte delle animazioni CSS può essere dichiarata semplicemente sotto le classi *-enter-active e *-leave-active. Ecco un esempio:

template
<Transition name="bounce">
  <p v-if="show" style="text-align: center;">
    Ciao, ecco del testo che rimbalza!
  </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);
  }
}

Ciao, ecco del testo che rimbalza!

Classi di Transizione Personalizzate

Si possono specificare anche classi di transizione personalizzate passando le seguenti prop a <Transition>:

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

Queste sovrascriveranno i nomi delle classi convenzionali. Ciò è particolarmente utile quando si desidera combinare il sistema di transizione di Vue con una libreria di animazioni CSS esistente, come Animate.css:

template
<!-- supponendo che Animate.css sia incluso nella pagina -->
<Transition
  name="custom-classes"
  enter-active-class="animate__animated animate__tada"
  leave-active-class="animate__animated animate__bounceOutRight"
>
  <p v-if="show">ciao</p>
</Transition>

Utilizzare Transizioni e Animazioni Insieme

Vue ha bisogno di aggiungere un listener di eventi per sapere quando una transizione è terminata. Può essere o transitionend o animationend, a seconda del tipo di regole CSS applicate. Se stai usando solo uno o l'altro, Vue può rilevare automaticamente il tipo corretto.

Tuttavia, in alcuni casi potresti voler usare entrambi sullo stesso elemento, ad esempio avendo un'animazione CSS innescata da Vue, insieme a un effetto di transizione CSS al passaggio del mouse. In questi casi dovrai dichiarare in modo esplicito quale vuoi che Vue gestisca, passando la prop type con un valore di animation o transition:

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

Transizioni Annidate e Durata Esplicita della Transizione

Sebbene le classi di transizione vengano applicate solo al diretto elemento figlio in <Transition>, possiamo applicare transizioni agli elementi annidati utilizzando i selettori CSS annidati:

template
<Transition name="nested">
  <div v-if="show" class="outer">
    <div class="inner">
      Ciao
    </div>
  </div>
</Transition>
css
/* regole che si riferiscono agli elementi annidati */
.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;
}

/* ... altro CSS necessario omesso */

Possiamo anche aggiungere un ritardo di transizione all'elemento annidato durante l'ingresso, che crea una sequenza di animazione di ingresso sfalsata (staggered):

css
/* ritarda l'ingresso dell'elemento annidato per ottenere un effetto sfalsato - staggered effect */
.nested-enter-active .inner {
  transition-delay: 0.25s;
}

Tuttavia, ciò crea un piccolo problema. Di default, il componente <Transition> cerca di capire automaticamente quando la transizione è finita, ascoltando il primo evento transitionend o animationend sull'elemento di transizione radice. Con una transizione annidata, il comportamento desiderato dovrebbe essere quello di aspettare fino a quando le transizioni di tutti gli elementi interni sono terminate.

In questi casi puoi specificare una durata di transizione esplicita (in millisecondi) utilizzando la prop duration sul componente <transition>. La durata totale dovrebbe corrispondere al ritardo più la durata della transizione dell'elemento interno:

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

Prova nel Playground

Se necessario, puoi anche specificare valori separati per le durate di ingresso e uscita utilizzando un oggetto:

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

Considerazioni sulle Prestazioni

Potresti notare che le animazioni mostrate sopra utilizzano principalmente proprietà come transform e opacity. Queste proprietà sono efficienti da animare perché:

  1. Non influenzano l'impaginazione del documento durante l'animazione, quindi non innescano costosi calcoli del layout CSS per ogni fotogramma dell'animazione.

  2. La maggior parte dei browser moderni può sfruttare l'accelerazione hardware della GPU quando anima transform.

In confronto, proprietà come height o margin innescano calcoli sul layout CSS, quindi sono molto più costose da animare e dovrebbero essere utilizzate con cautela. Possiamo consultare risorse come CSS-Triggers per vedere quali proprietà innescano questi calcoli se animate.

Hook JavaScript

Puoi collegarti al processo di transizione con JavaScript ascoltando gli eventi sul componente <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
// chiamato prima che l'elemento venga inserito nel DOM.
// usa questo per impostare lo stato "enter-from" dell'elemento
function onBeforeEnter(el) {}

// chiamato un frame dopo che l'elemento è stato inserito.
// usa questo per avviare l'animazione di ingresso.
function onEnter(el, done) {
  // chiama la callback done per indicare la fine della transizione
  // facoltativo se utilizzato in combinazione con CSS
  done()
}

// chiamato quando la transizione di ingresso è terminata.
function onAfterEnter(el) {}

// chiamato quando la transizione di ingresso viene annullata prima del completamento.
function onEnterCancelled(el) {}

// chiamato prima dell'hook di uscita.
//  Nella maggior parte delle volte, dovresti semplicemente usare l'hook di uscita
function onBeforeLeave(el) {}

// chiamato quando inizia la transizione di uscita.
// usa questo per avviare l'animazione di uscita.
function onLeave(el, done) {
  // chiama la callback done per indicare la fine della transizione
  // facoltativo se utilizzato in combinazione con CSS
  done()
}

// chiamato quando la transizione di uscita è terminata e l'elemento
// è stato rimosso dal DOM.
function onAfterLeave(el) {}

// disponibile solo con le transizioni v-show
function onLeaveCancelled(el) {}
js
export default {
  // ...
  methods: {
    // chiamato prima che l'elemento venga inserito nel DOM.
    // usa questo per impostare lo stato "enter-from" dell'elemento
    onBeforeEnter(el) {},

    // chiamato un frame dopo che l'elemento è stato inserito.
    // use this to start the animation.
    onEnter(el, done) {
      // chiama la callback done per indicare la fine della transizione
      // facoltativo se utilizzato in combinazione con CSS
      done()
    },

    // chiamato quando la transizione di ingresso è terminata.
    onAfterEnter(el) {},
    onEnterCancelled(el) {},

    // called before the leave hook.
    //  Nella maggior parte delle volte, dovresti semplicemente usare l'hook di uscita.
    onBeforeLeave(el) {},

    // chiamato quando inizia la transizione di uscita.
    // usa questo per avviare l'animazione di uscita.
    onLeave(el, done) {
      // chiama la callback done per indicare la fine della transizione
      // facoltativo se utilizzato in combinazione con CSS
      done()
    },

    // chiamato quando la transizione di uscita è terminata e l'elemento
    // è stato rimosso dal DOM.
    onAfterLeave(el) {},

    // disponibile solo con le transizioni v-show
    onLeaveCancelled(el) {}
  }
}

Questi hook possono essere utilizzati in combinazione con le transizioni/animazioni CSS, o da soli.

Quando si utilizzano solo transizioni JavaScript, di norma è una buona idea aggiungere la prop :css="false". Questo dice esplicitamente a Vue di saltare il rilevamento automatico delle transizioni CSS. Oltre ad essere leggermente più performante, ciò impedisce anche che le regole CSS interferiscano accidentalmente con la transizione:

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

Con :css="false", siamo pienamente responsabili anche del controllo di quando termina la transizione. In questo caso le callback done sono richieste per gli hook @enter e @leave. Altrimenti, gli hook verranno chiamati in modo sincrono e la transizione terminerà immediatamente.

Ecco una demo che utilizza la libreria GreenSock per eseguire le animazioni. Puoi utilizzare, ovviamente, qualsiasi altra libreria di animazione desideri, ad esempio Anime.js o Motion One.

Transizioni Riutilizzabili

Le transizioni possono essere riutilizzate attraverso il sistema dei componenti di Vue. Per creare una transizione riutilizzabile, possiamo creare un componente che racchiude il componente <Transition> e passa il contenuto dello slot:

vue
<!-- MyTransition.vue -->
<script>
// Logica degli hook JavaScript...
</script>

<template>
  <!-- racchiude il componente Transition nativo -->
  <Transition
    name="my-transition"
    @enter="onEnter"
    @leave="onLeave">
    <slot></slot> <!-- passa il contenuto dello slot -->
  </Transition>
</template>

<style>
/*
  CSS necessario...
  Nota: evita di utilizzare <style scoped> qui, poiché
  non si applica al contenuto dello slot.
*/
</style>

Ora MyTransition può essere importato e utilizzato proprio come la versione nativa:

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

Transizione all'Appear

Se vuoi applicare una transizione anche al rendering iniziale di un nodo, puoi aggiungere la prop appear:

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

Transizione tra Elementi

Oltre a fare un toggle per un elemento con v-if / v-show, possiamo fare una transizione anche tra due elementi utilizzando v-if / v-else / v-else-if, purché siamo sicuri che ci sia solo un elemento mostrato in un dato momento:

template
<Transition>
  <button v-if="docState === 'saved'">Modifica</button>
  <button v-else-if="docState === 'edited'">Salva</button>
  <button v-else-if="docState === 'editing'">Cancella</button>
</Transition>
Clicca per ciclare tra gli stati:

Prova nel Playground

Modalità di Transizione

Nell'esempio precedente gli elementi che entrano ed escono vengono animati contemporaneamente, e abbiamo dovuto renderli con position: absolute per evitare il problema con il layout quando entrambi gli elementi sono presenti nel DOM.

In alcuni casi, però, questo non è possibile, o, semplicemente, non è il comportamento desiderato. Potremmo volere che l'elemento in uscita venga animato prima e che l'elemento in entrata venga inserito solo dopo che l'animazione di uscita sia terminata. Coordinare questo tipo di animazioni manualmente sarebbe molto complicato - fortunatamente, possiamo abilitare questo comportamento passando a <Transition> una prop mode:

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

Ecco la demo precedente con mode="out-in":

Clicca per ciclare tra gli stati:

<Transition> supporta anche mode="in-out", anche se è molto meno utilizzata.

Transizione tra Componenti

<Transition> può essere utilizzato anche con i componenti dinamici, racchiudendoli:

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

Transizioni Dinamiche

Le prop di <Transition> come name possono essere anche dinamiche! Questo ci permette di applicare dinamicamente diverse transizioni, in base al cambiamento dello stato:

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

Questo può essere utile quando hai definito transizioni / animazioni CSS utilizzando le convenzioni di classe di transizione di Vue e vuoi passare da una all'altra.

Puoi applicare anche un comportamento diverso negli hook di transizione in JavaScript, in base allo stato attuale del tuo componente. Infine, il modo definitivo per creare transizioni dinamiche è attraverso i componenti di transizione riutilizzabili, che accettano prop per cambiare la natura delle transizioni da utilizzare. Potrebbe sembrare banale, ma l'unico limite è davvero la tua immaginazione.


Correlati

Transition has loaded