高阶路由过渡处理方案 —— 浏览器堆栈主动介入

目录

01: 前言

02: VueRouter 过渡动效可行性分析

03: 主动介入浏览器堆栈管理,分析可行性方案

04: 主动介入浏览器堆栈管理

05: 基于 GSAP 实现高阶路由过渡动画分析 

06: 基于 GSAP 实现高阶路由过渡动画处理

07: 通用组件:navbar 构建方案分析

08: 通用组件:构建 navbar

09: 基于 navbar 处理响应式的 pins 页面

10: 处理刷新空白问题 

11: 总结 


 

01: 前言

        在 vue 中,两个路由进行跳转的时候,会为其增加一些跳转的过渡动画,这是一个非常常见的需求。通常情况下,这种过渡动画,我们可以使用 vue-router 的过渡动效 进行实现。

        对于咱们的项目而言,当我们 item 中点击进入 详情页 的时候,我们也希望可以有一个对应的过渡动效。从而提升用户体验。

我们期望这个过渡动效可以:

        1. 同时在 PC 端 和 移动端 生效。

        2. 进入新页面时:在点击的具体 item 中呈现 由小到大的缩放动画。

        3. 退出新页面时:呈现 由大到小的缩放动画 缩回至点击的具体 item 中。

这样的一个过渡动效,我们应该如何进行实现呢?

直接通过 vue-router 的过渡动效 可以实现吗?

如果不行的话,我们又应该如何去做呢?

02: VueRouter 过渡动效可行性分析

        接下来我们要实现的是:item 到详情页的路由过渡动效。这样的一个过渡动效,我们如何去做呢?通常针对这种功能,我们首先都会想到 vue-router 提供的 过渡动效。 

        想要判断这个问题,我们首先需要搞明白 vue-router 的过渡动效的过渡机制是什么?然后再根据这个机制来分析可行性。

<!-- 使用动态过渡名称 -->
<router-view v-slot="{ Component, route }"><transition :name="route.meta.transition"><component :is="Component" /></transition>
</router-view>

以上这段代码是实现过渡动效的关键代码。

其中涉及到了三个组件:

1. router-view:路由出口

2. transition:动画组件

3. component:动态组件

据此可以得知,vue-router 过渡动效产生的关键就是 transition 动画组件。

同时我们知道对于 transition 而言,它能够产生动画的关键,其实是其内部元素 component 的动态组件切换。

但是这样的切换,它一定是基于整个 页面组件的。也就是说如果我们利用这种过渡动效,一定是:从 home 页面到 detail 页面 的整体页面组件的切换。

所以:是 无法 实现咱们期望的这种路由过渡效果的。 

03: 主动介入浏览器堆栈管理,分析可行性方案

        根据上一小节的分析,我们知道通过 vue-router 的过渡动效是无法实现咱们期望的路由切换效果的。那么应该如何去做?

        想要搞明白咱们的可行性方案,首先得先搞清楚 什么是路由跳转?所谓路由的跳转无非指的是两部分:

        1. 浏览器的 url 发生了变化。

        2. 浏览器中展示的页面组件发生了变化。

只要满足这两点,我们就认为 路径进行了跳转

        所以说,我们可不可以换个思路,我们 不去进行真实的路由跳转,而是 先修改浏览器的 URL,再切换展示的页面(以组件的形式覆盖整个浏览器可视区域)。这样对于用户而言,是不是就完成了整个路由的跳转工作。

这样我们的具体问题就变成了:

        1. 如何让浏览器的 url 发生变化,但是不跳转页面。

        2. 如何让一个新的组件以包含动画的形式进行展示。

想要完成第一个功能我们可以使用:History.pushState() 方法。而第二个功能我们可以使用  这个  GSAP 动画库进行实现。

04: 主动介入浏览器堆栈管理

// src/views/main/components/list/item.vue<script setup>
const emits = defineEmits(['click'])/** 进入详情页点击事件*/
const onToPinsClick = () => {emits('click', {id: props.data.id})
}
</script>
// src/views/main/components/list/index.vue<template><itemVue @click="onToPins" />
</template><script setup>
/** 进入 pins*/
const onToPins = (item) => {history.pushState(null, null, `/pins/${item.id}`)
}
</script>

05: 基于 GSAP 实现高阶路由过渡动画分析 

当 url 发生变化之后,我们接下来就只需要处理对应的动画就可以了。

动画的处理我们依赖于 GSAP 进行实现。对于 GSAP 而言,主要依赖两个方法:

        1. gsap.set(): 这个方法通常使用在动画开始之前,表示设置动画开始前的元素属性。

        2. gsap.to(): 这个方法表示 最终元素展示的状态

GSAP 会基于 set 和 to 的状态,来自动执行中间的补间动画。

所以我们只需要:

        1. 创建一个对应的组件,使用 transition 进行包裹。

        2. 计算出 set 时,组件元素对应的样式属性。

        3. 计算出 to 时,组件元素对应的样式属性。

然后就可以由 GSAP 自动实现对应的补间动画了。

06: 基于 GSAP 实现高阶路由过渡动画处理

- src/views
- - pins
- - - components
- - - - pins.vue
- - - index.vue
npm install --save gsap@3.9.1
// src/views/main/components/list/index.vue<template><!-- 大图详情处理 --><transition:css="false"@before-enter="beforeEnter"@enter="enter"@leave="leave"><pins-vue v-if="isVisiblePins" :id="currentPins.id" /></transition>
</template>
<script setup>
// 控制 pins 展示
const isVisiblePins = ref(false)
// 当前选中的 pins 属性
const currentPins = ref({})
/*** 进入 pins*/
const onToPins = (item) => {history.pushState(null, null, `/pins/${item.id}`)currentPins.value = itemisVisiblePins.value = true
}/*** 监听浏览器后退按钮事件*/
useEventListener(window, 'popstate', () => {isVisiblePins.value = false
})/*** 进入动画开始前*/
const beforeEnter = (el) => {gsap.set(el, {scaleX: 0,scaleY: 0,transformOrigin: '0 0',translateX: currentPins.value.localtion?.translateX,translateY: currentPins.value.localtion?.translateY,opacity: 0})
}
/*** 进入动画执行中*/
const enter = (el, done) => {gsap.to(el, {duration: 0.3,scaleX: 1,scaleY: 1,opacity: 1,translateX: 0,translateY: 0,onComplete: done})
}
/*** 离开动画执行中*/
const leave = (el, done) => {gsap.to(el, {duration: 0.3,scaleX: 0,scaleY: 0,x: currentPins.value.localtion?.translateX,y: currentPins.value.localtion?.translateY,opacity: 0})
}
</script>
// src/views/main/components/list/item.vue
<template><div @click="onToPinsClick" />
</template>
<script setup>
/*** 查看 vueuse 的源代码*(https://github.com/vueuse/vueuse/blob/main/packages/core/useElementBounding/index.ts)* 发现 useElementBounding 方法是仅在 window 的 scroll 时被触发,* 所以在移动端状态下会导致 useElementBounding 的返回值不再具备响应性。从而计算失败。* 所以我们可以修改 imgContainerCenter 为一个方法,* 利用 el.getBoundingClientRect 方法获取动态的 x、y、width、height , 从而进行正确的计算。*/
const imgContainerCenter = () => {const {x: imgContainerX,y: imgContainerY,width: imgContainerWidth,height: imgContainerHeight} = imgTarget.value.getBoundingClientRect()return {translateX: parseInt(imgContainerX + imgContainerWidth / 2),translateY: parseInt(imgContainerY + imgContainerHeight / 2)}
}
/*** 进入详情点击事件*/
const onToPinsClick = () => {emits('click', {id: props.data.id,localtion: imgContainerCenter()})
}
</script>

知识点讲解:

        你可以通过监听 <Transition> 组件事件的方式在过渡过程中挂上钩子函数。

        这些钩子可以与 CSS 过渡或动画结合使用,也可以单独使用。

        在使用仅由 JavaScript 执行的动画时,最好是添加一个 :css="false" prop。这显式地向 Vue 表明可以跳过对 CSS 过渡的自动探测。除了性能稍好一些之外,还可以防止 CSS 规则意外地干扰过渡效果。

        在有了 :css="false" 后,我们就自己全权负责控制什么时候过渡结束了。这种情况下对于 @enter 和 @leave 钩子来说,回调函数 done 就是必须的。否则,钩子将被同步调用,过渡将立即完成。

07: 通用组件:navbar 构建方案分析

接下来我们就需要处理 pins 中对应的页面样式了。

        pins 的页面样式同时可以应用到 pc端 和 移动端。而在 移动端 中,则会展示对应的 navbar 的内容,所以我们首先构建出 navbar 通用组件,然后基于 navbar 构建对应的 pins 样式。

对于 navbar 而言:

        1. 它分为 左、中、右 三个大的部分,三个部分都可以通过插槽进行指定。

        2. 左、右 两边的插槽可以自定义点击事件。

        3. 同时 navbar 有时候会存在吸顶效果,所以我们最好还可以通过一个 prop 指定对应的吸顶展示。

分析完成之后,接下来实现对应的 navbar 构建。 

08: 通用组件:构建 navbar

- src/libs
- - navbar
- - - index.vue
// src/libs/navbar/index.vue<template><divclass="w-full h-5 border-b flex items-center z-10 bg-white dark:bg-zinc-800 border-b-zinc-200 dark:border-b-zinc-700":class="[sticky ? 'sticky top-0 left-0' : 'relative']"><!-- 左 --><divclass="h-full w-5 absolute left-0 flex items-center justify-center"@click="onClickLeft"><slot name="left"><m-svg-iconname="back"class="w-2 h-2"fillClass="fill-zinc-900 dark:fill-zinc-200"/></slot></div><!-- 中 --><divclass="h-full flex items-center justify-center m-auto font-bold text-base text-zinc-900 dark:text-zinc-200"><slot></slot></div><!-- 右 --><divclass="h-full w-5 absolute right-0 flex items-center justify-center"@click="onClickRight"><slot name="right" /></div></div>
</template>
<script setup>
import { useRouter } from 'vue-router'const props = defineProps({clickLeft: {type: Function},clickRight: {type: Function},sticky: {type: Boolean}
})
const router = useRouter()
/*** 左侧按钮点击事件*/
const onClickLeft = () => {if (props.clickLeft) {props.clickLeft()return}router.back()
}/*** 右侧按钮点击事件*/
const onClickRight = () => {if (props.clickRight) {props.clickRight()}
}
</script><style lang="scss" scoped></style>

09: 基于 navbar 处理响应式的 pins 页面

// src/views/pins/components/pins.vue<template><divclass="fixed left-0 top-0 w-screen h-screen z-20 backdrop-blur-4xl bg-white dark:bg-zinc-800 pb-2 overflow-y-auto xl:p-2 xl:bg-transparent"><!-- 移动端下展示 navbar --><m-navbarv-if="isMobileTerminal"sticky@clickLeft="onPop"@clickRight="onPop">{{ pexelData.title }}<template #right><m-svg-iconname="share"class="w-3 h-3"fillClass="fill-zinc-900 dark:fill-zinc-200"></m-svg-icon></template></m-navbar><!-- pc 端下展示关闭图标 --><m-svg-iconv-elsename="close"class="w-3 h-3 ml-1 p-0.5 cursor-pointer duration-200 rounded-sm hover:bg-zinc-100 absolute right-2 top-2"fillClass="fill-zinc-400"@click="onPop"></m-svg-icon><div class="xl:w-[80%] xl:h-full xl:mx-auto xl:rounded-lg xl:flex"><imgclass="w-screen mb-2 xl:w-3/5 xl:h-full xl:rounded-tl-lg xl:rounded-bl-lg":src="pexelData.photo"/><divclass="xl:w-2/5 xl:h-full xl:bg-white xl:dark:bg-zinc-900 xl:rounded-tr-lg xl:rounded-br-lg xl:p-3"><div v-if="!isMobileTerminal" class="flex justify-between mb-2"><m-svg-iconname="share"class="w-4 h-4 p-1 cursor-pointer hover:bg-zinc-200 dark:hover:bg-zinc-800 duration-300 rounded"fillClass="fill-zinc-900 dark:fill-zinc-200"></m-svg-icon><m-buttonclass=""type="info"icon="heart"iconClass="fill-zinc-900 dark:fill-zinc-200"/></div><!-- 标题 --><pclass="text-base text-zinc-900 dark:text-zinc-200 ml-1 font-bold xl:text-xl xl:mb-5">{{ pexelData.title }}</p><!-- 作者 --><div class="flex items-center mt-1 px-1"><imgv-lazyclass="h-3 w-3 rounded-full":src="pexelData.avatar"alt=""/><span class="text-base text-zinc-900 dark:text-zinc-200 ml-1">{{pexelData.author}}</span></div></div></div></div>
</template><script setup>
import { ref } from 'vue'
import { getPexelsFromId } from '@/api/pexels'
import { isMobileTerminal } from '@/utils/flexible'
import { useRouter } from 'vue-router'
import { useStore } from 'vuex'const props = defineProps({id: {type: String,required: true}
})const pexelData = ref({})
/*** 获取详情数据*/
const getPexelData = async () => {const data = await getPexelsFromId(props.id)pexelData.value = data
}
getPexelData()/*** 关闭按钮处理事件*/
const router = useRouter()
const store = useStore()
const onPop = () => {// 配置跳转方式store.commit('app/changeRouterType', 'back')router.back()
}
</script><style lang="scss" scoped></style>

10: 处理刷新空白问题 

问题:例如 xx.xxx.xx/pins/5313576 这样的路径刷新浏览器时,会显示空白页面。

原因:项目中该路径未配置路由。

// src/views/pins/index.vue<template><div class="w-full h-full bg-zinc-200 dark:bg-zinc-800"><pins-vue :id="$route.params.id" /></div>
</template><script setup>
import pinsVue from './components/pins.vue'
</script>
// src/router/modules/mobile-routes.jsexport default [{path: '/',name: 'home',component: () => import('@/views/main/index.vue')},{path: '/pins/:id',name: 'pins',component: () => import('@/views/pins/index.vue')}
]
// src/router/modules/pc-routes.jsexport default [{path: '/',name: 'main',component: () => import('@/views/layout/index.vue'),children: [{path: '',name: 'home',component: () => import('@/views/main/index.vue')},{path: '/pins/:id',name: 'pins',component: () => import('@/views/pins/index.vue')}]}
]

11: 总结 

        本篇文章中咱们处理了详情页面,在详情页面的处理中,我们通过另外一种方式完成了 路由的过渡行为。同时也接触到了 GSAP 这样的动画库,可以使我们的动画处理变的更加方便。

        接下来我们将要处理 登录、注册,大家拭目以待吧。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/14845.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Redis基础篇

文章目录 2 Redis入门概述3 Redis10大数据类型3.1 Redis自字符串String3.2 Redis列表List3.3 Redis哈希Hash3.4 Redis集合Set3.5 Redis有序集合Sorted Set3.6 Redis地理空间 GEO3.7 Redis基数统计 HyperLogLog3.8 Redis位图bitmap3.9 Redis位域bitField3.10 Redis流Stream 4 Re…

Elasticsearch 分析器的高级用法一(同义词,高亮搜索)

Elasticsearch 分析器的高级用法一&#xff08;同义词&#xff0c;高亮搜索&#xff09; 同义词简介分析使用同义词案例 高亮搜索高亮搜索策略unifiedplainvh 同义词 简介 在搜索场景中&#xff0c;同义词用来处理不同的查询词&#xff0c;有可能是想表达相同的搜索目标。 例…

基于Go实现的分布式主键系统

基于Go实现的分布式主键系统 摘要 随着互联网的发展&#xff0c;微服务得到了快速的发展&#xff0c;在微服务架构下&#xff0c;分布式主键开始变得越来越重要。目前分布式主键的实现方式颇多&#xff0c;有基于数据库自增的、基于UUID的、基于Redis自增的、基于数据库号段的…

探寻最强性能云电脑:ToDesk云电脑、无影云、网易云游戏、易腾云横测大比拼

大家好&#xff0c;我是herosunly。985院校硕士毕业&#xff0c;现担任算法研究员一职&#xff0c;热衷于机器学习算法研究与应用。曾获得阿里云天池比赛第一名&#xff0c;CCF比赛第二名&#xff0c;科大讯飞比赛第三名。拥有多项发明专利。对机器学习和深度学习拥有自己独到的…

恶劣天候鲁棒三维目标检测论文整理

恶劣天候鲁棒三维目标检测论文整理 Sunshine to Rainstorm: Cross-Weather Knowledge Distillation for Robust 3D Object DetectionRobo3D: Towards Robust and Reliable 3D Perception against CorruptionsLossDistillNet: 3D Object Detection in Point Cloud Under Harsh W…

基于自抗扰控制器和线性误差反馈控制律(ADRC-LSEF)的控制系统simulink建模与仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 4.1 ADRC原理 4.2 线性误差反馈控制律(LSEF) 4.3 ADRC-LSEF融合系统 5.完整工程文件 1.课题概述 基于自抗扰控制器和线性误差反馈控制律(ADRC-LSEF)的控制系统simulink建模与仿真。 2.系统仿真结果 …

测试驱动编程(3)进阶单元测试(下)

文章目录 测试驱动编程(3)进阶单元测试&#xff08;下&#xff09;示例实战接收同事的需求开始迭代需求故事迭代1故事迭代2故事迭代3故事迭代4故事迭代5故事迭代6 测试驱动编程(3)进阶单元测试&#xff08;下&#xff09; 示例实战 接收同事的需求 你的同事正在开发一个远程…

【PROXYCHAINS】Kali Linux 上配置NAT PROXYCHAINS保姆级教程

kali linux配置agent 在博主配置kali 的时候遇到了一些小问题&#xff0c;主要就是连接一直报错超时。 方法一&#xff1a;优点&#xff1a;免费&#xff0c;但是agent很不稳定 搜索免费ip,在Google搜索free proxy list 检查可用ip 连接成功 cd /etcls |grep redsnano reds…

IDEA中一些常见操作【持续更新】

文章目录 前言善用debugidea中debug按钮不显示自动定位文件【始终选择打开的文件】idea注释不顶格【不在行首】快速定位类的位置【找文件非常快】创建文件添加作者及时间信息快速跳转到文件顶端 底端 前言 因为这些操作偶尔操作一次&#xff0c;不用刻意记忆&#xff0c;有个印…

苹果CMS:怎么重新安装

当我们安装好苹果CMS之后苹果cms&#xff1a;介绍及安装&#xff0c;但是最好我们在安装的时候配置好对应配置后&#xff0c;备份一份&#xff0c;如果不记得哪里配置出了问题&#xff0c;出现一些不可预料的问题&#xff0c;那我们可以简单暴力的直接重新安装&#xff0c;我们…

211初试自命题复试线仅302分!延边大学计算机考研考情分析!

延边大学&#xff08;Yanbian University&#xff09;&#xff0c;简称“延大”&#xff0c;地处吉林省延边朝鲜族自治州&#xff0c;是国家“双一流”建设高校、国家“211工程”重点建设大学、西部开发重点建设院校、吉林省人民政府和教育部共同重点支持建设大学、吉林省人民政…

计算机如何将输入文字显示出来的?渲染Image rendering

1.文字渲染的简单理解 渲染图像&#xff0c;可以理解为用cpu/gpu构造出原本不存在的图像。比如输入计算机的英文字符都是ASCII码&#xff0c;而我们在屏幕上看到显示的字符对应的应该是RGB/YUV的像素。计算机把ASCII字符转化成像素的过程就是文字渲染。又比如我们GPU用多个2D图…

DolphinDB 携手九鞅科技,助力固收投研效能飞跃

随着金融市场开放的广度与深度不断拓宽&#xff0c;金融产品呈现出多样化的发展态势&#xff0c;其中债券投资组合凭借其低风险性、高流动性与稳健的收益表现&#xff0c;逐渐成为投资理财领域备受瞩目的焦点。投资经理不仅需要了解哪些债券值得投资&#xff0c;更要对债券投资…

web4.0-元宇宙虚拟现实

互联网一直在不断演变和改变我们的生活方式&#xff0c;从Web逐渐 1.0时代的静态网页到Web 2.0时代的社会性和内容制作&#xff0c;再从Web逐渐 在3.0阶段&#xff0c;互联网发展一直推动着大家时代的发展。如今&#xff0c;大家正站在互联网演化的新起点上&#xff0c;迈入Web…

微信hook协议3.84最新版

PC微信接口使用说明 用户故事 更新日志 登录与退出 获取进程端口占用信息 获取微信进程总数 启动微信 点击登陆微信 刷新并获取登录二维码 获取登录二维码数据(可以不调用) 获取微信登陆状态 退出微信 结束微信 个人信息管理 获取个人详细信息 保存收款码 获取本人地址 修改本人…

如何彻底搞懂装饰器(Decorator)设计模式?

对于任何一个软件系统而言&#xff0c;往现有对象中添加新功能是一种不可避免的实现场景&#xff0c;但这一实现过程对现有系统的影响可大可小。从架构设计上讲&#xff0c;我们也知道存在一个开闭原则&#xff08;Open-Closed Principle&#xff0c;OCP&#xff09;&#xff0…

【简单介绍下7-Zip,什么是7-Zip?】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…

C++语言学习(五)—— 类与对象(一)

目录 一、类类型的定义 二、类成员的访问控制 2.1 什么是"类内"和"类外" 2.2 对于访问控制属性的说明 三、类类型的使用 3.1 进行抽象 3.2 声明类 3.3 实现类 3.4 使用类 四、构造函数的引入 五、析构函数的引入 六、重载构造函数的引入 6.1 …

一文读懂“双随机、一公开”:企业监管新风向

在这个信息透明化、法治化日益增强的时代&#xff0c;政府对企业监管的方式也在不断创新和完善。“双随机、一公开”作为一种新型监管模式&#xff0c;正逐渐成为市场监管领域的关键词。对于企业或个体工商户而言&#xff0c;了解并适应这一监管机制&#xff0c;对于维护自身合…

工具分享:VsCode注释神器,koro1FileHeader

他是有官方Wiki的。 https://github.com/OBKoro1/koro1FileHeader/wiki/ 项目在GitHub上开源。以下摘录部分wiki&#xff0c;用作介绍分享在这里插入代码片 如何找到setting.json设置模板 简单的输入命令 打开VSCode命令面板: mac: command p window: ctrl p输入> Ope…