Skip to content

Transition

Vue nabízí dvě vestavěné komponenty, které pomáhají pracovat s přechody a animacemi v reakci na změny stavu:

  • <Transition> pro aplikaci animací, když element nebo komponenta vstupuje či vystupuje z DOM. Tomu se věnuje tato stránka.

  • <TransitionGroup> pro aplikaci animací při vložení elementu nebo komponenty do seznamu v-for, při odebrání elementu nebo komponenty z tohoto seznamu nebo při jejich přesunu v rámci seznamu. Tím se zabývá další kapitola.

Kromě těchto dvou komponent můžeme ve Vue aplikovat animace také pomocí dalších technik, jako je přepínání CSS tříd nebo stavově řízené animace pomocí bindingu stylů. Tyto další techniky jsou popsány v kapitole Techniky animace.

Komponenta <Transition>

<Transition> je vestavěná komponenta, což znamená, že je k dispozici v jakékoli šabloně komponenty, aniž by bylo nutné ji registrovat. Lze ji použít k aplikaci vstupních a výstupních animací na elementy nebo komponenty, které jsou jí předány prostřednictvím výchozího slotu. Vstup nebo výstup může být spuštěn jednou z následujících akcí:

  • Podmíněné vykreslování přes v-if
  • Podmíněné zobrazení přes v-show
  • Dynamické přepínání komponent přes speciální element <component>
  • Změna speciálního atributu key

Zde je příklad nejzákladnějšího použití:

template
<button @click="show = !show">Změnit zobrazení</button>
<Transition>
  <p v-if="show">Ahoj</p>
</Transition>
css
/* Co tyto třídy znamenají, vysvětlíme vzápětí! */
.v-enter-active,
.v-leave-active {
  transition: opacity 0.5s ease;
}

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

Ahoj

TIP

<Transition> podporuje jako obsah svého slotu pouze jediný prvek nebo komponentu. Pokud je obsahem komponenta, musí mít také pouze jeden jediný root element.

Když je element v komponentě <Transition> vložen nebo odebrán, stane se toto:

  1. Vue automaticky zjistí, zda má cílový prvek aplikovány CSS přechody nebo animace. Pokud ano, budou v odpovídajících časech přidány / odebrány některé CSS třídy přechodu.

  2. Pokud existují listenery pro JavaScript události, budou jejich metody v odpovídajících časech provolány.

  3. Pokud nejsou detekovány žádné CSS přechody / animace a nejsou nastaveny žádné JavaScriptové události, operace DOM pro vložení a/nebo odebrání elementů budou provedeny v následujícím animačním snímku prohlížeče.

Přechody založené na CSS

Třídy přechodu

Existuje šest tříd aplikovaných pro přechody vstupu / výstupu.

Diagram přechodu

  1. v-enter-from: Počáteční stav pro vstup. Je přidána před vložením prvku, odebrána jeden snímek po vložení prvku.

  2. v-enter-active: Aktivní stav pro vstup. Používá se během celé fáze vstupu. Je přidána před vložením prvku a odstraněna po dokončení přechodu/animace. Třída může být použita k definici trvání (duration), zpoždění (delay) a křivky pro zjemnění (easing) pro vstupní přechod.

  3. v-enter-to: Koncový stav pro vstup. Je přidána jeden snímek po vložení prvku (ve stejný okamžik, kdy je odstraněno v-enter-from) a odstraněna po dokončení přechodu/animace.

  4. v-leave-from: Počáteční stav pro odchod. Je přidána okamžitě po spuštění odchodového přechodu a odstraněna po jednom snímání.

  5. v-leave-active: Aktivní stav pro odchod. Používá se během celé fáze odchodu. Je přidána okamžitě po spuštění odchodového přechodu a odstraněna po dokončení přechodu/animace. Tato třída může být použita k definování trvání, zpoždění a křivky pro odchodový přechod.

  6. v-leave-to: Koncový stav pro odchod. Je přidána jeden snímek po spuštění odchodového přechodu (ve stejný okamžik, kdy je odstraněno v-leave-from) a odstraněna po dokončení přechodu/animace.

v-enter-active a v-leave-active nám pro vstupní / odchodové přechody umožňují specifikovat různé křivky uvolnění, jak uvidíme na příkladu v následujících sekcích.

Pojmenované přechody

Přechod může být pojmenován pomocí vlastnosti name:

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

Pro pojmenovaný přechod budou jeho přechodové třídy uvozeny místo předpony v jménem přechodu. Například třída aplikovaná pro výše uvedený přechod bude fade-enter-active místo v-enter-active. CSS pro přechod fade by měl vypadat takto:

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

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

CSS přechody

<Transition> se nejčastěji používá v kombinaci s nativními CSS přechody, jako v jednoduchém příkladu výše. CSS vlastnost transition je zkratka, která nám umožňuje specifikovat více různých aspektů přechodu, včetně vlastností, které by měly být animovány, trvání přechodu a easing funkce.

Zde je více pokročilý příklad, který přechází mezi více vlastnostmi s různou dobou trvání a křivkami pro vstup a výstup:

template
<Transition name="slide-fade">
  <p v-if="show">Ahoj</p>
</Transition>
css
/*
  Animace pro vstup a výstup mohou mít různou
  dobu trvání a časování.
*/
.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;
}

Ahoj

CSS Animace

Nativní CSS animace se používají stejným způsobem jako CSS přechody, s tím rozdílem, že třída *-enter-from není odstraněna okamžitě po vložení elementu, ale až po události animationend.

Většinu CSS animací stačí deklarovat pod třídami *-enter-active a *-leave-active. Zde je příklad:

template
<Transition name="bounce">
  <p v-if="show" style="text-align: center;">
    Ahoj, já jsem poskakující 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);
  }
}

Ahoj, já jsem poskakující text!

Vlastní třídy přechodu

Můžete specifikovat i své vlastní třídy přechodu pomocí předání následujících vlastností (props) do komponenty <Transition>:

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

Tyto třídy budou použity místo tříd s výchozími názvy. Obzvlášť užitečné to je, pokud chcete kombinovat přechodový systém Vue s existující knihovnou CSS animací, jako je Animate.css:

template
<!-- předpokládá, že je Animate.css zahrnuto na stránce -->
<Transition
  name="custom-classes"
  enter-active-class="animate__animated animate__tada"
  leave-active-class="animate__animated animate__bounceOutRight"
>
  <p v-if="show">Ahoj</p>
</Transition>

Použití přechodů a animací společně

Vue potřebuje připojit event listenery, aby vědělo, kdy přechod skončil. V závislosti na typu použitých CSS pravidel to může být buď transitionend nebo animationend. Pokud používáte pouze jeden z nich, Vue automaticky detekuje správný typ.

Nicméně někdy můžete chtít mít na stejném elementu oba, například mít CSS animaci spouštěnou Vue spolu s efektem CSS přechodu při najetí myší. V těchto případech musíte explicitně deklarovat typ, o který se Vue stará, předáním vlastnosti type s hodnotou buď animation nebo transition:

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

Vnořené přechody a explicitní délky přechodů

Přestože jsou přechodové třídy použity pouze na přímém potomkovi uvnitř <Transition>, můžeme přecházet na vnořené elementy pomocí vnořených CSS selektorů:

template
<Transition name="nested">
  <div v-if="show" class="outer">
    <div class="inner">
      Ahoj
    </div>
  </div>
</Transition>
css
/* pravidla aplikovaná na vnořené prvky */
.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;
}

/* ... další potřebné CSS vynecháno */

Můžeme dokonce přidat zpoždění přechodu na vnořený prvek při vstupu, což vytvoří posunutou (staggered) animaci vstupu:

css
/* zpoždění vstupu vnořeného prvku pro "staggered" efekt */
.nested-enter-active .inner {
  transition-delay: 0.25s;
}

To však vytváří malý problém. Výchozím chováním komponenty <Transition> je automaticky zjistit, kdy přechod skončil, posloucháním první události transitionend nebo animationend na root elementu přechodu. S vnořeným přechodem by však požadovaným chováním mělo být čekání, dokud neskončí i přechody všech vnitřních prvků.

V takových případech můžete specifikovat explicitní dobu přechodu (v milisekundách) pomocí vlastnosti duration na komponentě <Transition>. Celková doba by měla odpovídat zpoždění plus době přechodu vnitřního prvku:

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

Vyzkoušejte si to

Pokud je to nutné, můžete specifikovat i samostatné hodnoty pro dobu trvání při vstupu a odchodu pomocí objektu:

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

Úvaha o výkonu

Můžete si všimnout, že animace zobrazené výše využívají převážně vlastnosti jako transform a opacity. Tyto vlastnosti jsou efektivní pro animaci, protože:

  1. Neovlivňují layout dokumentu během animace, takže nevyvolávají nákladné výpočty CSS layoutu při každém snímku animace.

  2. Většina moderních prohlížečů může využít hardwarovou akceleraci GPU během transform animace.

Naopak vlastnosti jako height nebo margin vyvolají změnu CSS layoutu, takže jsou na animaci mnohem dražší a měly by se používat opatrně. Pro zjištění, které všechny CSS vlastnosti změnu layoutu při animaci vyvolají, můžeme použít zdroje jako CSS Triggers.

JavaScript události

Pomocí JavaScriptu se můžete k procesu přechodu připojit pomocí naslouchání událostem na komponentě <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
// voláno před vložením elementu do DOM
// použijte k nastavení počátečního ("enter-from") stavu
function onBeforeEnter(el) {}

// voláno jeden snímek po vložení elementu
// použijte k zahájení animace přechodu
function onEnter(el, done) {
  // volání funkce `done` signalizuje konec přechodu
  // volitelné, pokud se používá ve spojení s CSS
  done()
}

// voláno po dokončení vstupního přechodu
function onAfterEnter(el) {}

// voláno, když je vstupní přechod zrušen před dokončením
function onEnterCancelled(el) {}

// voláno před odchodem
// většinou byste měli použít pouze metodu leave
function onBeforeLeave(el) {}

// voláno při začátku odchodového přechodu
// použijte k zahájení animace odchodu
function onLeave(el, done) {
  // volání funkce `done` signalizuje konec přechodu
  // volitelné, pokud se používá ve spojení s CSS
  done()
}

// voláno po dokončení odchodového přechodu 
// a odebrání elementu z DOM
function onAfterLeave(el) {}

// dostupné pouze pro přechody v rámci v-show
function onLeaveCancelled(el) {}
js
export default {
  // ...
  methods: {
    // voláno před vložením elementu do DOM
    // použijte k nastavení počátečního ("enter-from") stavu
    onBeforeEnter(el) {},

    // voláno jeden snímek po vložení elementu
    // použijte k zahájení animace přechodu
    onEnter(el, done) {
      // volání funkce `done` signalizuje konec přechodu
      // volitelné, pokud se používá ve spojení s CSS
      done()
    },

    // voláno po dokončení vstupního přechodu
    onAfterEnter(el) {},

    // voláno, pokud je vstupní přechod zrušen (cancelled) před dokončením
    onEnterCancelled(el) {},

    // voláno před odchodem
    // většinou byste měli použít pouze metodu leave
    onBeforeLeave(el) {},

    // voláno při začátku odchodového přechodu
    // použijte k zahájení animace odchodu
    onLeave(el, done) {
      // volání funkce `done` signalizuje konec přechodu
      // volitelné, pokud se používá ve spojení s CSS
      done()
    },

    // voláno po dokončení odchodového přechodu 
    // a odebrání elementu z DOM
    onAfterLeave(el) {},

    // dostupné pouze pro přechody v rámci v-show
    onLeaveCancelled(el) {}
  }
}

Tyto metody (hooks) lze použít ve spojení s CSS přechody / animacemi nebo samostatně.

Při použití čistě JavaScriptových přechodů je obvykle vhodné přidat vlastnost (prop) :css="false". Tímto Vue explicitně říkáme, aby se přeskočilo automatické detekování CSS přechodů. Kromě toho, že je to trochu výkonnější, to dále zabrání tomu, aby se CSS pravidla do přechodu náhodou vmísila:

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

S :css="false" jsme také plně zodpovědní za řízení konce přechodu. V tomto případě jsou pro metody (hooks) @enter a @leave vyžadovány callbacky done. Jinak budou metody volány synchronně a přechod okamžitě skončí.

Zde je ukázka použití knihovny GSAP k provedení animací. Samozřejmě můžete použít libovolnou jinou animační knihovnu, například Anime.js nebo Motion One:

Znovupoužitelné přechody

Přechody mohou být znovupoužity pomocí systému Vue komponent. Pro vytvoření znovupoužiteného přechodu můžeme vytvořit komponentu, která obaluje komponentu <Transition> a předává obsah slotu:

vue
<!-- MyTransition.vue -->
<script>
// Logika JavaScriptových metod...
</script>

<template>
  <!-- obalit vestavěnou komponentu `Transition` -->
  <Transition
    name="my-transition"
    @enter="onEnter"
    @leave="onLeave">
    <slot></slot> <!-- předat obsah slotu -->
  </Transition>
</template>

<style>
/*
  Nutné CSS...
  Poznámka: zde se vyhněte se použití `<style scoped>`,
  protože se nevztahuje na obsah slotu.
*/
</style>

Nyní může být MyTransition importována a používána stejně jako vestavěná verze:

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

Přechod ihned při zobrazení

Pokud chcete přechod aplikovat také ihned při počátečním vykreslení prvku, můžete přidat vlastnost appear:

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

Přechod mezi elementy

Kromě přepínání pomocí v-if / v-show můžeme mezi dvěma elementy také přecházet s využitím v-if / v-else / v-else-if, pokud se ujistíme, že je v každém okamžiku zobrazen pouze jeden prvek:

template
<Transition>
  <button v-if="docState === 'saved'">Editovat</button>
  <button v-else-if="docState === 'edited'">Uložit</button>
  <button v-else-if="docState === 'editing'">Zrušit</button>
</Transition>
Klikněte pro změnu stavu:

Vyzkoušejte si to

Režimy přechodu

V předchozím příkladu byly vstupující a odcházející prvky animovány současně a museli jsme je nastavit na position: absolute, abychom se vyhnuli problému s layoutem, když jsou v DOM přítomny oba prvky.

To však v některých případech není možné nebo to jednoduše není požadované chování. Můžeme chtít, aby odcházející prvek byl nejprve animován pryč a vstupující prvek byl vložen až po dokončení animace odcházejícího prvku. Manuální orchestrace takových animací by byla velmi složitá - naštěstí můžeme toto chování povolit předáním vlastnosti (prop) mode do <Transition>:

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

Zde je předchozí demo s mode="out-in":

Klikněte pro změnu stavu:

<Transition> také podporuje mode="in-out", i když se používá mnohem méně často.

Přechod mezi komponentami

<Transition> může být také použita nad dynamickou komponentou:

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

Dynamické přechody

Vlastnosti <Transition> jako name mohou být také dynamické! To nám umožňuje používat různé přechody operativně na základě změny stavu:

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

Může to být užitečné, pokud jste definovali přechody / animace pomocí konvenčních tříd Vue přechodů a chcete mezi nimi přepínat.

Můžete také aplikovat různé chování v JavaScriptových metodách pro přechod na základě aktuálního stavu vaší komponenty. V neposlední řadě, nejlepší způsob vytváření dynamických přechodů je pomocí znovupoužitelných přechodů, komponent, které přijímají vlastnosti (props) pro změnu povahy použitých přechodů. Možná to zní trochu kýčovitě, ale opravdu jediným omezením je vaše představivost.

Přechody s atributem key

Někdy si potřebujete vynutit nové vykreslení DOM elementu, aby se přechod spustil.

Například si vezměte tuto komponentu počítadla:

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>

Pokud bychom atribut key vynechali, aktualizoval by se pouze textový uzel, a proto by nedošlo k žádnému přechodu. Ovšem s nadefinovaným unikátném key Vue umí vytvořit nový element span, kdykoliv se změní hodnota count, a tak má komponenta Transition dva různé elementy, mezi kterými lze přechod provést.


Související

Transition has loaded