Code前端首页关于Code前端联系我们

Vue Router动画的核心是什么?先理清概念

terry 3周前 (09-04) 阅读数 44 #Vue

做Vue项目时,很多同学想给路由切换加动画,却卡在“不知道从哪下手”“动画不生效”“嵌套路由动画乱套”这些问题上,今天就把Vue Router动画从基础到进阶的逻辑拆明白,不管是简单淡入淡出,还是复杂嵌套滑动,看完能自己折腾出想要的效果~

得先明白**Vue的过渡系统**和**Vue Router**是咋配合的,Vue本身提供``组件,能给单个元素/组件的“进入/离开”加动画;而Vue Router里的``是用来渲染匹配路由的组件,所以给路由加动画,本质是给``包裹的组件切换加过渡效果。

举个简单逻辑:当用户从/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-fromfade-leave-activefade-leave-to

这些类会在组件切换时被自动添加/移除,配合CSS的transitionanimation属性,就能实现平滑过渡。

最基础的路由切换动画怎么实现?分步骤做

先从最简单的全局淡入淡出动画入手,步骤很清晰:

步骤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提供了上百种预设动画类(比如fadeInslideInLeftbounce),用法很简单:

  1. 安装Animate.css:npm install animate.css
  2. main.js里引入:import 'animate.css';
  3. <transition>里用enter-active-classleave-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是否对应(比如nameslide,CSS里写成slide-enter-from,别写错成slider-enter-from)。
  • 确认路由切换时,组件确实被销毁/重建(如果组件用了keep-alive,离开动画可能不触发,因为组件没被销毁,只是被缓存了)。

坑2:切换时出现“闪烁”

原因通常是没有给动画元素设置positionoverflow,比如做滑动动画时,父容器如果没设overflow: hidden,组件滑动时会超出容器,看起来像闪一下,解决方法:给包裹<transition>的父元素加overflow: hidden; position: relative;

坑3:后退时动画方向不对

比如前进是从右往左滑,后退应该从左往右滑,但实际还是从右往左,这时候要结合前面说的“判断路由方向”(用router.beforeEach记录isBack),动态切换transition的name或者CSS类。

坑4:动画时长和路由切换不同步

比如CSS里写了transition: 0.5s,但实际动画感觉“没做完就切走了”,这是因为Vue的过渡默认等待transitionendanimationend事件,如果自己用JS控制动画(比如GSAP),要记得在动画完成后调用done回调,告诉Vue“动画结束了,可以销毁旧组件了”。

把这些逻辑理清楚后,Vue Router动画其实没那么难——核心是利用Vue的过渡系统,给路由切换的“进入/离开”阶段加样式或JS逻辑,从基础淡入淡出,到动态控制、嵌套路由、结合第三方库,再到避坑,整个流程跑通后,项目里的页面切换体验能提升一大截,要是你现在刚好在做Vue项目的交互动效,照着这些步骤试一遍,肯定能折腾出想要的效果~

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

热门