Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Transition group animation changes behaviour when adding 3rd element

Context: I have a div on the top of the page that I show / hide from a button. The div is under the button and above the content. I've used transition-group so that the rest of the content slides up/down over the div when it shows/hides. The content has a margin-top so that it limits itself from the above div that shows/hides.

Need: I want a margin on top of the div so that when it's shown, it retains space between itself and the button. https://imgur.com/UG5iakC

Problems: I've tried 2 ways:

1) Placing a margin-top to the hiding div. Because I have position:absolute on the div when hiding it so that the content gets over the div, the div resizes to the size of the content, and so the margin gets automatically smaller; so when hiding it, the margin gets smaller before it hides, and it's ugly. GIF: https://gph.is/2QInDfj

2) Adding an hr above the div, inside the transition-group. Without the hr, the slide works just as intended, over the div. But when I add the hr and click to hide the div, the slide happens just as intended but the div and hr disappear instantly, instead of it showing and the content sliding over it and covering it. GIF: https://gph.is/2yd4JGt

Desired visual effect without the margin/hr on top: https://gph.is/2OPZyFV

HTML

<transition-group name="slide">
    <hr class="m-0" v-if="isVisible" key='h'>
    <div class="d-flex" v-if="isVisible" id="filters" key='x'>
        <div class="pl-5">
            <p class="filterTitles">Day</p>
            <app-day-filter v-for="day in weekDay" 
                :key="day.index" 
                :day="day">
            </app-day-filter>
        </div>
        <div class="pl-5">
            <p class="filterTitles">Time of day</p>
            <app-tod-filter v-for="todf in tod" 
                :key="tod.index" 
                :todf="todf">
            </app-tod-filter>
        </div>
    </div>
    <app-event v-for='(eveniment, index) in filterEvent' 
        :key='index' 
        :eveniment='eveniment' 
        :index='index'></app-event>
</transition-group>

CSS

.slide-enter {
    opacity:0;
}
.slide-enter-active {
    transition: all 1s ease-out;
}
.slide-leave-active{
    transition: all 1s ease-out;
    opacity: 0;
    position: absolute;
}
.slide-move {
    transition: transform 1s;
}
#filters {
/* border-top: 1px solid lightgrey; */
}

Suggestions?

Thanks

like image 363
bgd mhl Avatar asked Oct 10 '18 22:10

bgd mhl


1 Answers

This is primarily a CSS concern.

If the hr element is introduced into the layout within the transition-group, and, the transition CSS property is concerned with all, and, position is being set to absolute during the leave-active state (which will cause the element to "disappear" from its previous, relative position in the layout flow), then numerous elements and properties are going to be transitioned simultaneously, resulting in undesired effects.

However, given that the question seeks a solution without the margin and hr on top of the transition-group, and assuming the button has an event handler, like this:

<button class="filter-button" v-on:click="toggleSlider">Filters</button>

The function toggleSlider will toggle the isVisible property that the animated transition depends upon:

methods: {
  toggleSlider() {
    this.isVisible = !this.isVisible;
  }
}

And with CSS, instead of transitioning all, merely transition the properties that will achieve the sought after effect, namely, opacity, and with this answer, max-height. By removing the absolute positioning altogether, and using relative position plus z-indexing with the following CSS, the desired effect is achieved.

/* put margin spacing on the bottom of the button */
.filter-button {
  margin-bottom: 25px;
}

/* add relative positioning to enforce z-indexing */
.filter-group {
  position: relative;
  z-index: 1;
}

/* add relative positioning to enforce z-indexing */
.filter-content {
  position: relative;
  z-index: 2;
}

/* hidden states */
.slide-enter,
.slide-leave-to {
  opacity: 0;
  max-height: 0px;
}

/* shown states - max-height can be adjusted as desired */
.slide-enter-to,
.slide-leave {
  opacity: 1;
  max-height: 300px;
}

/* while animating during animation entry phase */
.slide-enter-active {
  transition: opacity 0.75s ease-in, max-height 0.5s ease-out;
}

/* while animating during the animation departure phase */
.slide-leave-active {
  transition: opacity 0.75s ease-out, max-height 0.5s ease-out;
}

/* add padding to bottom of filters section */
.pl-5 {
  padding-bottom: 25px;
}

By adding margin to the bottom of the button and the filter section, the spacing between sections is preserved.

I've created a CodeSandbox to illustrate this solution.

like image 53
jacefarm Avatar answered Oct 13 '22 02:10

jacefarm