I'm trying to make a page transition with vue router in Vue 3
This image sums up what I'm trying to do
I'm building a website with different pages (on the image: blue zone) that all have a scroll and a common fixed navigation bar at the top. If you click on one of the links of the topbar, it should move the entire page container to the bottom by 100vh to reveal a special view (on the image: red zone)
My problem
As the yellow bar is fixed, I don't manage to make it leave with the blue page, it stays fixed at y:0
I want to avoid clumsy stuff such as quickly adding on beforeRouteLeave a position:absolute top the navigation bar and compensating the top position based on scroll position.
I don't really manage to make the 2 views be on top of each other, so that the blue page reveals the red page that is already in the right position.
I don't manage to wrap my head around how I should approach this
I've been trying different things for a few days:
Single <router-view> in App.vue that contains the blue views, and the red view. No luck.
Two different <router-view> in App.vue. No luck.
Various levels of wrapper with absolute, overflow hidden, given 100vh values, etc... No luck
My questions
Would you know how I should split the different parts? (app, wrappers, sub-wrappers, croppers, ..)
Can it be done with a single <router-view>?
Can it be done without super clumsy JS on route change?
Thank you for reading
I hope this idea can help you.
Since page is just a component that changed on redirect, you can create a page that contains both of your pages.
For example, according to your pictures, I'll have 2 pages (Red
and Blue
). Then, I'll create Merge
that contains Red
and Blue
This is Red.vue
<template>
<div class="red-page">
Red page
<div style="height: 200px" />
<div class="link" @click="$emit('redirect')">To blue</div>
</div>
</template>
<script>
export default {
name: 'Red',
emits: ['redirect'],
};
</script>
<style>
.red-page {
background-color: red;
height: 100vh;
color: white;
}
.link {
cursor: pointer;
color: white;
}
</style>
This is Blue.vue
<template>
<div class="blue-page">
<div class="header" />
<!-- Any content -->
<div style="height: 700px" />
<div class="link" @click="$emit('redirect')">To red</div>
</div>
</template>
<script>
export default {
name: 'Blue',
emits: ['redirect'],
};
</script>
<style>
.blue-page {
background-color: blue;
height: 1000px;
}
.header {
background-color: yellow;
height: 50px;
position: fixed;
width: 90vw;
}
.link {
cursor: pointer;
color: white;
}
</style>
This is Merge.vue
<template>
<div>
<Red
style="position: fixed; height: 100vh; width: 100vw; z-index: -9999"
@redirect="redirect(false)"
/>
<Transition name="slide-down">
<Blue v-if="isBlue" @redirect="redirect(true)" />
</Transition>
</div>
</template>
<script>
import Blue from './Blue.vue';
import Red from './Red.vue';
export default {
name: 'Merge',
components: { Blue, Red },
data: () => ({
isBlue: true,
}),
methods: {
redirect(toRed) {
this.isBlue = !toRed;
history.pushState({}, null, this.isBlue ? '/blue' : '/red');
},
},
created() {
this.isBlue = this.$route.path === '/blue';
},
};
</script>
<style>
.slide-down-enter-active {
transition: all 0.8s cubic-bezier(1, 0.5, 0.8, 1);
}
.slide-down-leave-active {
transition: all 0.8s cubic-bezier(1, 0.5, 0.8, 1);
}
.slide-down-enter-from,
.slide-down-leave-to {
transform: translateY(1000px);
}
</style>
Put both Red
and Blue
inside Merge
. Then wrap Blue
with <Transition/>
. By this, you can add transition to Blue
and make it slide down to reveal Red
. Finally, inside Red
and Blue
, emit some signal when you try to redirect
The detail is here: https://stackblitz.com/edit/vue-fyebnk
In this example, I've made Blue
slides down when go to Red
and slides up when go back from Red
to Blue
.
In Blue
, you can see Red
is rendered when you look at Blue
right side. But it up to you to modify it since loading Red
first can be wasting if user is not gonna use it.
And of course, the transition can only occur if you click link inside Blue
or Red
. Any other redirect method will just immediately show Blue
or Red
Beside, if library is a way to go. I think you should have a look at RevealJS or sli.dev
Good luck to you