Teleport与过渡动画 #
一、Teleport组件 #
1.1 基本使用 #
Teleport可以将组件渲染到DOM树的其他位置。
vue
<template>
<div class="container">
<button @click="showModal = true">打开模态框</button>
<Teleport to="body">
<div v-if="showModal" class="modal">
<div class="modal-content">
<h2>模态框</h2>
<button @click="showModal = false">关闭</button>
</div>
</div>
</Teleport>
</div>
</template>
<script setup>
import { ref } from 'vue'
const showModal = ref(false)
</script>
1.2 Teleport属性 #
vue
<template>
<!-- to: 目标选择器 -->
<Teleport to="body">
<div>渲染到body</div>
</Teleport>
<!-- to: 目标DOM元素 -->
<Teleport :to="targetElement">
<div>渲染到指定元素</div>
</Teleport>
<!-- disabled: 禁用传送 -->
<Teleport to="body" :disabled="isMobile">
<div>移动端不传送</div>
</Teleport>
</template>
1.3 实际应用:模态框组件 #
vue
<!-- Modal.vue -->
<template>
<Teleport to="body">
<Transition name="modal">
<div v-if="modelValue" class="modal-overlay" @click.self="close">
<div class="modal-container">
<div class="modal-header">
<h3>{{ title }}</h3>
<button class="close-btn" @click="close">×</button>
</div>
<div class="modal-body">
<slot></slot>
</div>
<div class="modal-footer" v-if="$slots.footer">
<slot name="footer"></slot>
</div>
</div>
</div>
</Transition>
</Teleport>
</template>
<script setup>
defineProps({
modelValue: Boolean,
title: String
})
const emit = defineEmits(['update:modelValue'])
function close() {
emit('update:modelValue', false)
}
</script>
<style scoped>
.modal-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.modal-container {
background: white;
border-radius: 8px;
max-width: 500px;
width: 90%;
max-height: 90vh;
overflow: auto;
}
.modal-enter-active,
.modal-leave-active {
transition: opacity 0.3s ease;
}
.modal-enter-from,
.modal-leave-to {
opacity: 0;
}
</style>
二、过渡动画 #
2.1 Transition组件 #
vue
<template>
<button @click="show = !show">切换</button>
<Transition>
<p v-if="show">过渡内容</p>
</Transition>
</template>
<script setup>
import { ref } from 'vue'
const show = ref(true)
</script>
<style>
/* 进入动画 */
.v-enter-active {
transition: all 0.3s ease;
}
/* 离开动画 */
.v-leave-active {
transition: all 0.3s ease;
}
/* 进入开始状态 */
.v-enter-from {
opacity: 0;
transform: translateX(-20px);
}
/* 离开结束状态 */
.v-leave-to {
opacity: 0;
transform: translateX(20px);
}
</style>
2.2 自定义过渡类名 #
vue
<template>
<Transition
name="fade"
enter-active-class="animate__animated animate__fadeIn"
leave-active-class="animate__animated animate__fadeOut"
>
<p v-if="show">动画内容</p>
</Transition>
</template>
2.3 JavaScript钩子 #
vue
<template>
<Transition
@before-enter="onBeforeEnter"
@enter="onEnter"
@after-enter="onAfterEnter"
@enter-cancelled="onEnterCancelled"
@before-leave="onBeforeLeave"
@leave="onLeave"
@after-leave="onAfterLeave"
@leave-cancelled="onLeaveCancelled"
>
<div v-if="show">动画元素</div>
</Transition>
</template>
<script setup>
function onBeforeEnter(el) {
el.style.opacity = 0
}
function onEnter(el, done) {
// 动画结束后调用done
el.offsetHeight // 触发重排
el.style.transition = 'opacity 0.5s'
el.style.opacity = 1
el.addEventListener('transitionend', done)
}
function onAfterEnter(el) {
console.log('进入动画完成')
}
function onBeforeLeave(el) {
el.style.opacity = 1
}
function onLeave(el, done) {
el.style.transition = 'opacity 0.5s'
el.style.opacity = 0
el.addEventListener('transitionend', done)
}
function onAfterLeave(el) {
console.log('离开动画完成')
}
</script>
2.4 可复用过渡 #
vue
<!-- FadeTransition.vue -->
<template>
<Transition name="fade" mode="out-in">
<slot></slot>
</Transition>
</template>
<style scoped>
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.3s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
</style>
vue
<template>
<FadeTransition>
<div :key="currentPage">
{{ currentPage }}
</div>
</FadeTransition>
</template>
三、列表过渡 #
3.1 TransitionGroup #
vue
<template>
<button @click="addItem">添加</button>
<button @click="removeItem">移除</button>
<TransitionGroup name="list" tag="ul">
<li v-for="item in items" :key="item.id">
{{ item.text }}
</li>
</TransitionGroup>
</template>
<script setup>
import { ref } from 'vue'
const items = ref([
{ id: 1, text: '项目1' },
{ id: 2, text: '项目2' },
{ id: 3, text: '项目3' }
])
let nextId = 4
function addItem() {
items.value.push({ id: nextId++, text: `项目${nextId - 1}` })
}
function removeItem() {
items.value.pop()
}
</script>
<style>
.list-enter-active,
.list-leave-active {
transition: all 0.5s ease;
}
.list-enter-from,
.list-leave-to {
opacity: 0;
transform: translateX(30px);
}
/* 移动动画 */
.list-move {
transition: transform 0.5s ease;
}
</style>
3.2 列表排序动画 #
vue
<template>
<button @click="shuffle">打乱</button>
<TransitionGroup name="shuffle" tag="div" class="container">
<div v-for="item in items" :key="item.id" class="item">
{{ item.text }}
</div>
</TransitionGroup>
</template>
<script setup>
import { ref } from 'vue'
const items = ref([
{ id: 1, text: 'A' },
{ id: 2, text: 'B' },
{ id: 3, text: 'C' },
{ id: 4, text: 'D' }
])
function shuffle() {
items.value = items.value.sort(() => Math.random() - 0.5)
}
</script>
<style>
.container {
display: flex;
flex-wrap: wrap;
}
.item {
width: 50px;
height: 50px;
margin: 5px;
display: flex;
align-items: center;
justify-content: center;
background: #42b983;
color: white;
}
.shuffle-move {
transition: transform 0.5s ease;
}
</style>
四、过渡模式 #
4.1 in-out模式 #
vue
<template>
<Transition name="fade" mode="in-out">
<div :key="current" class="box">
{{ current }}
</div>
</Transition>
</template>
<style>
/* 先进入再离开 */
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.3s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
</style>
4.2 out-in模式 #
vue
<template>
<Transition name="fade" mode="out-in">
<div :key="current" class="box">
{{ current }}
</div>
</Transition>
</template>
<style>
/* 先离开再进入 */
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.3s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
</style>
五、常见过渡效果 #
5.1 滑动过渡 #
vue
<style>
.slide-enter-active,
.slide-leave-active {
transition: transform 0.3s ease;
}
.slide-enter-from {
transform: translateX(100%);
}
.slide-leave-to {
transform: translateX(-100%);
}
</style>
5.2 缩放过渡 #
vue
<style>
.scale-enter-active,
.scale-leave-active {
transition: all 0.3s ease;
}
.scale-enter-from,
.scale-leave-to {
opacity: 0;
transform: scale(0.9);
}
</style>
5.3 折叠过渡 #
vue
<template>
<button @click="expanded = !expanded">切换</button>
<Transition name="collapse">
<div v-show="expanded" class="content">
折叠内容
</div>
</Transition>
</template>
<style>
.collapse-enter-active,
.collapse-leave-active {
transition: all 0.3s ease;
overflow: hidden;
}
.collapse-enter-from,
.collapse-leave-to {
max-height: 0;
opacity: 0;
}
.content {
max-height: 200px;
}
</style>
六、总结 #
Teleport #
| 属性 | 说明 |
|---|---|
| to | 目标选择器或DOM元素 |
| disabled | 是否禁用传送 |
Transition类名 #
| 类名 | 说明 |
|---|---|
| v-enter-from | 进入开始状态 |
| v-enter-active | 进入过渡效果 |
| v-enter-to | 进入结束状态 |
| v-leave-from | 离开开始状态 |
| v-leave-active | 离开过渡效果 |
| v-leave-to | 离开结束状态 |
过渡动画要点:
- Teleport用于跨层级渲染
- Transition用于单个元素过渡
- TransitionGroup用于列表过渡
- 使用mode控制进入/离开顺序
- 可复用过渡组件封装动画
最后更新:2026-03-26