e
ec50n9
V1
2023/01/24阅读:28主题:嫩青
vue-router + gsap 跨组件动画实现
假设有/foo
和/bar
两个页面,两个页面里面都有一个div.me
元素,它们两者是独立的,大概长这样:

从/foo
跳转到/bar
的过程分为如下几步:

-
从 /foo
的div.me
中获取关键关键样式 -
在 onBeforeRouteLeave
中把样式键值存到to.query
中 -
跳转到 /bar
-
/bar
页面的onMounted()
中使用useRoute()
获取传过来的样式键值 -
使用 gsap.from
来为/bar
中的div.me
增加变化动画
大概就这个思路,实现起来也很简单:
首先在/foo
页面加上跳转链接和需要变换的组件:
<template>
<div>
<RouterLink to="/bar">go to bar</RouterLink>
<TheImage class="the-img" />
</div>
</template>
接下来是js
部分:
<script setup>
import gsap from 'gsap';
import { reactive, onMounted } from 'vue';
import { onBeforeRouteLeave, useRoute } from 'vue-router';
import TheImage from '../components/TheImage.vue';
const style = reactive({
width: "10rem",
height: "10rem",
color: "#000",
backgroundColor: "#000"
})
// 跳转时判断目的地是不是 foo 页面,如果是的话就把把数据一同传递过去
onBeforeRouteLeave((to) => {
if (to.name === "bar") to.query = { ...style }
})
</script>
这里使用了reactive
来创建一个响应式的sytle
对象,而非普通对象。接下来设置组件的样式:
<style scoped>
.the-img {
width: v-bind('style.width');
height: v-bind('style.height');
color: v-bind('style.color');
background-color: v-bind('style.backgroundColor');
}
</style>
⚠️ 需要注意的是,我这里使用了to.name
来进行判断,前提是路由配置中手动声明了路由的name
,比如我这里就是:
const routes = [
{
path: "/",
component: ()=>import("../App.vue"),
children: [
{
// 这里👇
name: "foo",
path: "foo",
component: ()=>import("../pages/Foo.vue")
},
{
// 还有这里👇
name: "bar",
path: "bar",
component: ()=>import("../pages/Bar.vue")
}
],
},
];
接下来就是/bar
页面,html
和css
部分和前面的一样,js
代码如下:
<script setup>
import gsap from 'gsap';
import { reactive, onMounted } from 'vue';
import { onBeforeRouteLeave, useRoute } from 'vue-router';
import TheImage from '../components/TheImage.vue';
const style = reactive({
width: "10rem",
height: "10rem",
color: "#000",
backgroundColor: "#000"
})
// 页面加载好之后,如果有传进来参数,就使用 gsap 来进行动画过渡
onMounted(() => {
const route = useRoute()
route.query && gsap.from(style, route.query)
})
</script>
这样就完成一个动画跳转了,但是还需要返回,我希望返回的时候动画也会逆转过来运行。其实也很简单,在/foo
页面加上onMounted()
那些逻辑,并且在/bar
页面加上onBeforeRouteLeave()
的逻辑即可:
// foo.vue
import gsap from 'gsap';
import { reactive, onMounted } from 'vue';
import { onBeforeRouteLeave, useRoute } from 'vue-router';
import TheImage from '../components/TheImage.vue';
const style = reactive({
width: "10rem",
height: "10rem",
color: "#000",
backgroundColor: "#000"
})
onMounted(() => {
const route = useRoute()
route.query && gsap.from(style, route.query)
})
onBeforeRouteLeave((to) => {
if (to.name === "bar") to.query = { ...style }
})
// bar.vue
import gsap from 'gsap';
import { reactive, onMounted } from 'vue';
import { onBeforeRouteLeave, useRoute } from 'vue-router';
import TheImage from '../components/TheImage.vue';
const style = reactive({
width: "10rem",
height: "10rem",
color: "#000",
backgroundColor: "#000"
})
onMounted(() => {
const route = useRoute()
route.query && gsap.from(style, route.query)
})
onBeforeRouteLeave((to) => {
if (to.name === "foo") to.query = { ...style }
})
稍微观察一下就可以发现,其实两份代码只有倒数第二行的to.name === "..."
不一样而已。
总结
这种方式实现跨组件动画有比较明显的缺点,就是两个组件为独立的组件。
因为这里只是人为的使它们看起来像而已。如果这个组件内部有状态,页面跳转后得手动传这些状态进去手动加上去,这是很不方便的。
以上。
作者介绍
e
ec50n9
V1