Skip to content

Commit b7d9e90

Browse files
committed
feat: Support route transition animation (#57)
1 parent 1bf881c commit b7d9e90

File tree

13 files changed

+165
-65
lines changed

13 files changed

+165
-65
lines changed

src/App.vue

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import type { ConfigProviderTheme } from 'vant'
33
import { localStorage } from '@/utils/local-storage'
44
import { useStore } from '@/stores'
5+
import { useRouteTransitionNameHook } from '@/stores/modules/routeTransitionName'
56
67
const store = useStore()
78
const theme = ref<ConfigProviderTheme>('light')
@@ -25,6 +26,23 @@ provide('isRealDark', computed(() => theme.value === 'dark'))
2526

2627
<template>
2728
<VanConfigProvider :theme="theme">
28-
<RouterView />
29+
<router-view v-slot="{ Component, route }">
30+
<transition :name="useRouteTransitionNameHook().routeTransitionName" appear>
31+
<div :key="route.name" class="app-wrapper">
32+
<component :is="Component" v-if="Component" />
33+
</div>
34+
</transition>
35+
</router-view>
2936
</VanConfigProvider>
3037
</template>
38+
39+
<style scoped>
40+
.app-wrapper {
41+
position: absolute;
42+
top: 0;
43+
left: 0;
44+
width: 100%;
45+
height: 100%;
46+
overflow-y: auto;
47+
}
48+
</style>

src/app.less

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#app {
2-
height: 100%;
2+
min-height: 100vh;
3+
position: relative;
34
overflow-x: hidden;
45
}
56

@@ -9,6 +10,25 @@
910
box-sizing: border-box;
1011
}
1112

13+
.slide-fadein-left-enter-active,
14+
.slide-fadein-left-leave-active,
15+
.slide-fadein-right-enter-active,
16+
.slide-fadein-right-leave-active {
17+
transition: opacity 0.3s, transform 0.4s, -webkit-transform 0.4s;
18+
}
19+
20+
.slide-fadein-left-enter-from,
21+
.slide-fadein-right-leave-to {
22+
transform: translateX(20px);
23+
opacity: 0;
24+
}
25+
26+
.slide-fadein-left-leave-to,
27+
.slide-fadein-right-enter-from {
28+
transform: translateX(-20px);
29+
opacity: 0;
30+
}
31+
1232
[data-theme='dark'] {
1333
&,
1434
* {

src/assets/logo.png

-13.5 KB
Binary file not shown.

src/router/README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
# File-based Routing
1+
# `File-based Routing`
22

33
Routes will be auto-generated for Vue files in the **src/views** dir with the same file structure.
44
Check out [`unplugin-vue-router`](https://github.com/posva/unplugin-vue-router) for more details.
5+
6+
**src/views** 目录下的 Vue 文件会自动生成相同结构的路由。
7+
8+
查看[' unplugin-vue-router '](https://github.com/posva/unplugin-vue-router)了解更多细节。

src/router/index.ts

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,31 @@
1-
// https://router.vuejs.org/zh/
2-
31
import { createRouter, createWebHistory } from 'vue-router/auto'
42
import NProgress from 'nprogress'
3+
4+
import { useRouteTransitionNameHook } from '@/stores/modules/routeTransitionName'
55
import 'nprogress/nprogress.css'
66

77
NProgress.configure({ showSpinner: true, parent: '#app' })
88
const router = createRouter({
99
history: createWebHistory(import.meta.env.VITE_APP_PUBLIC_PATH),
1010
})
1111

12-
router.beforeEach((_to, _from, next) => {
13-
NProgress.start() // start progress bar
12+
router.beforeEach((to, from, next) => {
13+
NProgress.start()
14+
const hook = useRouteTransitionNameHook()
15+
if (to.meta.level > from.meta.level)
16+
hook.setName('slide-fadein-left') // 进入动画
17+
18+
else if (to.meta.level < from.meta.level)
19+
hook.setName('slide-fadein-right') // 返回动画
20+
21+
else
22+
hook.setName('') // 没有动画
23+
1424
next()
1525
})
1626

1727
router.afterEach(() => {
18-
NProgress.done() // finish progress bar
28+
NProgress.done()
1929
})
2030

2131
// 导出路由实例,并在 `main.ts` 挂载

src/stores/index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { defineStore } from 'pinia'
1+
import { createPinia, defineStore } from 'pinia'
22

33
export const useStore = defineStore({
44
id: 'index',
@@ -8,3 +8,6 @@ export const useStore = defineStore({
88
mode: '',
99
}),
1010
})
11+
12+
const store = createPinia()
13+
export { store }
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { defineStore } from 'pinia'
2+
import { store } from '@/stores'
3+
4+
export const useRouteTransitionNameStore = defineStore({
5+
id: 'route-transition-name',
6+
state: () => ({
7+
// 过渡动画名称
8+
routeTransitionName: '',
9+
}),
10+
actions: {
11+
setName(name: string) {
12+
this.routeTransitionName = name
13+
},
14+
},
15+
})
16+
17+
export function useRouteTransitionNameHook() {
18+
return useRouteTransitionNameStore(store)
19+
}

src/typed-router.d.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,10 @@ import type {
3939

4040
declare module 'vue-router/auto/routes' {
4141
export interface RouteNamedMap {
42-
'/': RouteRecordInfo<'/', '/', Record<never, never>, Record<never, never>>,
43-
'/charts/': RouteRecordInfo<'/charts/', '/charts', Record<never, never>, Record<never, never>>,
44-
'/mock/': RouteRecordInfo<'/mock/', '/mock', Record<never, never>, Record<never, never>>,
45-
'/unocss/': RouteRecordInfo<'/unocss/', '/unocss', Record<never, never>, Record<never, never>>,
42+
'index': RouteRecordInfo<'index', '/', Record<never, never>, Record<never, never>>,
43+
'charts': RouteRecordInfo<'charts', '/charts', Record<never, never>, Record<never, never>>,
44+
'mock': RouteRecordInfo<'mock', '/mock', Record<never, never>, Record<never, never>>,
45+
'unocss': RouteRecordInfo<'unocss', '/unocss', Record<never, never>, Record<never, never>>,
4646
}
4747
}
4848

src/views/README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# `definePage`
2+
3+
We used macro [`definePage()`](https://github.com/posva/unplugin-vue-router?tab=readme-ov-file#extending-existing-routes) to define the route name and meta information for each page, making it easy to control the transition animations for each route.
4+
5+
我们使用 宏 [`definePage()`](https://github.com/posva/unplugin-vue-router?tab=readme-ov-file#extending-existing-routes) 定义每个页面的路由名称和元信息,可以轻松控制每个路由的过渡动画。

src/views/charts/index.vue

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
11
<script setup lang="ts">
2+
definePage({
3+
name: 'charts',
4+
meta: {
5+
level: 2,
6+
},
7+
})
8+
29
const barOption = {
310
title: {},
411
tooltip: {},
@@ -61,19 +68,21 @@ const onClickLeft = () => history.back()
6168
</script>
6269

6370
<template>
64-
<VanNavBar title="📊 Echarts" left-arrow fixed @click-left="onClickLeft" />
71+
<div>
72+
<VanNavBar title="📊 Echarts" left-arrow fixed @click-left="onClickLeft" />
6573

66-
<div class="container">
67-
<div class="chart">
68-
<Chart :option="refBarOption" :style="{ height: '330px' }" />
69-
</div>
74+
<div class="container">
75+
<div class="chart">
76+
<Chart :option="refBarOption" :style="{ height: '330px' }" />
77+
</div>
7078

71-
<div class="chart item">
72-
<Chart :option="refLineOption" :style="{ height: '330px' }" />
73-
</div>
79+
<div class="chart item">
80+
<Chart :option="refLineOption" :style="{ height: '330px' }" />
81+
</div>
7482

75-
<div class="chart item">
76-
<Chart :option="refScoreOption" :style="{ height: '330px' }" />
83+
<div class="chart item">
84+
<Chart :option="refScoreOption" :style="{ height: '330px' }" />
85+
</div>
7786
</div>
7887
</div>
7988
</template>
@@ -83,7 +92,6 @@ const onClickLeft = () => history.back()
8392
width: 100%;
8493
height: 100%;
8594
padding: 60px 16px;
86-
position: relative;
8795
}
8896
8997
.chart {

0 commit comments

Comments
 (0)