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 está entrando y saliendo 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 próximo capítulo.
Aparte de estos dos componentes, también podemos aplicar animaciones en Vue utilizando otras técnicas como la alternancia de clases CSS o animaciones impulsadas por el estado a través de enlaces de estilo. Estas técnicas adicionales se cubren en el capítulo Técnicas de Animación.
El Componente <Transition>
<Transition> es un componente incorporado: esto significa que está disponible en el template de cualquier componente sin necesidad de registrarlo. Se puede usar para aplicar animaciones de entrada y salida en elementos o componentes que se le pasan a través de su slot predeterminado. La entrada o salida puede ser desencadenada por uno de los siguientes:
- Renderizado condicional a través de
v-if - Visualización condicional a través de
v-show - Alternancia de componentes dinámicos a través del elemento especial
<component> - Cambio del atributo especial
key
Este es un ejemplo del uso más básico:
template
<button @click="show = !show">Toggle</button>
<Transition>
<p v-if="show">hola</p>
</Transition>css
/* ¡A continuación explicaremos qué hacen estas clases! */
.v-enter-active,
.v-leave-active {
transition: opacity 0.5s ease;
}
.v-enter-from,
.v-leave-to {
opacity: 0;
}hola
TIP
<Transition> solo soporta un único elemento o componente como contenido de su slot. Si el contenido es un componente, el componente también debe tener un único elemento raíz.
Cuando un elemento en un componente <Transition> es insertado o eliminado, ocurre lo siguiente:
Vue detectará automáticamente si el elemento objetivo tiene transiciones o animaciones CSS aplicadas. Si las tiene, un número de clases de transición CSS se añadirán/eliminarán en los momentos apropiados.
Si hay escuchadores para hooks de JavaScript, estos hooks serán llamados en los momentos apropiados.
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 Transition
Hay seis clases aplicadas para las transiciones de entrada / salida.

v-enter-from: Estado inicial para la entrada. Añadida antes de que el elemento sea insertado, eliminada un frame después de que el elemento sea insertado.v-enter-active: Estado activo para la entrada. Aplicada durante toda la fase de entrada. Añadida antes de que el elemento sea insertado, eliminada cuando la transición/animación finaliza. Esta clase se puede usar para definir la duración, el retardo y la curva de aceleración para la transición de entrada.v-enter-to: Estado final para la entrada. Añadida un frame después de que el elemento sea insertado (al mismo tiempo quev-enter-fromes eliminada), eliminada cuando la transición/animación finaliza.v-leave-from: Estado inicial para la salida. Añadida inmediatamente cuando se dispara una transición de salida, eliminada después de un frame.v-leave-active: Estado activo para la salida. Aplicada durante toda la fase de salida. Añadida inmediatamente cuando se dispara una transición de salida, eliminada cuando la transición/animación finaliza. Esta clase se puede usar para definir la duración, el retardo y la curva de aceleración para la transición de salida.v-leave-to: Estado final para la salida. Añadida un frame después de que se dispara una transición de salida (al mismo tiempo quev-leave-fromes eliminada), eliminada cuando la transición/animación finaliza.
v-enter-active y v-leave-active nos dan la capacidad de especificar diferentes curvas de aceleración para las transiciones de entrada / salida, de las cuales veremos un ejemplo en las siguientes secciones.
Transiciones con Nombre
Una transición se puede nombrar a través de la prop name:
template
<Transition name="fade">
...
</Transition>Para una transición nombrada, sus clases de transición se prefijarán con 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 fundido debería verse así:
css
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.5s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}Transiciones CSS
<Transition> se usa más comúnmente en combinación con transiciones CSS nativas, como se vio 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 animarse, la duración de la transición y las curvas de aceleración.
Aquí hay un ejemplo más avanzado que transiciona múltiples propiedades, con diferentes duraciones y curvas de aceleración para entrada y salida:
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 que el elemento es insertado, sino en un evento animationend.
Para la mayoría de las animaciones CSS, simplemente podemos declararlas bajo las clases *-enter-active y *-leave-active. Aquí hay 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 de Transición Personalizadas
También puedes especificar clases de transición personalizadas pasando las siguientes props a <Transition>:
enter-from-classenter-active-classenter-to-classleave-from-classleave-active-classleave-to-class
Estas anularán los nombres de clases convencionales. Esto es especialmente útil cuando quieres combinar el sistema de transición de Vue con una biblioteca de animación CSS existente, como Animate.css:
template
<!-- asumiendo 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 de Transiciones y Animaciones Juntas
Vue necesita adjuntar escuchadores de eventos para saber cuándo ha terminado una transición. Puede ser transitionend o animationend, dependiendo del tipo de reglas CSS aplicadas. Si solo estás usando una u otra, Vue puede detectar automáticamente el tipo correcto.
Sin embargo, en algunos casos puede que quieras tener ambos en el mismo elemento, por ejemplo, tener una animación CSS desencadenada por Vue, junto con un efecto de transición CSS al pasar el ratón. En estos casos, tendrás que declarar explícitamente el tipo que quieres que Vue tenga en cuenta, pasando la prop type, con un valor de animation o transition:
template
<Transition type="animation">...</Transition>Duración de las Transiciones Anidadas y la Transición Explícita
Aunque las clases de transición solo se aplican al elemento hijo directo en <Transition>, podemos transicionar elementos anidados usando 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 en la entrada, lo que crea una secuencia de animación de entrada escalonada:
css
/* retrasar la entrada del elemento anidado para lograr un efecto escalonado */
.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 en el elemento raíz de la transición. Con una transición anidada, el comportamiento deseado debería ser esperar hasta que las transiciones de todos los elementos internos hayan terminado.
En tales casos, puedes especificar una duración de transición explícita (en milisegundos) usando la prop duration en el componente <Transition>. La duración total debería coincidir con el retardo más la duración de la transición del elemento interno:
template
<Transition :duration="550">...</Transition>Hola
Si es necesario, también puedes especificar valores separados para las duraciones de entrada y salida usando un objeto:
template
<Transition :duration="{ enter: 500, leave: 800 }">...</Transition>Consideraciones de Rendimiento
Puede que notes que las animaciones mostradas anteriormente utilizan principalmente propiedades como transform y opacity. Estas propiedades son eficientes de animar porque:
No afectan el diseño del documento durante la animación, por lo que no desencadenan costosos cálculos de diseño CSS en cada frame de animación.
La mayoría de los navegadores modernos pueden aprovechar la aceleración de hardware de la GPU al animar
transform.
En comparación, propiedades como height o margin desencadenarán el diseño CSS, por lo que son mucho más costosas de animar y deben usarse con precaución.
Hooks de JavaScript
Puedes enganchar el proceso de transición con JavaScript escuchando eventos en el componente <Transition>:
template
<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 sea insertado en el DOM.
// usa esto para establecer el estado "enter-from" del elemento
function onBeforeEnter(el) {}
// llamado un frame después de que el elemento sea 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 finalizado.
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 parte del tiempo, deberías usar solo el hook de salida.
function onBeforeLeave(el) {}
// llamado cuando la transición de salida comienza.
// 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
done()
}
// llamado cuando la transición de salida ha finalizado y el
// elemento ha sido eliminado del DOM.
function onAfterLeave(el) {}
// solo disponible con transiciones v-show
function onLeaveCancelled(el) {}Estos hooks se pueden usar en combinación con transiciones / animaciones CSS o por sí solos.
Cuando se usan transiciones solo con JavaScript, generalmente es una buena idea añadir la prop :css="false". Esto le dice explícitamente a Vue que omita la detección automática de transiciones CSS. Aparte de ser ligeramente más eficiente, esto también evita que las reglas 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 obligatorios para los hooks @enter y @leave. De lo contrario, los hooks se llamarán sincrónicamente y la transición finalizará inmediatamente.
Aquí tienes una demo usando la librería GSAP para realizar las animaciones. Puedes, por supuesto, usar cualquier otra librería de animación que desees, por ejemplo Anime.js o Motion One:
Transiciones Reutilizables
Las transiciones pueden reutilizarse a través del sistema de componentes de Vue. Para crear una transición reutilizable, podemos crear un componente que envuelva el componente <Transition> y pase el contenido del slot:
vue
<script>
// Lógica de los hooks de JavaScript...
</script>
<template>
<!-- envuelve el componente Transition incorporado -->
<Transition
name="my-transition"
@enter="onEnter"
@leave="onLeave">
<slot></slot> <!-- pasa el contenido del slot -->
</Transition>
</template>
<style>
/*
CSS necesario...
Nota: evita usar <style scoped> aquí ya que no
se aplica al contenido del slot.
*/
</style>Ahora MyTransition puede importarse y usarse igual que la versión incorporada:
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 transicionar entre dos elementos usando v-if / v-else / v-else-if, siempre que nos aseguremos de que solo un elemento se muestre en un momento dado:
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>Haga clic para recorrer los estados:
Modos de Transición
En el ejemplo anterior, los elementos de entrada y salida se animan al mismo tiempo, y tuvimos 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. Podríamos querer que el elemento de salida se anime primero, y que el elemento de entrada solo se inserte después de que la animación de salida haya terminado. Orquestar tales animaciones manualmente sería muy complicado; por suerte, podemos habilitar este comportamiento pasando a <Transition> una prop mode:
template
<Transition mode="out-in">
...
</Transition>Aquí está la demo anterior con mode="out-in":
Haga clic para recorrer los estados:
<Transition> también soporta mode="in-out", aunque es mucho menos usado.
Transición entre Componentes
<Transition> también se puede usar alrededor de componentes dinámicos:
template
<Transition name="fade" mode="out-in">
<component :is="activeComponent"></component>
</Transition>Componente A
Transiciones Dinámicas
¡Las props de <Transition> como name también pueden ser dinámicas! Nos permite aplicar dinámicamente diferentes transiciones basándose en el cambio de estado:
template
<Transition :name="transitionName">
<!-- ... -->
</Transition>Esto puede ser útil cuando has definido transiciones / animaciones CSS usando las convenciones de clases de transición de Vue y quieres alternar entre ellas.
También puedes aplicar diferentes comportamientos en los hooks de transición de JavaScript basándote en el estado actual de tu componente. Finalmente, la forma definitiva 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 es realmente tu imaginación.
Transiciones con el Atributo Key
A veces necesitas forzar el re-renderizado de un elemento DOM para que ocurra una transición.
Toma este componente de contador, por ejemplo:
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>Si hubiéramos excluido el atributo key, solo se actualizaría el nodo de texto y, por lo tanto, no ocurriría ninguna transición. Sin embargo, con el atributo key en su lugar, Vue sabe que debe crear un nuevo elemento span cada vez que count cambia y, por lo tanto, el componente Transition tiene 2 elementos diferentes entre los que transicionar.
Relacionado