Skip to content

🎭 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是否显示组件booleanfalse
name动画类型string'fade'
duration动画时长,单位毫秒number | object300
appear是否在初始渲染时使用过渡booleanfalse

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>

🔧 常见问题

❓ 动画不生效?

🔍 检查清单

  1. ✅ 确认 show 属性正确绑定
  2. ✅ 检查 CSS 样式是否被覆盖
  3. ✅ 验证动画名称是否正确
  4. ✅ 确保元素有明确的尺寸
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>

相关文档 📚

🎯 核心组件

🎨 交互组件

🔔 反馈组件

⚡ 高级功能

基於Vant構建的企業級移動端解決方案