🎭 Transition 过渡动画 - 让界面动起来!
🌟 动画魔法师:为你的应用注入生命力,让每一次交互都充满惊喜!
✨ 什么是 Transition?
Transition 是 Vant 提供的过渡动画组件,它就像一位优雅的舞者,为元素的进入和离开添加流畅的动画效果。无论是淡入淡出、滑动展开,还是缩放变换,都能让你的界面变得生动有趣!
🎯 核心特色:
- 🎨 丰富动画:内置多种过渡效果
- ⚡ 性能优化:基于 CSS3 transform,流畅丝滑
- 🔧 简单易用:一个组件搞定所有动画需求
- 🎛️ 高度定制:支持自定义动画时长和缓动函数
🚀 快速上手
📦 基础用法
最简单的淡入淡出效果:
vue
<template>
<div>
<!-- 🎯 控制按钮 -->
<van-button @click="show = !show" type="primary">
{{ show ? '隐藏' : '显示' }}
</van-button>
<!-- ✨ 过渡动画 -->
<van-transition :show="show">
<div class="content">
🎉 Hello Vant Transition!
</div>
</van-transition>
</div>
</template>
<script setup>
import { ref } from 'vue';
const show = ref(false);
</script>
<style>
.content {
width: 200px;
height: 100px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
display: flex;
align-items: center;
justify-content: center;
border-radius: 8px;
margin-top: 16px;
}
</style>🎨 动画类型
🌟 内置动画效果
Vant 提供了丰富的内置动画效果:
| 动画名称 | 效果描述 | 适用场景 |
|---|---|---|
fade | 淡入淡出 | 🌅 通用过渡,优雅自然 |
fade-up | 向上淡入 | 📱 移动端常用,符合手势习惯 |
fade-down | 向下淡入 | 🔽 下拉菜单、通知等 |
fade-left | 向左淡入 | ⬅️ 侧边栏、抽屉等 |
fade-right | 向右淡入 | ➡️ 内容切换、导航等 |
slide-up | 向上滑入 | 📋 底部弹窗、操作面板 |
slide-down | 向下滑入 | 📢 顶部通知、下拉刷新 |
slide-left | 向左滑入 | 📄 页面切换、卡片滑动 |
slide-right | 向右滑入 | 🔄 返回操作、侧滑菜单 |
🎪 动画效果展示
vue
<template>
<div class="demo-container">
<!-- 🎛️ 动画选择器 -->
<van-field
v-model="selectedAnimation"
is-link
readonly
label="选择动画"
placeholder="点击选择动画效果"
@click="showPicker = true"
/>
<van-popup v-model:show="showPicker" position="bottom">
<van-picker
:columns="animationOptions"
@confirm="onConfirm"
@cancel="showPicker = false"
/>
</van-popup>
<!-- 🎯 控制按钮 -->
<van-button
@click="show = !show"
type="primary"
block
style="margin: 16px 0;"
>
{{ show ? '隐藏动画' : '显示动画' }}
</van-button>
<!-- ✨ 动画演示区域 -->
<div class="animation-stage">
<van-transition :show="show" :name="selectedAnimation">
<div class="demo-box">
<div class="animation-name">{{ selectedAnimation }}</div>
<div class="animation-desc">{{ getAnimationDesc(selectedAnimation) }}</div>
</div>
</van-transition>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue';
const show = ref(false);
const showPicker = ref(false);
const selectedAnimation = ref('fade');
const animationOptions = [
{ text: '淡入淡出 (fade)', value: 'fade' },
{ text: '向上淡入 (fade-up)', value: 'fade-up' },
{ text: '向下淡入 (fade-down)', value: 'fade-down' },
{ text: '向左淡入 (fade-left)', value: 'fade-left' },
{ text: '向右淡入 (fade-right)', value: 'fade-right' },
{ text: '向上滑入 (slide-up)', value: 'slide-up' },
{ text: '向下滑入 (slide-down)', value: 'slide-down' },
{ text: '向左滑入 (slide-left)', value: 'slide-left' },
{ text: '向右滑入 (slide-right)', value: 'slide-right' },
];
const onConfirm = ({ selectedValues }) => {
selectedAnimation.value = selectedValues[0];
showPicker.value = false;
};
const getAnimationDesc = (name) => {
const descriptions = {
'fade': '🌅 优雅的淡入淡出',
'fade-up': '📱 向上淡入,移动端首选',
'fade-down': '🔽 向下淡入,适合通知',
'fade-left': '⬅️ 向左淡入,侧边栏专用',
'fade-right': '➡️ 向右淡入,内容切换',
'slide-up': '📋 向上滑入,底部弹窗',
'slide-down': '📢 向下滑入,顶部通知',
'slide-left': '📄 向左滑入,页面切换',
'slide-right': '🔄 向右滑入,返回操作',
};
return descriptions[name] || '✨ 精美动画效果';
};
</script>
<style>
.demo-container {
padding: 16px;
}
.animation-stage {
min-height: 200px;
display: flex;
align-items: center;
justify-content: center;
background: #f7f8fa;
border-radius: 8px;
margin-top: 16px;
}
.demo-box {
width: 200px;
height: 120px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
border-radius: 12px;
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
}
.animation-name {
font-size: 16px;
font-weight: bold;
margin-bottom: 8px;
}
.animation-desc {
font-size: 12px;
opacity: 0.8;
text-align: center;
}
</style>⚙️ 高级配置
🎛️ 自定义动画时长
vue
<template>
<div>
<!-- 🐌 慢速动画 (1秒) -->
<van-transition :show="show1" :duration="1000">
<div class="slow-box">慢速动画 (1s)</div>
</van-transition>
<!-- ⚡ 快速动画 (200ms) -->
<van-transition :show="show2" :duration="200">
<div class="fast-box">快速动画 (0.2s)</div>
</van-transition>
<!-- 🎯 不同进入和离开时长 -->
<van-transition
:show="show3"
:duration="{ enter: 500, leave: 300 }"
>
<div class="custom-box">自定义时长</div>
</van-transition>
</div>
</template>🎨 自定义动画效果
创建你自己的动画效果:
vue
<template>
<van-transition :show="show" name="my-custom">
<div class="custom-content">自定义动画</div>
</van-transition>
</template>
<style>
/* 🎭 自定义动画类名 */
.my-custom-enter-active,
.my-custom-leave-active {
transition: all 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
.my-custom-enter-from {
opacity: 0;
transform: scale(0.8) rotate(-10deg);
}
.my-custom-leave-to {
opacity: 0;
transform: scale(1.2) rotate(10deg);
}
/* 🌟 添加一些特效 */
.custom-content {
background: linear-gradient(45deg, #ff6b6b, #4ecdc4);
color: white;
padding: 20px;
border-radius: 12px;
text-align: center;
box-shadow: 0 8px 32px rgba(255, 107, 107, 0.3);
}
</style>🎪 实际应用场景
📱 移动端弹窗
vue
<template>
<div>
<!-- 🎯 触发按钮 -->
<van-button @click="showModal = true" type="primary" block>
打开弹窗
</van-button>
<!-- 🌟 遮罩层 -->
<van-transition :show="showModal">
<div class="modal-overlay" @click="showModal = false">
<!-- 📋 弹窗内容 -->
<van-transition :show="showModal" name="slide-up">
<div class="modal-content" @click.stop>
<div class="modal-header">
<h3>🎉 精美弹窗</h3>
<van-icon name="cross" @click="showModal = false" />
</div>
<div class="modal-body">
<p>这是一个使用 Transition 组件创建的弹窗示例。</p>
<p>具有流畅的动画效果和良好的用户体验。</p>
</div>
<div class="modal-footer">
<van-button @click="showModal = false" type="default">
取消
</van-button>
<van-button @click="showModal = false" type="primary">
确认
</van-button>
</div>
</div>
</van-transition>
</div>
</van-transition>
</div>
</template>
<script setup>
import { ref } from 'vue';
const showModal = ref(false);
</script>
<style>
.modal-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: flex-end;
z-index: 1000;
}
.modal-content {
background: white;
border-radius: 16px 16px 0 0;
width: 100%;
max-height: 70vh;
overflow: hidden;
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 16px 20px;
border-bottom: 1px solid #eee;
}
.modal-body {
padding: 20px;
line-height: 1.6;
}
.modal-footer {
padding: 16px 20px;
display: flex;
gap: 12px;
border-top: 1px solid #eee;
}
</style>🔄 页面切换动画
vue
<template>
<div class="page-container">
<!-- 🧭 导航栏 -->
<van-nav-bar
:title="currentPage.title"
left-arrow
@click-left="goBack"
/>
<!-- 📄 页面内容 -->
<div class="page-content">
<van-transition
:show="true"
:name="transitionName"
mode="out-in"
>
<component :is="currentPage.component" :key="currentPage.name" />
</van-transition>
</div>
<!-- 🎯 页面切换按钮 -->
<div class="page-controls">
<van-button
v-for="page in pages"
:key="page.name"
@click="navigateTo(page)"
:type="currentPage.name === page.name ? 'primary' : 'default'"
size="small"
>
{{ page.title }}
</van-button>
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue';
const currentPageIndex = ref(0);
const transitionName = ref('slide-right');
const pages = [
{ name: 'home', title: '首页', component: 'PageHome' },
{ name: 'about', title: '关于', component: 'PageAbout' },
{ name: 'contact', title: '联系', component: 'PageContact' },
];
const currentPage = computed(() => pages[currentPageIndex.value]);
const navigateTo = (page) => {
const newIndex = pages.findIndex(p => p.name === page.name);
const oldIndex = currentPageIndex.value;
// 🎭 根据导航方向选择动画
transitionName.value = newIndex > oldIndex ? 'slide-left' : 'slide-right';
currentPageIndex.value = newIndex;
};
const goBack = () => {
if (currentPageIndex.value > 0) {
transitionName.value = 'slide-right';
currentPageIndex.value--;
}
};
</script>🎯 API 参考
Props
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
show | 是否显示组件 | boolean | false |
name | 动画类型 | string | 'fade' |
duration | 动画时长,单位毫秒 | number | object | 300 |
appear | 是否在初始渲染时使用过渡 | boolean | false |
Events
| 事件名 | 说明 | 回调参数 |
|---|---|---|
before-enter | 进入前触发 | - |
enter | 进入中触发 | el: Element |
after-enter | 进入后触发 | - |
before-leave | 离开前触发 | - |
leave | 离开中触发 | el: Element |
after-leave | 离开后触发 | - |
动画时长配置
typescript
// 🕐 统一时长
duration: 500
// 🎭 分别设置进入和离开时长
duration: {
enter: 300,
leave: 500
}💡 最佳实践
🎨 动画选择指南
| 场景 | 推荐动画 | 原因 |
|---|---|---|
| 🔔 通知提示 | fade-down | 从顶部自然出现,符合用户预期 |
| 📋 底部弹窗 | slide-up | 从底部滑出,移动端标准交互 |
| 🎭 模态对话框 | fade | 简洁优雅,不会分散注意力 |
| 📄 页面切换 | slide-left/right | 明确的方向感,增强导航体验 |
| 🎯 工具提示 | fade-up | 轻量级动画,快速响应 |
⚡ 性能优化技巧
vue
<template>
<!-- ✅ 推荐:使用 v-show 配合 Transition -->
<van-transition :show="visible">
<div v-show="visible" class="content">内容</div>
</van-transition>
<!-- ❌ 避免:频繁的 DOM 创建销毁 -->
<van-transition :show="visible">
<div v-if="visible" class="content">内容</div>
</van-transition>
</template>
<script setup>
// 🎯 使用合理的动画时长
const duration = {
enter: 250, // 进入稍快,提升响应感
leave: 200 // 离开更快,减少等待感
};
// 🔧 避免过于复杂的动画
const simpleTransition = 'fade'; // ✅ 简单高效
const complexTransition = 'bounce-scale-rotate'; // ❌ 过于复杂
</script>🎪 动画组合技巧
vue
<template>
<!-- 🌟 多层动画效果 -->
<van-transition :show="show" name="fade">
<div class="container">
<van-transition :show="show" name="slide-up" :duration="400">
<div class="header">标题区域</div>
</van-transition>
<van-transition :show="show" name="slide-up" :duration="500">
<div class="content">内容区域</div>
</van-transition>
<van-transition :show="show" name="slide-up" :duration="600">
<div class="footer">底部区域</div>
</van-transition>
</div>
</van-transition>
</template>🔧 常见问题
❓ 动画不生效?
🔍 检查清单:
- ✅ 确认
show属性正确绑定 - ✅ 检查 CSS 样式是否被覆盖
- ✅ 验证动画名称是否正确
- ✅ 确保元素有明确的尺寸
vue
<!-- ❌ 问题示例 -->
<van-transition :show="visible">
<div style="display: none;">内容</div> <!-- display: none 会阻止动画 -->
</van-transition>
<!-- ✅ 正确示例 -->
<van-transition :show="visible">
<div class="content">内容</div>
</van-transition>❓ 如何实现复杂动画?
vue
<template>
<van-transition
:show="show"
name="custom-complex"
@before-enter="onBeforeEnter"
@enter="onEnter"
@after-enter="onAfterEnter"
>
<div class="complex-element">复杂动画</div>
</van-transition>
</template>
<script setup>
// 🎭 使用 JavaScript 钩子实现复杂动画
const onBeforeEnter = (el) => {
el.style.opacity = '0';
el.style.transform = 'scale(0.5) rotate(-180deg)';
};
const onEnter = (el) => {
el.offsetHeight; // 触发重排
el.style.transition = 'all 0.5s cubic-bezier(0.68, -0.55, 0.265, 1.55)';
el.style.opacity = '1';
el.style.transform = 'scale(1) rotate(0deg)';
};
const onAfterEnter = (el) => {
console.log('🎉 动画完成!');
};
</script>🌟 进阶技巧
🎨 自定义缓动函数
css
/* 🎭 创建独特的动画感觉 */
.my-transition-enter-active {
transition: all 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
.my-transition-leave-active {
transition: all 0.3s cubic-bezier(0.55, 0.085, 0.68, 0.53);
}
/* 🌟 弹性效果 */
.bounce-enter-active {
animation: bounce-in 0.5s;
}
@keyframes bounce-in {
0% {
transform: scale(0);
}
50% {
transform: scale(1.1);
}
100% {
transform: scale(1);
}
}🔄 动画序列
vue
<template>
<div class="sequence-container">
<van-transition
v-for="(item, index) in items"
:key="item.id"
:show="show"
name="slide-up"
:duration="300"
:style="{ transitionDelay: `${index * 100}ms` }"
>
<div class="sequence-item">{{ item.text }}</div>
</van-transition>
</div>
</template>
<script setup>
const items = [
{ id: 1, text: '第一个元素' },
{ id: 2, text: '第二个元素' },
{ id: 3, text: '第三个元素' },
];
</script>相关文档 📚
🎯 核心组件
- Popup 弹出层 - 弹窗容器,常与过渡动画配合使用
- Dialog 对话框 - 对话框组件,内置优雅的过渡效果
- ActionSheet 动作面板 - 底部动作面板,使用滑入动画
- Overlay 遮罩层 - 背景遮罩,支持淡入淡出效果
🎨 交互组件
- Swipe 轮播 - 轮播组件,支持滑动过渡动画
- Tab 标签页 - 标签页切换,可配合过渡动画
- Collapse 折叠面板 - 折叠展开动画效果
- NoticeBar 通知栏 - 滚动通知,支持滑动动画
🔔 反馈组件
- Toast 轻提示 - 消息提示,内置多种过渡效果
- Notify 消息通知 - 顶部通知,使用下滑动画
- Loading 加载 - 加载状态,支持淡入淡出
⚡ 高级功能
- ConfigProvider 全局配置 - 统一配置动画主题和时长
- Locale 国际化 - 多语言环境下的动画配置