Skip to content

Transição

A Vue oferece dois componentes embutidos que podem ajudar a trabalhar com as transições e animações em resposta as mudanças de estado:

  • <Transition> para aplicação de animações quando um elemento ou componente está entrando ou saindo do DOM. Isto é abordado nesta página.

  • <TransitionGroup> para aplicação de animações quando um elemento ou componente é inserido, removido, movido de dentro de uma lista de v-for. Isto é abordado no próximo capítulo.

Além destes dois componentes, podemos também aplicar animações na Vue usando outras técnicas tais como alternância de classes de CSS ou animações orientadas a estado através vinculações de estilo. Estas técnicas adicionais são abordadas no capítulo Técnicas de Animação.

O Componente <Transition>

<Transition> é um componente embutido: isto significa que está disponível em qualquer modelo de marcação do componente sem ter que registá-lo. Isto pode ser utilizado para aplicar animações de entrada e saída sobre os elementos ou componentes passados para ele através da sua ranhura padrão. A entrada e saída pode ser acionada por um dos seguintes:

  • Interpretação condicional através de v-if
  • Exibição condicional através de v-show
  • Alternância de componentes dinâmicos através do elemento especial <component>

Isto é um exemplo de uso mais básico:

template
<button @click="show = !show">Toggle</button>
<Transition>
  <p v-if="show">hello</p>
</Transition>
css
/* nós explicaremos o que são estas classes fazem a seguir! */
.v-enter-active,
.v-leave-active {
  transition: opacity 0.5s ease;
}

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

hello

Dica

<Transition> apenas suporta um único elemento ou componente como seu conteúdo de ranhura. Se o conteúdo é um componente, o componente deve também ter apenas um único elemento de raiz.

Quando um elemento em um componente <Transition> é inserido ou removido, isto é o que acontece:

  1. A Vue automaticamente farejará se elemento alvo tiver transições de CSS ou animações aplicadas. Se tiver, um número de classes de transições de CSS serão adicionadas ou removidas no momento apropriado.

  2. Se houverem ouvintes para gatilhos de JavaScript, estes gatilhos serão chamados no momento apropriado.

  3. Se nenhuma transição ou animação de CSS for detetada e nenhum gatilho de JavaScript for fornecido, as operações do DOM para inserção e ou remoção serão executadas sobre o próximo quadro de animação do navegador.

Transições Baseadas em CSS

Classes de Transição

Existem seis classes aplicadas para as transições de entrada e saída.

Diagrama de Transição

  1. v-enter-from: Estado inicial para entrada. Adicionado antes do elemento ser inserido, removido um quadro depois do elemento ser inserido.

  2. v-enter-active: estado ativo para entrada. Aplciado durante a fase de entrada inteira. Adicionado antes do elemento ser inserido, removido quando a transição ou animação terminar. Esta calsse pode ser usada para definir a duração, o atraso e a curva de flexão para a transição de entrada.

  3. v-enter-to: Estado final para entrada. Adicionado um quadro depois do elemento ser inserido (no mesmo momento que v-enter-from for removido), removido quando a transição ou animação terminar.

  4. v-leave-from: Estado inicial para saída. Adicionada imediatamente quando a transição de saída for acionada, removida depois de um quadro.

  5. v-leave-active: Estado ativo para saída. Aplicada durante a fase de saída inteira. Adicionada imediatamente quando um transição de saída é acionada, removida quando a transição ou animação termina. Esta classe pode ser usada para definir a duração, atraso e a curva de flexão para a transição de saída.

  6. v-leave-to: Estado final para saída. Adicionada um quadro depois de uma transição de saída ser acionada (ao mesmo tempo que v-leave-from é removida), removida quando a transição ou animação termina.

v-enter-active e v-leave-active dão-nos a habilidade de especificar curvas de flexão diferentes para as transições de entrada ou saída, as quais veremos exemplos nas seguintes secções.

Transições Nomeadas

Um transição pode ser nomeada através da propriedade name:

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

Para uma transição nomeada, suas classes de transição serão prefixadas com seu nome no lugar de v. Por exemplo, a classe aplicada para a transição de cima será fade-enter-active no lugar de v-enter-active. A CSS para a transição de desaparecimento (fade, em Inglês) deve parecer-se com isto:

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

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

Transições de CSS

<Transition> é comummente usado em conjunto com transições de CSS nativa, conforme visto no exemplo básico de cima. A propriedade de CSS transition é uma forma abreviada que permite-nos especificar vários aspetos de uma transição, incluindo propriedades que devem ser animadas, duração da transição, e curvas de flexão.

Abaixo está mais um exemplo avançado que realiza transição de várias propriedades, com diferentes durações e curvas de flexão para entrada e saída:

template
<Transition name="slide-fade">
  <p v-if="show">hello</p>
</Transition>
css
/*

  Animações de entrada e saída podem usar diferentes
  duranções e funções de tempo.
*/
.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

Animações de CSS

As animações de CSS nativa são aplicadas da mesma maneira que as transições de CSS com a diferença de que *-enter-from não é removida imediatamente depois do elemento ser inserido, mas sobre um evento de animationend.

Para a maior parte das animações de CSS, podemos simplesmente declará-las sob as classes *-enter-active e *-leave-active. Abaixo encontra-se o exemplo disto:

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);
  }
}

Hello here is some bouncy text!

Classes de Transição Personalizadas

Tu podes também especificar classes de transição personalizadas passando as seguintes propriedades para o <Transition>:

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

Estas irão sobrepor-se aos nomes convencionais das classes. Isto é especialmente útil quando queres combinar o sistema de transição da Vue com uma biblioteca de animação de CSS existente, tal como Animate.css:

template
<!-- assumindo que Animate.css está incluída na página -->
<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>

Usando Juntas Transições e Animações

A Vue precisa atribuir ouvintes de evento para saber quando uma transição termina. Isto pode ser tanto transitionend ou animationend, dependendo do tipo de regras de CSS aplicada. Se estás apenas a usar um ou o outro, a Vue pode automaticamente detetar o tipo correto.

No entanto, em alguns casos podes desejar ter ambos no mesmo elemento, por exemplo, ter uma animação de CSS acionada pela Vue, juntamente com um efeito de transição de CSS sobre pairar do ponteiro do rato. Nestes casos, terás que declarar explicitamente o tipo que quiseres que a Vue preocupe-se passando a propriedade type, com o valor de ou animation ou transition:

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

Transições Encaixadas e Durações de Transição Explícita

Apesar das classes de transição serem apenas aplicadas ao elemento filho direto no <Transition>, podemos aplicar a transição aos elementos encaixados usando seletores de CSS encaixado:

template
<Transition name="nested">
  <div v-if="show" class="outer">
    <div class="inner">
      Hello
    </div>
  </div>
</Transition>
css
/* regras que miram os elementos encaixados */
.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;
}

/* ... outras CSS necessárias omitidas */

Nós podemos até mesmo adicionar um atraso de transição para o elemento encaixado na entrada, o que cria uma sequência de animação de entrada escalonada:

css
/* atrasar a entrada do elemento encaixado para o efeito escalonado */
.nested-enter-active .inner {
  transition-delay: 0.25s;
}

No entanto, isto cria um pequeno problema. Por padrão, o componente <Transition> tenta automaticamente saber quando a transição terminou ouvindo o primeiro evento transitionend ou animationend sobre o elemento de transição de raiz. Com uma transição encaixada, o comportamento desejado deve ser esperar até as transições de todos elementos internos estiverem termidas.

Nestes casos podes especificar uma duração de transição explícita (em milissegundos) usando a propriedade duration no componente <transition>. A duração total deve corresponder ao atraso mais a duração da transição do elemento interno:

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

Experimente-o na Zona de Testes

Se necessário, podes também especificar valores separados para as durações de entrada e saída usando um objeto:

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

Considerações de Desempenho

Tu podes reparar que as animações mostradas acima estão na maior parte das vezes usando propriedades como transform e opacity. Estas propriedades são eficiantes ao animar porque:

  1. Elas não afetam a disposição do documento durante a animação, então elas não acionam cálculos de disposição de CSS dispendiosos em cada quadro da animação.

  2. A maior parte dos navegadores podem influenciar a aceleração de hardware da GPU quando estão animando a transform.

Em comparação, propriedades como height ou margin acionarão a disposição de CSS, então são muito mais dispensiosas para animar, e devem ser usadas com cautela. Nós podemos consultas recursos como CSS-Triggers para saber quais propriedades acionarão a disposição se as animarmos.

Gatilhos de JavaScript

Tu podes ligar-te ao processo de transição com a JavaScript ouvindo os eventos sobre o 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
// chamada antes do elemento ser inserido no DOM.
// usa isto para definir o estado "enter-from" do elemento
function onBeforeEnter(el) {}

// chamada um quadro depois do elemento ser inserido.
// usa isto para iniciar a animação de entrada.
function onEnter(el, done) {
  // chama a resposta "done" para indicar o fim da transição
  // opcional se usada em conjunto com a CSS
  done()
}

// chamada quando a transição de entrada termina.
function onAfterEnter(el) {}
function onEnterCancelled(el) {}

// chamada antes do gatilho de saída.
// A maior parte das vezes, deves apenas usar o gatilho de saída
function onBeforeLeave(el) {}

// chamada quando a transição de saída começa.
// usa isto para iniciar a animação de saída.
function onLeave(el, done) {
  // chama a resposta "done" para indicar o fim da transição
  // opcional se usada em conjunto com a CSS
  done()
}

// chamda quando a transição de saída termina e o
// elemento foi removido do DOM.
function onAfterLeave(el) {}

// apenas disponível com as transições de "v-show"
function onLeaveCancelled(el) {}
js
export default {
  // ...
  methods: {
    // chamada antes do elemento ser inserido no DOM.
    // usa isto para definir o estado "enter-from" do elemento
    onBeforeEnter(el) {},

    // chamada um quadro depois do elemento ser inserido.
    // usa isto para iniciar a animação de entrada.
    onEnter(el, done) {
      // chama a resposta "done" para indicar o fim da transição
      // opcional se usada em conjunto com a CSS
      done()
    },

    // chamada quando a transição de entrada termina.
    onAfterEnter(el) {},
    onEnterCancelled(el) {},

    // chamada antes do gatilho de saída.
    // A maior parte das vezes, deves apenas usar o gatilho de saída
    onBeforeLeave(el) {},

    // chamada quando a transição de saída começa.
    // usa isto para iniciar a animação de saída.
    onLeave(el, done) {
      // chama a resposta "done" para indicar o fim da transição
      // opcional se usada em conjunto com a CSS
      done()
    },

    // chamda quando a transição de saída termina e o
    // elemento foi removido do DOM.
    onAfterLeave(el) {},

    // apenas disponível com as transições de "v-show"
    onLeaveCancelled(el) {}
  }
}

Estes gatilhos podem ser usados em conjunto com as transições ou animações de CSS ou por conta própria.

Quando usamos transições em JavaScript apenas, é usualmente uma boa ideia adicionar a propriedade :css="false". Isto diz explicitamente a Vue para ignorar a deteção automática de transição de CSS. Além de ter ligeiramente um desempenho melhor, isto também impedi as regras de CSS de acidentalmente interferirem com a transição:

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

Com :css="false", somos também completamente responsáveis pelo controle de quando a transição termina. Neste caso, as respostas done são obrigatórias para os gatilhos @enter e @leave. De outro modo, os gatilhos serão chamados de maneira síncrona e a transição terminará imediatamente.

Cá está uma demonstração usando a biblioteca GreenSock para realizar as animações. Tu podes, com certeza, usar qualquer outra biblioteca de animação que quiseres, por exemplo Anime.js ou Motion One.

Transições Reutilizáveis

As transições podem ser reutilizadas através do sistema de componente da Vue. Para criar uma transição reutilizável, podemos criar um componente que envolve o componente <Transition> e passar o conteúdo da ranhura:

vue
<!-- MyTransition.vue -->
<script>
// Lógica dos gatilhos de JavaScript...
</script>

<template>
  <!-- envolve o componente Transition embutido -->
  <Transition
    name="my-transition"
    @enter="onEnter"
    @leave="onLeave">
    <slot></slot> <!-- passa o conteúdo da ranhura -->
  </Transition>
</template>

<style>
/*
  CSS necessária...
  Nota: evite usar <style scoped> aqui já que não
  se aplica ao conteúdo da ranhura.
*/
</style>

Agora MyTransition pode ser importada e usada tal como a versão embutida:

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

Transição sobre o Aparecimento

Se quiseres também aplicar uma transição sobre a interpretação inicial de um nó, podes adicionar o atributo appear:

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

Transição Entre Elementos

Além de alternar um elemento com v-if ou v-show, podemos também realizar a transição entre dois elementos usando v-if, v-else ou v-else-if:

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>
Click to cycle through states:

Experimente-o na Zona de Testes

Modos de Transição

No exemplo anterior, as entradas e saídas dos elementos são animadas ao mesmo tempo, e tinhamos que torná-las position: absolute para evitar o problema de disposição quando ambos elementos estiverem presentes no DOM.

No entanto, em alguns casos isto não é uma opção, ou simplesmente não é o comportamento desejado. Nós podemos desejar deixar o elemento ser animado primeiro, e para entrada o elemento ser apenas inserido depois de terminada a animação de saída. Orquestrar tais animações manualmente seria muito complicado - felizmente, podemos ativar este comportamento passando para <Transition> uma propriedade mode:

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

Cá está a demonstração anterior com mode="out-in":

Click to cycle through states:

<Transition> também suporta mode="in-out", embora que seja muito menos usada com frequência.

Transição Entre Componentes

<Transition> também pode ser usada em torno dos componentes dinâmicos:

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

Transições Dinâmicas

As propriedades de <Transition> como name também podem ser dinâmicas! Ela permite-nos aplicar dinamicamente transições diferentes baseadas na mudança de estado:

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

Isto pode ser útil quando tiveres definido transições ou animações de CSS usando as convenções de classe de transição da Vue e quiseres alternar entre elas.

Tu também podes aplicar comportamento diferente nos gatilhos de transição de JavaScript baseado no estado atual do teu componente. Finalmente, a maneira fundamental de criar transições dinâmicas é através de componentes de transição reutilizáveis que aceitam propriedades para mudar a natureza da transição ou transições a ser usadas. Isto pode soar foleiro, mas o único limite é realmente a tua imaginação.


Relacionado ao

Transição has loaded