likes
comments
collection
share

【笔记】VueRouter 路由过度效果 - 左出右进

作者站长头像
站长
· 阅读数 16

主要是用于一种场景,用于解决切换路由时出现滚动条,或者渲染抖动的情况。:

  • 前进时(/ -> /docs),左出-右进
  • 后退时(/docs -> /),右出-左进

演示如下: 【笔记】VueRouter 路由过度效果 - 左出右进


实现时会用到如下组件:

以及需要路由守卫和路由视图插槽的参与:

  • router.afterEach(to => to.meta)
  • <RouterView v-slot="{ Component, route }">

正常的路由列表配置就不过多描述。

1. 页面元素构成

首先,我们需要确定路由的组成结构,这里以常见结构为例:

<RouterView v-slot="{ Component, route }">
  <Transition :name="route.meta.transitionName">
    <component :is="Component" />
  </Transition>
</RouterView>
  1. 使用路由插槽属性将路由切换的组件获取出来。
  2. 一个组件就是一个页面,Transition 即可满足需求,所以这里不需要 TransitionGroup
  3. 因为要区分前进后退或者是需要区分状态,这里都会用到 route.meta 自定义属性,这里定义了一个 transitionName 的属性,来定义过度的样式。
  4. 使用component来接受插槽传递出来的组件进行渲染。

2. 利用导航守卫控制过度样式

如果只想拥有动画不必区分跳转情况的,则可不考虑。

这里是需要区分左右,且动画表现一致,即离开的页面从左侧出去,跳转的页面从右侧进入。

那么就会需要路由导航的介入了。

router.afterEach((to, from) => {
  // 记录进入的路由 url
  const toDepth = to.path === '/' ? 0 : to.path.split('/').length
  // 记录离开的路由 url
  const fromDepth = from.path === '/' ? 0 : from.path.split('/').length

  // 切换过度样式
  to.meta.transitionName = toDepth < fromDepth ? 'fade-left' : 'fade-right'

  // 控制首页载入时候不启用路由过度动画
  if (from.fullPath === '/' && !from.name) {
    to.meta.transitionName = ''
  }
})
  1. 使用 (top, from) 来获取 path 信息,切分 / 来获取 url 深度
  2. to < from 的深度时,则使用左侧退出过度,反之是右侧退出过度
  3. 当然还需要处理首次进入时的状态,这里较为简单,如果有特殊的首页逻辑,那么针对性处理即可

3. 过度样式

文档:Transition | 基于 CSS 的过渡效果

进入状态

  1. v-enter-from:进入动画的起始状态。在元素插入之前添加,在元素插入完成后的下一帧移除。
  2. v-enter-active:进入动画的生效状态。应用于整个进入动画阶段。在元素被插入之前添加,在过渡或动画完成之后移除。这个 class 可以被用来定义进入动画的持续时间、延迟与速度曲线类型。
  3. v-enter-to:进入动画的结束状态。在元素插入完成后的下一帧被添加 (也就是 v-enter-from 被移除的同时),在过渡或动画完成之后移除。

离开状态

  1. v-leave-from:离开动画的起始状态。在离开过渡效果被触发时立即添加,在一帧后被移除。
  2. v-leave-active:离开动画的生效状态。应用于整个离开动画阶段。在离开过渡效果被触发时立即添加,在过渡或动画完成之后移除。这个 class 可以被用来定义离开动画的持续时间、延迟与速度曲线类型。
  3. v-leave-to:离开动画的结束状态。在一个离开动画被触发后的下一帧被添加 (也就是 v-leave-from 被移除的同时),在过渡或动画完成之后移除。

由于需要控制进出跳转的状态,那么在这就把所有状态都用上了。

/* 路由过度样式 */
/* (1.) */
.fade-left-enter-active,
.fade-left-leave-active,
.fade-right-enter-active,
.fade-right-leave-active {
  position: absolute;
  top: 0;
  right: 0;
  left: 0;
  bottom: 0;
  height: max-content;
  backface-visibility: hidden;
  will-change: transform;
  transition: transform 0.5s ease, opacity 0.5s ease;
  overflow: hidden;
}

/* (2.) */
.fade-left-enter-active,
.fade-right-enter-active {
  transition-delay: 0.3s, 0.3s;
}

/* (3.) */
.fade-left-enter-to,
.fade-right-enter-to {
  transform: translate3d(0, 0, 0);
}

/* (4.) */
.fade-left-enter-from {
  opacity: 0;
  transform: translate3d(-100%, 0, 0);
}

.fade-right-enter-from {
  opacity: 0;
  transform: translate3d(100%, 0, 0);
}

/* (5.) */
.fade-left-leave-to {
  opacity: 0;
  transform: translate3d(100%, 0, 0);
}

.fade-right-leave-to {
  opacity: 0;
  transform: translate3d(-100%, 0, 0);
}

/* (6.) */
.t-start {
  min-height: 100vh;
  overflow: hidden;
}
  1. *-enter-active*-leave-active 激活状态使用 absolute 是为了让组件不影响 body 的结构,从而不会产生滚动条,渲染抖动等。
  2. *-enter-active 覆盖延迟时间是为了让体感上有个比较明显的进出变化,当然不覆盖延迟也可以,主要看需求和选择。
  3. *-enter-to 不管左进还是右进,结束时状态一定是表示最终状态,这里是为了不影响元素的定位问题,所以选择了使用 translate3d
  4. *-enter-from 让元素的起始位进行 x 轴偏移 100% 即一个页面的宽度。
  5. *-leave-to 让元素的结束位进行 x 轴偏移 100% 即一个页面的宽度。与 4. *-enter-from 偏移向相反即可。
  6. .t-start 这里定义了一个样式也是为了处理父级产生滚动条,渲染抖动等情况。

4. 解决父级元素产生滚动条,渲染抖动等情况

文档:Transition | JavaScript 钩子

为了解决这个情况那么会用到以下事件:

  • @before-enter 在元素被插入到 DOM 之前被调用,用这个来设置元素的 "enter-from" 状态
  • @after-enter 当元素进入过渡完成时被调用
<RouterView v-slot="{ Component, route }">
  <Transition :name="route.meta.transitionName" @before-enter="onBeforeEnter" @after-enter="onAfterEnter">
    <component :is="Component" />
  </Transition>
</RouterView>
const onBeforeEnter = () => {
  document.querySelector('#app')?.classList.toggle('t-start', true)
}
const onAfterEnter = () => {
  document.querySelector('#app')?.classList.toggle('t-start', false)
}

用到这个主要的情况是在执行过度动画中,由于切换不同周期的 Transition-class 导致的。

那么需要在 Transition 上新增 @before-enter@after-enter 事件,为父级添加一个 高度、隐藏滚动条 的样式即可解决。

这个问题在不同布局样式中不一定会出现,主要还是取决于如何定义布局样式的行为。 以 html>body>#app 布局结构在如下定义时不会出现此问题。

html,
body,
#app {
  width: 100%;
  height: 100%;
  overflow: hidden;
}

版权声明: 本文版权属于作者 林小帅,未经授权不得转载及二次修改。 转载或合作请在下方留言及联系方式。