Skip to content

Transition

Vue ofrece dos componentes incorporados que pueden ayudar a trabajar con transiciones y animaciones en respuesta al cambio de estado:

  • <Transition> para aplicar animaciones cuando un elemento o componente entra y sale del DOM. Esto se cubre en esta página.

  • <TransitionGroup> para aplicar animaciones cuando un elemento o componente es insertado, eliminado o movido dentro de una listav-for. Esto se cubre en el siguiente capítulo.

Ademas de estos dos componentes, también podemos aplicar animaciones en Vue usando otras técnicas como alternar clases CSS o animaciones impulsadas por el estado a través de enlaces de estilo. Estas técnicas adicionales se tratan en el capítulo Técnicas de animación.

El Componente <Transition>

<Transition> es un componente integrado: esto significa que está disponible en la plantilla de cualquier componente sin tener que registrarlo. Se puede utilizar para aplicar animaciones de entrada y salida en elementos o componentes pasados a través de un slot por defecto. La entrada o salida puede ser activada por una de las siguientes opciones:

  • Renderizado condicional mediante v-if.
  • Visualización condicional mediante v-show.
  • Alternancia de componentes dinámicos a través del elemento especial <component>.
  • Cambiando el atributo especial key:

Este es un ejemplo del uso más básico:

template
<button @click="show = !show">Alternar</button>
<Transition>
  <p v-if="show">Hola</p>
</Transition>
css
/* ¡explicaremos lo que hacen estas clases a continuación! */
.v-enter-active,
.v-leave-active {
  transition: opacity 0.5s ease;
}

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

hola

TIP

<Transition> sólo admite un único elemento o componente como contenido del slot. Si el contenido es un componente, el componente también debe tener un único elemento raíz.

Cuando se inserta o elimina un elemento en un componente <Transition>, esto es lo que sucede:

  1. Vue detectará automáticamente si el elemento de destino tiene transiciones o animaciones CSS aplicadas. Si las tiene, se añadirá/eliminará un número de clases de transición CSS en los momentos adecuados.

  2. Si hay escuchadores para hooks de JavaScript, estos hooks serán llamados en los momentos apropiados.

  3. Si no se detectan transiciones / animaciones CSS y no se proporcionan hooks de JavaScript, las operaciones del DOM para la inserción y/o eliminación se ejecutarán en el siguiente frame de animación del navegador.

Transiciones Basadas en CSS

Clases de Transición

Hay seis clases aplicadas para las transiciones de entrada/salida.

Diagrama de Transición

  1. v-enter-from: Estado inicial de la entrada. Se añade antes de que se inserte el elemento, se elimina un frame después de que se inserte el elemento.

  2. v-enter-active: Estado activo para entrar. Se aplica durante toda la fase de entrada. Se añade antes de que se inserte el elemento, se elimina cuando termina la transición/animación. Esta clase se puede utilizar para definir la duración, el retardo y la curva de atenuación de la transición de entrada.

  3. v-enter-to: Estado de finalización de la entrada. Se añade un frame después de la inserción del elemento (al mismo tiempo que se elimina el v-enter-from), se elimina cuando termina la transición/animación.

  4. v-leave-from: Estado inicial para la salida. Se añade inmediatamente cuando se activa una transición de salida, se elimina después de un frame.

  5. v-leave-active: Estado activo para la salida. Se aplica durante toda la fase de salida. Se añade inmediatamente cuando se activa una transición de salida, y se elimina cuando termina la transición/animación. Esta clase puede utilizarse para definir la duración, el retardo y la curva de atenuación de la transición de salida.

  6. v-leave-to: Estado de finalización de la salida. Se añade un frame después de que se active una transición de salida (al mismo tiempo que se elimina v-leave-from), y se elimina cuando termina la transición/animación.

v-enter-active y v-leave-active nos dan la posibilidad de especificar diferentes curvas de atenuación para las transiciones de entrada/salida, de las que veremos un ejemplo en las siguientes secciones.

Transiciones Asignadas

Una transición puede ser nombrada a través de la proposición name:

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

Para una transición asignada, sus clases de transición llevarán el prefijo de su nombre en lugar de v. Por ejemplo, la clase aplicada para la transición anterior será fade-enter-active en lugar de v-enter-active. El CSS para la transición de desvanecimiento debería tener este aspecto:

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

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

Transiciones CSS

La propiedad <Transition> se utiliza más comúnmente en combinación con transiciones CSS nativas, como se ve en el ejemplo básico anterior. La propiedad CSS transition es una abreviatura que nos permite especificar múltiples aspectos de una transición, incluyendo las propiedades que deben ser animadas, la duración de la transición y las curvas de atenuación.

Aquí hay un ejemplo más avanzado que transiciona múltiples propiedades, con diferentes duraciones y curvas de atenuación para entrar y salir:

template
<Transition name="slide-fade">
  <p v-if="show">hola</p>
</Transition>
css
/*
  Las animaciones de entrada y salida pueden utilizar
  diferentes duraciones y funciones de sincronizació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;
}

hola

Animaciones CSS

Las animaciones CSS nativas se aplican de la misma manera que las transiciones CSS, con la diferencia de que *-enter-from no se elimina inmediatamente después de insertar el elemento, sino en un evento animationend.

Para la mayoría de las animaciones CSS, podemos simplemente declararlas bajo las clases *-enter-active y *-leave-active. He aquí un ejemplo:

template
<Transition name="bounce">
  <p v-if="show" style="text-align: center;">
    Hola, ¡aquí hay un texto animado!
  </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);
  }
}

Hola, ¡aquí hay un texto animado!

Clases para Transición Personalizadas

También puedes especificar clases de transición personalizadas pasando las siguientes props a <Transition>:

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

Estas sobreescriben los nombres de clase convencionales. Esto es especialmente útil cuando se quiere combinar el sistema de transición de Vue con una biblioteca de animación CSS existente, como Animate.css:

template
<¡ -- suponiendo que Animate.css está incluido en la página -->
<Transition
  name="custom-classes"
  enter-active-class="animate__animated animate__tada"
  leave-active-class="animate__animated animate__bounceOutRight"
>
  <p v-if="show">hola</p>
</Transition>

Uso Conjunto de Transiciones y Animaciones

Vue necesita adjuntar escuchadores de eventos para saber cuándo ha terminado una transición. Estos pueden ser transitionend o animationend, dependiendo del tipo de reglas CSS aplicadas. Si sólo utilizas una u otra, Vue puede detectar automáticamente el tipo correcto.

Sin embargo, en algunos casos puedes querer tener ambos en el mismo elemento, por ejemplo, tener una animación CSS activada por Vue, junto con un efecto de transición CSS al pasar por encima. En estos casos, tendrás que declarar explícitamente el tipo que quieres que le interese a Vue pasando la proposición type, con un valor de animation o transition:

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

Transiciones Anidadas y Duraciones de Transición Explícitas

Aunque las clases de transición sólo se aplican al elemento hijo directo de <Transition>, podemos realizar transiciones en elementos anidados utilizando selectores CSS anidados:

template
<Transition name="nested">
  <div v-if="show" class="outer">
    <div class="inner">
      Hola
    </div>
  </div>
</Transition>
css
/* reglas que apuntan a elementos anidados */
.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;
}

/* ... se omite otro CSS necesario */

Incluso podemos añadir un retardo de transición al elemento anidado al entrar, lo que crea una secuencia de animación de entrada gradual:

css
/* retrasa la entrada del elemento anidado para un efecto gradual */
.nested-enter-active .inner {
  transition-delay: 0.25s;
}

Sin embargo, esto crea un pequeño problema. Por defecto, el componente <Transition> intenta averiguar automáticamente cuándo ha terminado la transición escuchando el primer evento transitionend o animationend del elemento de transición raíz. Con una transición anidada, el comportamiento deseado debería ser esperar hasta que las transiciones de todos los elementos internos hayan terminado.

En estos casos se puede especificar una duración explícita de la transición (en milisegundos) utilizando la propiedad duration del componente <transition>. La duración total debe coincidir con el retardo más la duración de la transición del elemento interno:

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

Pruébalo en la Zona de Práctica

Si es necesario, también puede especificar valores separados para las duraciones de entrada y salida utilizando un objeto:

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

Consideraciones sobre el Rendimiento

Puedes observar que las animaciones mostradas anteriormente utilizan principalmente propiedades como transform y opacity. Estas propiedades son eficientes para animar porque:

  1. No afectan al diseño del documento durante la animación, por lo que no activan el costoso cálculo del diseño CSS en cada frame de la animación.

  2. La mayoría de los navegadores modernos pueden aprovechar la aceleración por hardware de la GPU al animar el transform.

En comparación, propiedades como height o margin activarán la maquetación CSS, por lo que son mucho más costosas de animar, y deben utilizarse con precaución. Podemos consultar recursos como CSS-Triggers para ver qué propiedades activarán el maquetado si las animamos.

Hooks de JavaScript

Podemos usar hooks en el proceso de transición con JavaScript escuchando los eventos del 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
// llamado antes de que el elemento se inserte en el DOM.
// Utiliza esto para establecer el estado de "entrada" del elemento
function onBeforeEnter(el) {}

// llamado de un frame después de que el elemento está insertado.
// usa esto para iniciar la animación de entrada.
function onEnter(el, done) {
  // llama al callback done para indicar el fin de la transición
  // opcional si se usa en combinación con CSS
  done()
}

// llamado cuando la transición de entrada ha terminado.
function onAfterEnter(el) {}
// llamado cuando la transición de entrada es cancelada antes de completarse.
function onEnterCancelled(el) {}

// llamado antes del hook de salida.
// La mayoría de las veces, sólo debes usar el hook de salida
function onBeforeLeave(el) {}

// llamado cuando comienza la transición de salida.
// usa esto para iniciar la animación de salida.
function onLeave(el, done) {
  // llama al callback done para indicar el fin de la transición
  // opcional si se usa en combinación con CSS
  hecho()
}

// llamado cuando la transición de salida ha terminado y el
// elemento ha sido eliminado del DOM.
function onAfterLeave(el) {}

// sólo disponible con transiciones v-show
function onLeaveCancelled(el) {}
js
export default {
  // ...
  methods: {
    // llamado antes de que el elemento se inserte en el DOM.
    // Utiliza esto para establecer el estado de "entrada" del elemento
    onBeforeEnter(el) {}

    // llamado de un frame después de que el elemento está insertado.
    // usa esto para iniciar la animación de entrada.
    onEnter(el, done) {
      // llama al callback done para indicar el fin de la transición
      // opcional si se usa en combinación con CSS
      done()
    }

    // llamado cuando la transición de entrada ha terminado.
    onAfterEnter(el) {}
    onEnterCancelled(el) {}

    // llamado antes del hook de salida.
    // La mayoría de las veces, sólo debes usar el hook de salida
    onBeforeLeave(el) {}

    // llamado cuando comienza la transición de salida.
    // usa esto para iniciar la animación de salida.
    onLeave(el, done) {
      // llama al callback done para indicar el fin de la transición
      // opcional si se usa en combinación con CSS
      hecho()
    }

    // llamado cuando la transición de salida ha terminado y el
    // elemento ha sido eliminado del DOM.
    onAfterLeave(el) {}

    // sólo disponible con transiciones v-show
    onLeaveCancelled(el) {}
  }
}

Estos hooks pueden utilizarse en combinación con transiciones/animaciones de CSS o por sí solos.

Cuando se utilizan transiciones sólo JavaScript, suele ser una buena idea añadir la propiedad :css="false". Esto le dice explícitamente a Vue que omita la detección automática de transiciones CSS. Además de ser un poco más eficiente, esto también evita que las reglas de CSS interfieran accidentalmente con la transición:

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

Con :css="false", también somos totalmente responsables de controlar cuándo termina la transición. En este caso, los callbacks done son necesarios para los hooks @enter y @leave. De lo contrario, los hooks serán llamados de forma sincrónica y la transición terminará inmediatamente.

Aquí hay una demostración que utiliza la librería GreenSock para realizar las animaciones. Por supuesto, puedes usar cualquier otra librería de animación que quieras, por ejemplo Anime.js o Motion One.

Transiciones Reutilizables

Las transiciones pueden ser reutilizadas a través del sistema de componentes de Vue. Para crear una transición reutilizable, podemos crear un componente que envuelve el componente <Transition> y pasa el contenido del slot:

vue
<!-- MyTransition.vue -->
<script>
// lógica del hook de JavaScript...
</script>

<template>
  <!-- envolver el componente integrado Transition -->
  <Transition
    name="my-transition"
    @enter="onEnter"
    @leave="onLeave">
    <slot></slot> <!-- pasar el contenido del slot -->
  </Transition>
</template>

<style>
/*
  CSS necesario...
  Nota: evita el uso de <style scoped> aquí
  ya que no se aplica al contenido del slot.
*/
</style>

Ahora MyTransition puede importarse y utilizarse igual que la versión integrada:

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

Transición al Aparecer

Si también quieres aplicar una transición en el renderizado inicial de un nodo, puedes añadir la prop appear:

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

Transición Entre Elementos

Además de alternar un elemento con v-if / v-show, también podemos hacer una transición entre dos elementos utilizando v-if / v-else / v-else-if, siempre y cuando nos aseguremos de que sólo se está mostrando un elemento en cada momento:

template
<Transition>
  <button v-if="docState === 'saved'">Editar</button>
  <button v-else-if="docState === 'edited'">Guardar</button>
  <button v-else-if="docState === 'editing'">Cancelar</button>
</Transition>
Haz clic para recorrer los estados:

Pruébalo en la Zona de Práctica

Modos de Transición

En el ejemplo anterior, los elementos que entran y salen se animan al mismo tiempo, y hemos tenido que hacerlos position: absolute para evitar el problema de diseño cuando ambos elementos están presentes en el DOM.

Sin embargo, en algunos casos esto no es una opción, o simplemente no es el comportamiento deseado. Podemos querer que el elemento que sale se anime primero, y que el elemento que entra sólo se inserte después de que la animación de salida haya terminado. Orquestar estas animaciones manualmente sería muy complicado; sin embargo, podemos habilitar este comportamiento pasándole a <Transition> una propiedad mode:

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

Aquí está la demostración anterior con mode="out-in":

Haz clic para recorrer los estados:

<Transition> también soporta mode="in-out", aunque se utiliza con mucha menos frecuencia.

Transición Entre Componentes

<Transition> también puede utilizarse alrededor de componentes dinámicos:

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

Transiciones Dinámicas

¡Las props de <Transition> como name también pueden ser dinámicas! Esto nos permite aplicar transiciones diferentes de forma dinámica en función al cambio de estado:

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

Esto puede ser útil cuando has definido transiciones / animaciones CSS utilizando las convenciones de clase de transition de Vue y quieres cambiar entre ellas.

También puedes aplicar diferentes comportamientos en los hooks transition de JavaScript basados en el estado actual de tu componente. Finalmente, la última forma de crear transiciones dinámicas es a través de componentes de transición reutilizables que aceptan props para cambiar la naturaleza de la(s) transición(es) a utilizar. Puede sonar cursi, pero el único límite realmente es tu imaginación.


Relacionado

Transition has loaded