Vue 3 Page transition : how to have the next page already in place and reveal it with a swipe?

434 views Asked by At

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

1

There are 1 answers

3
duckstery On BEST ANSWER

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