Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Vue/CSS, how to make a smooth height transition between two alternating elements (Fiddle included)

I'm trying to make two elements that alternate on an opposing v-show predicate to transition between themselves in terms of height, but I'm unable to find a good solution for this. This may be a bit elementary, but I'm inexperienced with transitions/animations, and I can't find any good examples to guide me here.

HTML:

<div id="app">

  <div class="history">
    <p>
    How to make the green bordered area transition smoothly between different height in states A and B?
    </p>
    <div class="placeholder-content">
    </div>
  </div>

  <div class="interaction">
    <button @click="(show_A ? show_A = false : show_A = true);">
      Cycle states
    </button>

    <transition name="swap">
    <div v-show="show_A" class="interaction-A"> A </div>
    </transition>

    <transition name="swap">
    <div v-show="!show_A" class="interaction-B"> B </div>
    </transition>

  </div>
</div>

CSS:

.swap-enter{
}
.swap-leave-to{
}
.swap-enter-active{
}
.swap-leave-active{
}
.swap-move{
}

I've formulated the problem in a fiddle:

https://fiddle.jshell.net/jensmtg/7zun5c9f/

like image 215
jensmtg Avatar asked Oct 29 '22 05:10

jensmtg


1 Answers

There's a few critical mistakes in your code.

First, you need to use one transition element, not two, since you want the transition to occur IN the transition element, not ACROSS multiple transition elements, even though it involves them.

HTML:

<div class="interaction" v-bind:class="{ show_B: !show_A }">
    <button @click="(show_A ? show_A = false : show_A = true);">
      Cycle states
    </button>

    <transition name="fade">
      <div v-if="show_A" class="interaction-A" key="a"> A </div>
      <div v-else class="interaction-B" key="b"> B </div>
    </transition>
</div>

Second, you'll need to apply the key attribute to each element that you want to transition with a unique key, so that Vue recognizes that they are different.

I've cleaned up your code and used v-if and v-else to ensure that the elements are being torn down.

Finally, using a fade transition gives you a nice smooth change that accounts for the height difference.

CSS:

.interaction {
  border: 10px solid lightgreen;
  display: flex;
  flex: 1 0 auto;
  max-height: 225px;
  transition: max-height 0.25s ease-out;
}

.interaction.show_B {
    max-height: 325px;
    transition: max-height 0.15s ease-in;
}

.fade-enter-active, .fade-leave-active {
  transition-property: opacity;
  transition-duration: .10s;
 }

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

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

In order to smoothly transition the outer container while we transition the inner elements, a class show_B is applied when the content is toggled to differentiate between A and B child content.

<div class="interaction" v-bind:class="{ show_B: !show_A }">

We can use this to apply a transition and a new max size, resizing the outer content as the child content resizes:

.interaction.show_B {
    max-height: 325px;
    transition: max-height 0.15s ease-in;
}

You can see the working fiddle here.

like image 105
David L Avatar answered Nov 10 '22 14:11

David L