阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > 路由过渡动画变化

路由过渡动画变化

作者:陈川 阅读数:59283人阅读 分类: Vue.js

路由过渡动画变化

路由过渡动画是Vue.js应用中提升用户体验的重要手段。通过合理的动画设计,可以让页面切换更加自然流畅,引导用户注意力,同时保持应用的连贯性。Vue Router与Vue的过渡系统完美结合,提供了多种实现路由过渡的方式。

基础路由过渡实现

最简单的路由过渡可以通过Vue的<transition>组件实现。当路由切换时,Vue会自动为旧组件和新组件添加进入/离开的过渡类名:

<router-view v-slot="{ Component }">
  <transition name="fade" mode="out-in">
    <component :is="Component" />
  </transition>
</router-view>

对应的CSS样式:

.fade-enter-active,
.fade-leave-active {
  transition: opacity 0.5s ease;
}

.fade-enter-from,
.fade-leave-to {
  opacity: 0;
}

这种基础过渡适合大多数简单场景,通过调整transition属性可以控制动画的持续时间、缓动函数等参数。

动态过渡效果

根据不同的路由切换方向,可以应用不同的过渡效果。这需要结合Vue Router的导航守卫和组件的状态:

// router.js
const router = createRouter({
  history: createWebHistory(),
  routes: [...],
})

router.beforeEach((to, from) => {
  const toDepth = to.meta.depth || 0
  const fromDepth = from.meta.depth || 0
  to.meta.transitionName = toDepth < fromDepth ? 'slide-right' : 'slide-left'
})

然后在组件中使用动态过渡名称:

<router-view v-slot="{ Component, route }">
  <transition :name="route.meta.transitionName || 'fade'">
    <component :is="Component" />
  </transition>
</router-view>

对应的CSS:

.slide-left-enter-active,
.slide-left-leave-active,
.slide-right-enter-active,
.slide-right-leave-active {
  transition: all 0.5s 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%);
}

基于路由元信息的过渡控制

通过路由的meta字段可以更精细地控制每个路由的过渡行为:

const routes = [
  {
    path: '/',
    component: Home,
    meta: {
      transition: 'zoom'
    }
  },
  {
    path: '/about',
    component: About,
    meta: {
      transition: 'fade'
    }
  }
]

组件中使用:

<router-view v-slot="{ Component, route }">
  <transition :name="route.meta.transition || 'fade'">
    <component :is="Component" />
  </transition>
</router-view>

组合过渡与动画

Vue的过渡系统支持同时使用CSS过渡和动画,这在需要更复杂效果时非常有用:

<transition 
  name="bounce"
  enter-active-class="animate__animated animate__bounceIn"
  leave-active-class="animate__animated animate__bounceOut"
>
  <router-view />
</transition>

这里使用了Animate.css库,但也可以自定义动画:

@keyframes slideIn {
  from { transform: translateY(20px); opacity: 0; }
  to { transform: translateY(0); opacity: 1; }
}

@keyframes slideOut {
  from { transform: translateY(0); opacity: 1; }
  to { transform: translateY(-20px); opacity: 0; }
}

.slide-enter-active {
  animation: slideIn 0.4s forwards;
}

.slide-leave-active {
  animation: slideOut 0.4s forwards;
}

列表与路由的复合过渡

当路由切换涉及列表项变化时,可以结合<transition-group>实现更丰富的效果:

<router-view v-slot="{ Component }">
  <transition name="page" mode="out-in">
    <component :is="Component" />
  </transition>
</router-view>

在子组件内部:

<transition-group name="list" tag="ul">
  <li v-for="item in items" :key="item.id">
    {{ item.text }}
  </li>
</transition-group>

对应的样式:

.page-enter-active, .page-leave-active {
  transition: all 0.3s;
}
.page-enter-from, .page-leave-to {
  opacity: 0;
  transform: scale(0.95);
}

.list-move,
.list-enter-active,
.list-leave-active {
  transition: all 0.5s ease;
}

.list-enter-from,
.list-leave-to {
  opacity: 0;
  transform: translateX(30px);
}

.list-leave-active {
  position: absolute;
}

JavaScript钩子实现复杂过渡

对于需要程序控制的复杂过渡,可以使用JavaScript钩子:

<router-view v-slot="{ Component }">
  <transition
    :css="false"
    @before-enter="beforeEnter"
    @enter="enter"
    @leave="leave"
  >
    <component :is="Component" />
  </transition>
</router-view>

JavaScript部分:

methods: {
  beforeEnter(el) {
    el.style.opacity = 0
    el.style.transform = 'scale(0.9)'
  },
  enter(el, done) {
    gsap.to(el, {
      opacity: 1,
      scale: 1,
      duration: 0.5,
      ease: 'power2.out',
      onComplete: done
    })
  },
  leave(el, done) {
    gsap.to(el, {
      opacity: 0,
      scale: 1.1,
      duration: 0.3,
      ease: 'power2.in',
      onComplete: done
    })
  }
}

这里使用了GSAP库来实现更精细的动画控制,但也可以使用原生Web Animations API。

路由过渡的性能优化

复杂的路由过渡可能影响应用性能,需要注意以下几点:

  1. 尽量使用CSS硬件加速:
.transform-layer {
  will-change: transform;
  backface-visibility: hidden;
  perspective: 1000px;
}
  1. 避免同时动画过多元素,可以使用appear属性控制初始渲染动画:
<transition appear @before-appear="customBeforeAppear">
  <!-- 内容 -->
</transition>
  1. 对于复杂场景,考虑使用<keep-alive>缓存组件状态:
<router-view v-slot="{ Component }">
  <transition name="fade" mode="out-in">
    <keep-alive>
      <component :is="Component" />
    </keep-alive>
  </transition>
</router-view>

嵌套路由的过渡处理

嵌套路由需要特殊处理过渡效果,以确保父子路由的动画协调:

<router-view v-slot="{ Component, route }">
  <transition :name="route.meta.transition || 'fade'">
    <component :is="Component" />
  </transition>
</router-view>

在子路由组件中:

<router-view v-slot="{ Component }" class="child-view">
  <transition name="slide-up">
    <component :is="Component" />
  </transition>
</router-view>

对应的样式需要确保层级关系正确:

.child-view {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
}

.slide-up-enter-active,
.slide-up-leave-active {
  transition: all 0.3s ease-out;
}

.slide-up-enter-from {
  transform: translateY(20px);
  opacity: 0;
}

.slide-up-leave-to {
  transform: translateY(-20px);
  opacity: 0;
}

路由过渡与状态管理结合

将过渡状态存储在Vuex或Pinia中可以实现全局统一的过渡控制:

// store/transition.js
export const useTransitionStore = defineStore('transition', {
  state: () => ({
    type: 'fade',
    duration: 300
  }),
  actions: {
    setTransition(payload) {
      this.type = payload.type
      this.duration = payload.duration
    }
  }
})

组件中使用:

<script setup>
import { useTransitionStore } from '@/stores/transition'
const transition = useTransitionStore()
</script>

<template>
  <router-view v-slot="{ Component }">
    <transition 
      :name="transition.type"
      :duration="transition.duration"
      mode="out-in"
    >
      <component :is="Component" />
    </transition>
  </router-view>
</template>

响应式路由过渡

根据视口大小或用户偏好动态调整过渡效果:

// 检测用户偏好
const prefersReducedMotion = window.matchMedia(
  '(prefers-reduced-motion: reduce)'
).matches

// 在组件中
const transitionName = ref(prefersReducedMotion ? 'fade' : 'slide')

watch(() => route.path, () => {
  if (prefersReducedMotion) return
  
  // 根据路由变化动态调整过渡
  if (route.path === '/about') {
    transitionName.value = 'zoom'
  } else {
    transitionName.value = 'slide'
  }
})

高级路由过渡模式

对于需要更复杂控制的场景,可以结合Vue的渲染函数和自定义指令:

app.directive('route-transition', {
  mounted(el, binding) {
    const { value } = binding
    el.style.transition = `all ${value.duration}ms ${value.easing}`
  },
  updated(el, binding) {
    const { value, oldValue } = binding
    if (value.active !== oldValue.active) {
      if (value.active) {
        el.classList.add('enter-active')
        el.classList.remove('leave-active')
      } else {
        el.classList.add('leave-active')
        el.classList.remove('enter-active')
      }
    }
  }
})

使用自定义过渡组件:

const RouteTransition = {
  functional: true,
  render(h, { children }) {
    const data = {
      props: {
        name: 'custom-transition',
        mode: 'out-in'
      },
      on: {
        beforeEnter(el) {
          // 自定义逻辑
        },
        afterEnter(el) {
          // 自定义逻辑
        }
      }
    }
    return h('transition', data, children)
  }
}

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌