Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Smooth vue collapse transition on v-if

Tags:

I'm struggling with vue transitions trying to show / hide content using v-if smoothly. Whilst I understand the css classes and transitions, I can make the content appear 'smoothly' using things like opacity or translation etc...but once the animation is complete (or rather as it starts), any html sections below seem to 'jump'.

I'm trying to achieve the same affect as the Bootstrap 4 'collapse' class - click one of the top buttons here: https://getbootstrap.com/docs/4.0/components/collapse/

As the hidden section appears / disappears, all the html content 'slides' nicely with it.

Is this possible using Vue transition for content being shown using v-if? All the samples on the vue transitions docs, whilst having great css transition effects, have the below html 'jump' once the transition has started or is complete.

I've seen some pure js solutions using max-height - https://jsfiddle.net/wideboy32/7ap15qq0/134/

and tried with vue: https://jsfiddle.net/wideboy32/eywraw8t/303737/

.smooth-enter-active, .smooth-leave-active {   transition: max-height .5s; } .smooth-enter, .smooth-leave-to {   max-height: 0 .5s; } 

Thanks!

like image 471
The Wideboy Avatar asked Aug 25 '18 19:08

The Wideboy


2 Answers

i also had similar task. I found that it isn't possible to do it without JS. So i write custom transition component ( Reusable Transitions ) and it works for me:

Vue.component('transition-collapse-height', {    template: `<transition      enter-active-class="enter-active"      leave-active-class="leave-active"      @before-enter="beforeEnter"      @enter="enter"      @after-enter="afterEnter"      @before-leave="beforeLeave"      @leave="leave"      @after-leave="afterLeave"    >      <slot />    </transition>`,    methods: {      /**       * @param {HTMLElement} element       */      beforeEnter(element) {        requestAnimationFrame(() => {          if (!element.style.height) {            element.style.height = '0px';          }            element.style.display = null;        });      },      /**       * @param {HTMLElement} element       */      enter(element) {        requestAnimationFrame(() => {          requestAnimationFrame(() => {            element.style.height = `${element.scrollHeight}px`;          });        });      },      /**       * @param {HTMLElement} element       */      afterEnter(element) {        element.style.height = null;      },      /**       * @param {HTMLElement} element       */      beforeLeave(element) {        requestAnimationFrame(() => {          if (!element.style.height) {            element.style.height = `${element.offsetHeight}px`;          }        });      },      /**       * @param {HTMLElement} element       */      leave(element) {        requestAnimationFrame(() => {          requestAnimationFrame(() => {            element.style.height = '0px';          });        });      },      /**       * @param {HTMLElement} element       */      afterLeave(element) {        element.style.height = null;      },    },  });    new Vue({    el: '#app',    data: () => ({      isOpen: true,    }),    methods: {      onClick() {        this.isOpen = !this.isOpen;      }    }  });
.enter-active,  .leave-active {    overflow: hidden;    transition: height 1s linear;  }    .content {    background: grey;  }
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>  <div id="app">    <button @click="onClick">      open/hide    </button>    <transition-collapse-height>     <div v-show="isOpen" class="content">       <br/>       <br/>       <br/>       <br/>     </div>    </transition-collapse-height>  </div>
like image 166
Alexandr Vysotsky Avatar answered Sep 20 '22 15:09

Alexandr Vysotsky


If you want to animate max-height, then you should enter the amount of max-height for the element you want to animate, also correct the second class as you put 's' (or seconds) in max-height definition :

p{   max-height: 20px; } .smooth-enter-active, .smooth-leave-active {   transition: max-height .5s; } .smooth-enter, .smooth-leave-to {   max-height: 0; } 

if you want something like bs4 collapse then the example inside vue website will do :

.smooth-enter-active, .smooth-leave-active {   transition: opacity .5s; } .smooth-enter, .smooth-leave-to {   opacity: 0 } 

Edit : What you are trying to do is achievable by first finding out the height of the content and then setting it inside .*-enter-to and .*-leave classes. One way to do that is demonstrated in fiddle below :

https://jsfiddle.net/rezaxdi/sxgyj1f4/3/

You can also completely forget about v-if or v-show and just hide the element using height value which I think is a lot smoother :

https://jsfiddle.net/rezaxdi/tgfabw65/9/

like image 34
r3zaxd1 Avatar answered Sep 19 '22 15:09

r3zaxd1