路由过渡动画 | Vue Router
概述
Vue Router 与 Vue 的过渡系统完美集成,允许你为路由切换添加平滑的动画效果。通过合理的过渡动画,可以显著提升用户体验和应用的专业感。
基本过渡配置
使用 Vue Transition 组件
vue
<template>
<router-view v-slot="{ Component }">
<transition name="fade" mode="out-in">
<component :is="Component" />
</transition>
</router-view>
</template>
<style>
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.5s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
</style>不同的过渡模式
1. 淡入淡出(Fade)
css
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.3s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}2. 滑动效果(Slide)
css
.slide-enter-active,
.slide-leave-active {
transition: transform 0.3s ease;
}
.slide-enter-from {
transform: translateX(100%);
}
.slide-leave-to {
transform: translateX(-100%);
}3. 缩放效果(Scale)
css
.scale-enter-active,
.scale-leave-active {
transition: all 0.3s ease;
}
.scale-enter-from {
transform: scale(0.9);
opacity: 0;
}
.scale-leave-to {
transform: scale(1.1);
opacity: 0;
}基于路由的过渡动画
根据路由元信息动态过渡
vue
<template>
<router-view v-slot="{ Component, route }">
<transition :name="route.meta.transition || 'fade'" mode="out-in">
<component :is="Component" :key="route.path" />
</transition>
</router-view>
</template>
<script setup>
// 路由配置
const routes = [
{
path: '/',
component: Home,
meta: { transition: 'slide' }
},
{
path: '/about',
component: About,
meta: { transition: 'fade' }
}
]
</script>根据路由方向动态过渡
vue
<template>
<router-view v-slot="{ Component, route }">
<transition :name="transitionName" mode="out-in">
<component :is="Component" :key="route.path" />
</transition>
</router-view>
</template>
<script setup>
import { ref, watch } from 'vue'
import { useRouter } from 'vue-router'
const router = useRouter()
const transitionName = ref('slide-left')
// 监听路由变化,决定过渡方向
watch(
() => router.currentRoute.value,
(to, from) => {
const toDepth = to.meta.depth || 0
const fromDepth = from.meta.depth || 0
transitionName.value = toDepth < fromDepth ? 'slide-right' : 'slide-left'
}
)
</script>
<style>
.slide-left-enter-active,
.slide-left-leave-active,
.slide-right-enter-active,
.slide-right-leave-active {
transition: transform 0.3s ease;
}
.slide-left-enter-from {
transform: translateX(100%);
}
.slide-left-leave-to {
transform: translateX(-100%);
}
.slide-right-enter-from {
transform: translateX(-100%);
}
.slide-right-leave-to {
transform: translateX(100%);
}
</style>高级过渡技巧
1. 交错动画(Staggered)
vue
<template>
<router-view v-slot="{ Component, route }">
<transition-group name="stagger" tag="div" class="page-container">
<component :is="Component" :key="route.path" />
</transition-group>
</router-view>
</template>
<style>
.stagger-enter-active {
transition: all 0.3s ease;
transition-delay: calc(0.1s * var(--stagger-index));
}
.stagger-leave-active {
transition: all 0.3s ease;
}
.stagger-enter-from {
opacity: 0;
transform: translateY(20px);
}
.stagger-leave-to {
opacity: 0;
transform: translateY(-20px);
}
</style>2. 页面特定的过渡样式
vue
<script setup>
// 为不同页面设置不同的过渡延迟
const pageTransitions = {
'/': { delay: 0, duration: 0.3 },
'/about': { delay: 0.1, duration: 0.5 },
'/contact': { delay: 0.2, duration: 0.4 }
}
function getTransitionStyle(route) {
const config = pageTransitions[route.path] || pageTransitions['/']
return {
'--transition-delay': `${config.delay}s`,
'--transition-duration': `${config.duration}s`
}
}
</script>
<template>
<router-view v-slot="{ Component, route }">
<transition name="custom">
<component
:is="Component"
:key="route.path"
:style="getTransitionStyle(route)"
/>
</transition>
</router-view>
</template>
<style>
.custom-enter-active,
.custom-leave-active {
transition: all var(--transition-duration) ease;
transition-delay: var(--transition-delay);
}
</style>实际应用场景
场景 1:移动端应用过渡
vue
<template>
<div class="mobile-app">
<nav class="tab-bar">
<router-link to="/">首页</router-link>
<router-link to="/discover">发现</router-link>
<router-link to="/profile">我的</router-link>
</nav>
<router-view v-slot="{ Component, route }">
<transition :name="getMobileTransition(route)" mode="out-in">
<component :is="Component" :key="route.path" />
</transition>
</router-view>
</div>
</template>
<script setup>
function getMobileTransition(route) {
const tabOrder = ['/', '/discover', '/profile']
const currentIndex = tabOrder.indexOf(route.path)
const prevIndex = tabOrder.indexOf(history.state?.prevPath || '/')
return currentIndex > prevIndex ? 'slide-left' : 'slide-right'
}
</script>
<style>
.mobile-app {
position: relative;
overflow: hidden;
}
.slide-left-enter-active,
.slide-left-leave-active,
.slide-right-enter-active,
.slide-right-leave-active {
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
position: absolute;
width: 100%;
top: 0;
}
.slide-left-enter-from {
transform: translateX(100%);
}
.slide-left-leave-to {
transform: translateX(-100%);
}
.slide-right-enter-from {
transform: translateX(-100%);
}
.slide-right-leave-to {
transform: translateX(100%);
}
</style>场景 2:管理后台过渡
vue
<template>
<div class="admin-layout">
<aside class="sidebar">
<nav>
<router-link to="/admin/dashboard">仪表盘</router-link>
<router-link to="/admin/users">用户管理</router-link>
<router-link to="/admin/settings">系统设置</router-link>
</nav>
</aside>
<main class="content">
<router-view v-slot="{ Component, route }">
<transition name="page-fade" mode="out-in">
<component :is="Component" :key="route.path" />
</transition>
</router-view>
</main>
</div>
</template>
<style>
.page-fade-enter-active {
animation: pageFadeIn 0.4s ease;
}
.page-fade-leave-active {
animation: pageFadeOut 0.3s ease;
}
@keyframes pageFadeIn {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes pageFadeOut {
from {
opacity: 1;
transform: translateY(0);
}
to {
opacity: 0;
transform: translateY(-20px);
}
}
</style>性能优化技巧
1. 硬件加速优化
css
.optimized-transition {
transform: translateZ(0);
backface-visibility: hidden;
perspective: 1000px;
}
.smooth-transition {
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
will-change: transform;
}2. 减少重排重绘
css
.efficient-transition {
/* 只变换 opacity 和 transform,避免布局变化 */
transition: opacity 0.3s ease, transform 0.3s ease;
}
.avoid-layout-transition {
/* 避免这些属性,它们会导致重排 */
/* transition: width, height, margin, padding */
}3. 适当的动画时长
css
/* 快速响应(移动端) */
.quick-transition {
transition-duration: 0.2s;
}
/* 标准时长 */
.standard-transition {
transition-duration: 0.3s;
}
/* 强调效果 */
.emphasized-transition {
transition-duration: 0.5s;
}最佳实践
1. 保持一致性
css
/* 定义统一的过渡变量 */
:root {
--transition-duration-fast: 0.2s;
--transition-duration-normal: 0.3s;
--transition-duration-slow: 0.5s;
--transition-timing: cubic-bezier(0.4, 0, 0.2, 1);
}
.page-transition {
transition: all var(--transition-duration-normal) var(--transition-timing);
}2. 可访问性考虑
css
/* 尊重用户偏好 */
@media (prefers-reduced-motion: reduce) {
.page-transition {
transition: none;
}
}3. 优雅降级
javascript
// 检测浏览器支持情况
function supportsSmoothTransitions() {
return 'transition' in document.documentElement.style
}
// 不支持的浏览器使用简单过渡
const transitionName = supportsSmoothTransitions()
? 'smooth-slide'
: 'basic-fade'故障排除
常见问题及解决方案
动画闪烁
- 确保使用
mode="out-in"或mode="in-out" - 检查元素定位是否正确
- 确保使用
性能问题
- 避免同时动画过多元素
- 使用
will-change属性优化
移动端卡顿
- 使用硬件加速属性
- 简化动画效果
🎯 总结:合理的路由过渡动画可以大大提升用户体验。通过精心设计的动画效果,可以让应用看起来更加专业和流畅。