Vue Router动画的核心是什么?先理清概念
做Vue项目时,很多同学想给路由切换加动画,却卡在“不知道从哪下手”“动画不生效”“嵌套路由动画乱套”这些问题上,今天就把Vue Router动画从基础到进阶的逻辑拆明白,不管是简单淡入淡出,还是复杂嵌套滑动,看完能自己折腾出想要的效果~
得先明白**Vue的过渡系统**和**Vue Router**是咋配合的,Vue本身提供`举个简单逻辑:当用户从/pageA跳到/pageB,<router-view>
里渲染的组件会从PageA变成PageB,这时候用<transition>
把<router-view>
包起来,就能控制“PageA离开”和“PageB进入”的动画。
这里要记住几个关键的CSS类(以transition的name为fade
为例):
fade-enter-from
:进入动画开始前的状态(比如opacity: 0
)fade-enter-active
:进入动画执行中的状态(比如加过渡时间)fade-enter-to
:进入动画结束后的状态(比如opacity: 1
)- 离开的逻辑类似,对应
fade-leave-from
、fade-leave-active
、fade-leave-to
这些类会在组件切换时被自动添加/移除,配合CSS的transition
或animation
属性,就能实现平滑过渡。
最基础的路由切换动画怎么实现?分步骤做
先从最简单的全局淡入淡出动画入手,步骤很清晰:
步骤1:用<transition>
包裹<router-view>
在App.vue(或路由的根组件)里,把<router-view>
用<transition>
包起来,还能给transition加个name
(方便后续CSS定位):
<template> <div id="app"> <transition name="fade"> <router-view></router-view> </transition> <!-- 导航栏之类的其他内容 --> </div> </template>
步骤2:写CSS过渡样式
在<style>
里定义动画的起始、结束和过渡过程:
.fade-enter-from, .fade-leave-to { opacity: 0; /* 进入前、离开后是透明的 */ } .fade-enter-active, .fade-leave-active { transition: opacity 0.5s ease; /* 过渡时间0.5秒,缓动效果 */ } .fade-enter-to, .fade-leave-from { opacity: 1; /* 进入后、离开前是不透明的 */ }
这样一来,每次路由切换时,离开的组件会从透明变不透明(然后消失),进入的组件会从透明变不透明(逐渐出现),实现淡入淡出,如果想改动画时长或效果,直接调CSS里的transition
属性就行,比如把5s
改成3s
,或者把ease
换成linear
(线性过渡)。
不同路由页面要不同动画?动态过渡这么搞
实际项目里,不可能所有页面切换都是淡入淡出,比如从首页跳详情页要“从下往上滑”,从详情页返回首页要“从上往下滑”;或者某些页面用缩放动画,另一些用滑动,这时候得动态控制过渡效果。
方法1:用路由元信息(meta
)标记动画
在路由配置(router/index.js
)里,给每个路由加meta
字段,标记要用的动画name
:
const routes = [ { path: '/', name: 'Home', component: Home, meta: { transition: 'slide-left' } // 首页的进入动画用slide-left }, { path: '/detail', name: 'Detail', component: Detail, meta: { transition: 'slide-right' } // 详情页的进入动画用slide-right } ]
然后在App.vue里,把transition的name
绑定成动态变量,从当前路由的meta
里取:
<template> <transition :name="$route.meta.transition"> <router-view></router-view> </transition> </template> <style> /* 滑动动画的CSS,以slide-left为例 */ .slide-left-enter-from { transform: translateX(100%); /* 从右侧完全进入 */ } .slide-left-enter-active { transition: transform 0.5s ease; } .slide-left-enter-to { transform: translateX(0); /* 最终到原位 */ } /* slide-right类似,把translateX改成-100%之类的,根据需求调整方向 */ </style>
方法2:根据路由方向判断动画(前进/后退)
有时候要区分“用户是前进到新页面,还是回退到上一页”,比如手机App里的返回动画,这时候可以结合router.beforeEach
钩子,记录路由的进入方向:
在router/index.js
里:
let isBack = false; router.beforeEach((to, from, next) => { // 判断是前进还是后退:如果to的路径在from的匹配记录里更靠后,算前进;否则后退 isBack = from.matched.some(record => record.path === to.path); next(); });
然后在App.vue里,把transition的name
根据isBack
动态切换:
<template> <transition :name="isBack ? 'slide-back' : 'slide-forward'"> <router-view></router-view> </transition> </template> <script> import router from './router'; // 引入路由实例 export default { data() { return { isBack: false } }, mounted() { router.beforeEach((to, from) => { this.isBack = from.matched.some(record => record.path === to.path); }); } } </script>
这样就能根据用户操作方向,自动切换“前进动画”和“后退动画”,模拟原生App的切换逻辑~
嵌套路由的动画咋处理?多层结构的过渡逻辑
如果项目用了嵌套路由(比如页面里有侧边栏+内容区,内容区是子路由),这时候要注意:每个<router-view>
都得单独加<transition>
,不然父路由和子路由的动画会冲突。
举个结构例子:父组件Layout
里有侧边栏,和一个显示子路由的<router-view>
;子路由组件Article
里可能还有自己的子路由(比如评论区)。
父路由的动画包裹
在Layout.vue
里,给子路由的<router-view>
加过渡:
<template> <div class="layout"> <aside>侧边栏</aside> <transition name="slide-right"> <router-view></router-view> <!-- 这里渲染子路由,比如Article --> </transition> </div> </template>
子路由自己的动画
在Article.vue
里,如果还有子路由(比如/article/comment
),同样给内部的<router-view>
加过渡:
<template> <div class="article"> <h1>文章内容</h1> <transition name="fade"> <router-view></router-view> <!-- 这里渲染评论区组件 --> </transition> </div> </template>
这样父路由切换时用滑动动画,子路由切换时用淡入淡出,层级关系就很清晰,要注意的是,嵌套路由的动画时长、方向要协调,别让父动画和子动画“打架”(比如父动画还没结束,子动画就开始闪)。
想搞更炫酷的效果?结合第三方动画库
如果觉得自己写CSS动画太麻烦,或者想要更复杂的效果(比如弹性动画、3D翻转),可以结合第三方动画库,比如Animate.css、GSAP(GreenSock)。
用Animate.css快速实现预设动画
Animate.css提供了上百种预设动画类(比如fadeIn
、slideInLeft
、bounce
),用法很简单:
- 安装Animate.css:
npm install animate.css
- 在
main.js
里引入:import 'animate.css';
- 在
<transition>
里用enter-active-class
和leave-active-class
指定动画类:<transition enter-active-class="animate__animated animate__fadeIn" leave-active-class="animate__animated animate__fadeOut" > <router-view></router-view> </transition>
这样切换路由时,进入的组件会执行fadeIn
动画,离开的组件执行fadeOut
动画,不用自己写CSS过渡类,省事儿~
用GSAP实现复杂自定义动画
如果需要更精细的控制(比如动画序列、贝塞尔曲线调节、滚动触发),可以用GSAP,思路是在<transition>
的JavaScript钩子(比如@enter
、@leave
)里写GSAP的动画逻辑。
举个例子,让进入的组件从下往上滑并放大:
<transition @enter="enterAnimation" @leave="leaveAnimation" > <router-view></router-view> </transition> <script> import { gsap } from 'gsap'; export default { methods: { enterAnimation(el, done) { gsap.fromTo(el, { y: '100%', scale: 0.8 }, // 起始状态:从下方完全隐藏,缩小 { y: 0, scale: 1, duration: 0.6, onComplete: done } // 结束状态,完成后调用done告诉Vue动画结束 ); }, leaveAnimation(el, done) { gsap.to(el, { y: '-100%', opacity: 0, duration: 0.6, onComplete: done } // 离开时向上滑并透明 ); } } } </script>
GSAP的优势是能做非常复杂的时间线动画(比如先滑入再缩放再淡入),适合追求极致交互的项目~
路由动画常见坑有哪些?避坑指南
折腾动画时,这些问题很容易踩雷,提前避坑能省很多调试时间:
坑1:动画完全不生效
- 检查
<transition>
是不是真的包住了<router-view>
,有没有多包或少包(比如把导航栏也包进去了,导致动画对象不对)。 - 看CSS类名和transition的
name
是否对应(比如name
是slide
,CSS里写成slide-enter-from
,别写错成slider-enter-from
)。 - 确认路由切换时,组件确实被销毁/重建(如果组件用了
keep-alive
,离开动画可能不触发,因为组件没被销毁,只是被缓存了)。
坑2:切换时出现“闪烁”
原因通常是没有给动画元素设置position
或overflow
,比如做滑动动画时,父容器如果没设overflow: hidden
,组件滑动时会超出容器,看起来像闪一下,解决方法:给包裹<transition>
的父元素加overflow: hidden; position: relative;
。
坑3:后退时动画方向不对
比如前进是从右往左滑,后退应该从左往右滑,但实际还是从右往左,这时候要结合前面说的“判断路由方向”(用router.beforeEach
记录isBack
),动态切换transition的name
或者CSS类。
坑4:动画时长和路由切换不同步
比如CSS里写了transition: 0.5s
,但实际动画感觉“没做完就切走了”,这是因为Vue的过渡默认等待transitionend
或animationend
事件,如果自己用JS控制动画(比如GSAP),要记得在动画完成后调用done
回调,告诉Vue“动画结束了,可以销毁旧组件了”。
把这些逻辑理清楚后,Vue Router动画其实没那么难——核心是利用Vue的过渡系统,给路由切换的“进入/离开”阶段加样式或JS逻辑,从基础淡入淡出,到动态控制、嵌套路由、结合第三方库,再到避坑,整个流程跑通后,项目里的页面切换体验能提升一大截,要是你现在刚好在做Vue项目的交互动效,照着这些步骤试一遍,肯定能折腾出想要的效果~
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。