欢迎来到今天的“Vue Router 动画魔术秀”!
大家好,我是你们今天的魔术师(兼讲师),今天我们要一起揭秘 Vue Router 路由切换时的动画效果,让你的页面切换不再生硬,而是像丝绸般顺滑!准备好学习了吗? Let’s go!
一、动画的基础:transition
组件
在 Vue 的世界里,动画的核心武器就是 transition
组件。它能侦测到组件的进入、离开以及更新,并在这些时刻添加 CSS 类名,让我们能通过 CSS 控制动画。
-
transition
组件的基本结构:<transition name="fade"> <router-view /> </transition>
这里的
name
属性非常重要,它会影响到自动生成的 CSS 类名。 比如name="fade"
, Vue 会自动生成以下类名:类名 描述 fade-enter-from
进入过渡的起始状态。在元素插入之前添加,在元素插入之后移除。你可以理解为元素“即将进入”的状态。 fade-enter-active
进入过渡的激活状态。在整个进入过渡阶段应用。它会被用来定义过渡的持续时间、延迟和速度曲线。在元素插入之前添加,在过渡完成之后移除。 fade-enter-to
进入过渡的结束状态。在元素插入之后添加 (在一个 fade-enter-from
移除的同时),在过渡完成之后移除。你可以理解为元素“正在进入”的状态。fade-leave-from
离开过渡的起始状态。在离开过渡开始时添加,在离开过渡完成之后移除。你可以理解为元素“即将离开”的状态。 fade-leave-active
离开过渡的激活状态。在整个离开过渡阶段应用。它会被用来定义过渡的持续时间、延迟和速度曲线。在离开过渡开始时添加,在过渡完成之后移除。 fade-leave-to
离开过渡的结束状态。离开过渡开始时添加 (在一个 fade-leave-from
移除的同时),在过渡完成之后移除。你可以理解为元素“正在离开”的状态。 -
简单的淡入淡出动画:
<template> <transition name="fade" mode="out-in"> <router-view :key="$route.fullPath" /> </transition> </template> <style scoped> .fade-enter-from { opacity: 0; } .fade-enter-active { transition: opacity 0.5s ease; } .fade-enter-to { opacity: 1; } .fade-leave-from { opacity: 1; } .fade-leave-active { transition: opacity 0.5s ease; position: absolute; /*重要!*/ left: 0; top: 0; } .fade-leave-to { opacity: 0; } </style>
mode="out-in"
: 这个属性告诉transition
组件,先让旧组件离开 (out),再让新组件进入 (in)。这样可以避免两个组件同时存在时的混乱。:key="$route.fullPath"
: 这个key
属性至关重要,它告诉 Vue,如果路由改变了,router-view
就应该被视为一个新的组件,从而触发过渡。 如果没有这个key
,Vue 可能会认为只是router-view
内部的内容发生了变化,而不是一个全新的组件,导致过渡失效。 也可以用$route.path
,但是$route.fullPath
包含 query 参数和 hash。position: absolute; left: 0; top: 0;
非常重要,必须加在.fade-leave-active
上。否则在离开过渡时,旧组件会占据新的组件的位置,导致动画显示不正确。
二、让动画更智能:动态过渡
上面的例子虽然简单,但只能实现一种动画。如果想根据不同的路由切换方向,应用不同的动画效果,该怎么办呢? 这就需要用到动态过渡。
-
思路: 在路由切换时,判断是前进还是后退,然后根据方向设置不同的
transition
组件的name
属性。 -
实现:
-
定义一个混合 (mixin): 用来记录路由切换的方向。
// src/mixins/routeTransition.js export default { data() { return { transitionName: "slide-left" // 默认向左滑动 }; }, watch: { $route(to, from) { if (to.meta.index > from.meta.index) { this.transitionName = "slide-left"; // 前进,向左滑动 } else { this.transitionName = "slide-right"; // 后退,向右滑动 } } } };
meta.index
: 需要在路由配置中,为每个路由添加一个index
属性,表示页面的层级关系。 比如,首页的index
是 0,二级页面的index
是 1,以此类推。
-
在 App.vue 中使用混合:
<template> <transition :name="transitionName" mode="out-in"> <router-view :key="$route.fullPath" /> </transition> </template> <script> import routeTransition from "./mixins/routeTransition"; export default { mixins: [routeTransition] }; </script> <style scoped> /* 向左滑动 */ .slide-left-enter-from { transform: translateX(100%); } .slide-left-enter-active { transition: transform 0.3s ease; } .slide-left-enter-to { transform: translateX(0); } .slide-left-leave-from { transform: translateX(0); } .slide-left-leave-active { transition: transform 0.3s ease; position: absolute; left: 0; top: 0; } .slide-left-leave-to { transform: translateX(-100%); } /* 向右滑动 */ .slide-right-enter-from { transform: translateX(-100%); } .slide-right-enter-active { transition: transform 0.3s ease; } .slide-right-enter-to { transform: translateX(0); } .slide-right-leave-from { transform: translateX(0); } .slide-right-leave-active { transition: transform 0.3s ease; position: absolute; left: 0; top: 0; } .slide-right-leave-to { transform: translateX(100%); } </style>
-
配置路由:
// src/router/index.js import Vue from "vue"; import VueRouter from "vue-router"; import Home from "../views/Home.vue"; import About from "../views/About.vue"; import Detail from "../views/Detail.vue"; Vue.use(VueRouter); const routes = [ { path: "/", name: "Home", component: Home, meta: { index: 0 } }, { path: "/about", name: "About", component: About, meta: { index: 1 } }, { path: "/detail/:id", name: "Detail", component: Detail, meta: { index: 2 } } ]; const router = new VueRouter({ mode: "history", base: process.env.BASE_URL, routes }); export default router;
meta: { index: ... }
: 每个路由都必须配置index
属性,用于判断路由切换的方向。
-
三、更高级的动画:JavaScript 钩子
transition
组件还提供了 JavaScript 钩子,允许我们在动画的各个阶段执行 JavaScript 代码,实现更复杂的动画效果。
-
常用的钩子:
钩子 描述 beforeEnter
在元素被插入到 DOM 之前调用。 enter
在元素被插入到 DOM 之后调用。 afterEnter
在进入过渡完成之后调用。 enterCancelled
当进入过渡被取消时调用。 beforeLeave
在离开过渡开始之前调用。 leave
在离开过渡开始之后调用。 afterLeave
在离开过渡完成之后调用。 leaveCancelled
当离开过渡被取消时调用。 -
使用 JavaScript 钩子的例子:
<template> <transition :name="transitionName" mode="out-in" @before-enter="beforeEnter" @enter="enter" @after-enter="afterEnter" @before-leave="beforeLeave" @leave="leave" @after-leave="afterLeave" > <router-view :key="$route.fullPath" /> </transition> </template> <script> import routeTransition from "./mixins/routeTransition"; export default { mixins: [routeTransition], methods: { beforeEnter(el) { console.log("beforeEnter", el); // 在元素被插入到 DOM 之前执行的代码 }, enter(el, done) { console.log("enter", el); // 在元素被插入到 DOM 之后执行的代码 // 可以使用 `done` 回调函数来手动控制过渡的完成 // 例如,使用 JavaScript 动画库来实现更复杂的动画 // 在动画完成后调用 `done()` setTimeout(() => { done(); // 必须调用 done() }, 500); }, afterEnter(el) { console.log("afterEnter", el); // 在进入过渡完成之后执行的代码 }, beforeLeave(el) { console.log("beforeLeave", el); // 在离开过渡开始之前执行的代码 }, leave(el, done) { console.log("leave", el); // 在离开过渡开始之后执行的代码 // 同样可以使用 `done` 回调函数来手动控制过渡的完成 setTimeout(() => { done(); // 必须调用 done() }, 500); }, afterLeave(el) { console.log("afterLeave", el); // 在离开过渡完成之后执行的代码 } } }; </script> <style scoped> /* 这里可以保留简单的 CSS 过渡,或者完全使用 JavaScript 控制动画 */ </style>
done
回调函数: 在enter
和leave
钩子中,可以接收一个done
回调函数。 如果使用了done
,就必须在动画完成后调用它,否则 Vue 会认为过渡没有完成,导致页面卡住。- JavaScript 动画库: 可以使用 GSAP、Anime.js 等 JavaScript 动画库,在钩子函数中编写更复杂的动画逻辑。
四、使用 transition
组件的一些注意事项:
router-view
必须是transition
组件的直接子元素: 否则动画可能无法正常工作。key
属性: 必须为router-view
添加key
属性,并且key
的值应该是能够唯一标识路由的值,例如$route.fullPath
。mode
属性: 根据需要选择out-in
或in-out
模式。- CSS 类名: 确保 CSS 类名与
transition
组件的name
属性一致。 - 性能: 复杂的动画可能会影响页面性能,要注意优化。 尽量使用 CSS 过渡,而不是 JavaScript 动画,因为 CSS 过渡通常性能更好。
五、总结
今天我们一起学习了 Vue Router 中实现路由过渡动画的几种方法:
- 使用
transition
组件和 CSS 类名实现简单的动画。 - 使用动态过渡,根据路由切换方向应用不同的动画效果。
- 使用 JavaScript 钩子,实现更复杂的动画效果。
掌握这些技巧,你就可以让你的 Vue 应用的路由切换更加生动有趣,给用户带来更好的体验!
最后,给大家留一个小作业: 尝试使用 JavaScript 钩子,结合 GSAP 动画库,实现一个更炫酷的路由过渡动画。 期待你们的精彩作品!
今天的“Vue Router 动画魔术秀”就到这里,感谢大家的观看! 我们下次再见!