返回页面动画错乱的问题。

来源:16-8 通用组件:虚拟任务栈处理

城北丶

2023-07-12

点击我的页面返回按钮时首页的动画并没有添加back动画,而依然是push动画。即使把老师的transition-router-view组件代码全部复制到项目中也会有这个问题。所以我想问下老师这个问题该如何解决?如果不加keep-alive的话,动画是正常的。
图片描述

代码如下:

<template>
  <!-- 
    这个组件用于页面切换时的过渡动画
    router-view 是路由的出口,用于渲染路由的内容,v-slot="{ Component }"是插槽语法,用于获取当前路由组件
    transition 是Vue内置的过渡组件,name属性用于指定过渡动画的名称
    keep-alive 是Vue内置的缓存组件,用于缓存组件,避免组件被销毁
    component 是Vue内置的动态组件,用于渲染组件
   -->
  <router-view v-slot="{ Component }">
    <!-- 
      @before-enter="beforeEnter"是动画的钩子函数,用于在动画开始前执行
      @after-leave="afterLeave"是动画的钩子函数,用于在动画结束后执行
      -->
    <transition
      :name="transitionName"
      @before-enter="beforeEnter"
      @after-leave="afterLeave"
    >
      <!-- 
          isAnimation:当动画开始执行给当前组件设置样式,这样可以避免两个组件切换的时候被顶下去的问题
          includes:匹配需要缓存的组件名称,如果不匹配则不缓存
         -->
      <keep-alive :include="virtualTaskStack">
        <component
          :is="Component"
          :class="{
            'fixed top-0 left-0 bottom-0 w-screen h-screen overflow-hidden z-50':
              isAnimation
          }"
          :key="$route.fullPath"
        />
      </keep-alive>
    </transition>
  </router-view>
</template>
<script>
// 不使用动画
const NONE = 'none'
// 进入动画
const PUSH = 'push'
// 离开动画
const BACK = 'back'
// 路由动画的类型(枚举)
const ROUTER_TYPE_ENUM = [NONE, PUSH, BACK]
</script>
<script setup>
const props = defineProps({
  // 路由的跳转动画类型,PUSH 表示进入,BACK 表示离开
  routerType: {
    type: String,
    default: NONE, // 默认不使用动画
    validator: (value) => {
      const result = ROUTER_TYPE_ENUM.includes(value)
      if (!result) {
        throw new Error(
          `你的 routerType 必须是 ${ROUTER_TYPE_ENUM.join('、')} 中的一个`
        )
      }
      return result
    }
  },
  // 首页的组件名称,用于判断是否是首页
  mainComponentName: {
    type: String,
    required: true
  }
})

// 任务栈,存放需要缓存的组件名称,默认只有首页
const virtualTaskStack = ref([props.mainComponentName])

const router = useRouter()
// 跳转动画的名称
const transitionName = ref('')
// 监听路由的前置守卫,每次路由跳转前都会执行
router.beforeEach((to, from) => {
  // 设置当前路由的过度动画名称
  transitionName.value = props.routerType

  // 如果是进入页面,则将页面名称入栈,进行缓存,这样可以让页面之间切换时不会被销毁
  if (props.routerType === PUSH) {
    // 入栈
    virtualTaskStack.value.push(to.name)
  } else if (props.routerType === BACK) {
    // 出栈
    virtualTaskStack.value.pop()
  }
  // 进入首页默认清空栈
  if (to.name === props.mainComponentName) {
    clearTask()
  }
})

// 当前动画是否执行完毕,false为执行完毕
const isAnimation = ref(false)
// 动画开始前执行的函数
const beforeEnter = () => {
  // 动画开始前,将动画状态设置为true,表示动画正在执行
  isAnimation.value = true
}
// 动画结束后执行的函数
const afterLeave = () => {
  // 动画结束后,将动画状态设置为false,表示动画已经执行完毕
  isAnimation.value = false
  transitionName.value = ''
}

/**
 * 清空栈
 */
const clearTask = () => {
  virtualTaskStack.value = [props.mainComponentName]
}
</script>
<style lang="scss" scoped>
// push页面时:新页面的进入动画
.push-enter-active {
  animation-name: push-in;
  animation-duration: 10s;
}
// push页面时:老页面的退出动画
.push-leave-active {
  animation-name: push-out;
  animation-duration: 10s;
}
// push页面时:新页面的进入动画
@keyframes push-in {
  0% {
    transform: translate(100%, 0);
  }
  100% {
    transform: translate(0, 0);
  }
}
// push页面时:老页面的退出动画
@keyframes push-out {
  0% {
    transform: translate(0, 0);
  }
  100% {
    transform: translate(-50%, 0);
  }
}

// 后退页面时:即将展示的页面动画
.back-enter-active {
  animation-name: back-in;
  animation-duration: 10s;
}
// 后退页面时:后退的页面执行的动画
.back-leave-active {
  animation-name: back-out;
  animation-duration: 10s;
}
// 后退页面时:即将展示的页面动画
@keyframes back-in {
  0% {
    transform: translate(-100%, 0);
  }
  100% {
    transform: translate(0, 0);
  }
}
// 后退页面时:后退的页面执行的动画
@keyframes back-out {
  0% {
    transform: translate(0, 0);
  }
  100% {
    transform: translate(50%, 0);
  }
}
</style>

写回答

1回答

Sunday

2023-07-12

你好

transition-router-view 这个组件的话,需要保证内部的组件都有正确的入栈(push)和出栈(pop)行为才可以的。

0
1
城北丶
我找见问题了。是因为首页index.vue组件根节点不能有同级注释。去掉注释就好了。
2023-07-12
共1条回复

基于 Vue3 ,打造前台+中台通用开发提效解决方案

42 种前台常见业务模型, 15 种中台通用组件,成为前端提效高手

788 学习 · 517 问题

查看课程