Yung Silva
Yung Silva

Reputation: 1500

Slide Reverse Transitions, Vue Js

I'm developing a single page application / mobile app, with Vue.JS. I want a slide effect when changing the pages, and I can do it like this:

transition name="slide"
 router-view transition
transition

But I wanted the reverse effect of the slide when the user returns the page. In other words, when the user opens a new page, the page will come from the right, but when they go back, the page will come from the left.

There is a plugin for Vue router, called vue-router-transition, but it does not work. It is out of date, it only works with very old versions of Vue.

Also there is a tutorial on dynamic transitions, but only works when it is parents routes, ex: example.com/rota1/rota2/rota3, which is not my case.

I thought of the following logic in the before.each.router, set the transition class (slide-left or slide-right) depending on whether the user clicked on a go back button.

The problem is that I do not know how to apply this logic in code. I would have to pass the value of a variable that is in main.js to app.vue and I do not know how to do this.

Upvotes: 3

Views: 6500

Answers (3)

Syed
Syed

Reputation: 16513

Hope this helps:

new Vue({
  el: '#demo',
  data: {
    slideTransition: 'slide-left',
    showChild: false,
  },
  watch: {
    showChild(value) {
      if (value) {
        this.setSlideTransition('slide-right');
      } else {
        this.setSlideTransition('slide-left');
      }
    },
  },
  methods: {
    setSlideTransition(slideDirection) {
      // Note: 300ms mentioned below is matching with css transition timing
      setTimeout(() => { this.slideTransition = slideDirection; }, 300);
    },
  },
});
body {
  margin: 0;
  color: #bdbdbd;
  background-color: #161616;
  font-family: Helvetica neue, roboto;
}

.container {
  width: 500px;
  background: #161616;
}

main {
  width: 60%;
  height: 300px;
  background-color: #333;
}

aside {
  width: 40%;
  background-color: #555;
}

.container, main, .parent, .child, .content {
  display: flex;
  align-items: center;
  justify-content: center;
}

.parent {
  background-color: deepskyblue;
}

.child {
  background-color: deeppink;
}

.container, main, aside {
  position: relative;
  height: 199px;
}

.pin {
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  position: absolute;
}

.pt-50 {
  padding-top: 50px;
}

/* transitions */
.fade-enter-active,
.fade-leave-active {
  transition-property: opacity;
  transition-duration: 0.25s;
}

.fade-enter-active {
  transition-delay: 0.25s;
}

.fade-enter,
.fade-leave-active {
  opacity: 0;
}

.slide-left-leave-active,
.slide-left-enter-active,
.slide-right-leave-active,
.slide-right-enter-active {
  transition: 0.3s;
}

.slide-left-enter {
  transform: translate(100%, 0);
}

.slide-left-leave-to {
  transform: translate(-100%, 0);
}

.slide-right-enter {
  transform: translate(-100%, 0);
}

.slide-right-leave-to {
  transform: translate(100%, 0);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>

<div id="demo" class="container">
  <aside>
    <transition name="fade">
      <div class="pin parent" v-if="!showChild">
        <div>
          <h1>Parent</h1>
          <a href="#" @click="showChild = true">Go To Child</a>
        </div>
      </div>
    </transition>
    <transition :name="slideTransition">
      <div class="pin child" v-if="showChild">
        <div>
          <h1>Child</h1>
          <a href="#" @click="showChild = false">Go To Parent</a>
        </div>
      </div>
    </transition>        
  </aside>
  <main>
   <div>
    <h1>Main</h1>
    <transition name="fade">
      <div v-if="showChild" class="pin content pt-50" key="child">
        <h4>Child Content here</h4>
      </div>
      <div v-else="" class="pin content pt-50" key="parent">
        <h4>Parent Content here</h4>
      </div>
    </transition>
   </div>
  </main>
</div>

Upvotes: 2

Philippe
Philippe

Reputation: 97

I see 2 options:

  1. Store a variable in Vuex "transitionBack" and set it to true. Change the router-link to a @click method. In the method, first save the variable and then navigate to the link. Check that variable on your beforeRouteUpdate(to, from, next) method.
  2. Implement some logic in your beforeRouteUpdate(to, from, next) method that will check on the name of the link (e.g. if you can only go "back" from a certain page, then keep a list of all these type of pages)

Upvotes: 1

Philip
Philip

Reputation: 3536

A while ago I've used the meta object in vue-router and added a "fake" depth, because I haven't any children objects. If you use children, then go with this example: https://github.com/vuejs/vue-router/blob/dev/examples/transitions/app.js

export default () => {
  return [{
      meta: {
        depth: 0
      },
      path: '/home',
      component: Home
    },
    {
      meta: {
        depth: 1
      },
      path: '/about',
      component: About
    }]
}

Now you can check it by your own like this in your App.vue.

watch: {
  $route(to, from) {
    const toDepth = to.meta.depth || 0;
    const fromDepth = from.meta.depth || 0;

    this.transitionName = toDepth >= fromDepth ? 'slide-left' : 'slide-right';
  }
}

Upvotes: 2

Related Questions