0

I have vue project.

Every time some elements on the page disappear because of v-if the rest of the page is slightly rearranged. I want it to happen smoothly.

All elements have :key attribute.

Example: I have centred 2 boxes in one row. When one is gone, the second one is still centred, so changes position. image

How to handle this?

EDIT 1 I tried:

    <div>
        <CompoentA :key=345 class="one-line" v-show="showComponentA" />
        <transition name="moving">
            <CompoentB class="one-line" :key=123 />
        </transition>
    </div>
.one-line { display: inline-table; }
.moving-move { transition: transform 1s; }
  • As devdgehog mentions, `v-if` is removing the items. You should think about either binding classnames, and then using CSS transitions to animate them, binding styles or alternatively, think about using something like masonry. https://vuejs.org/v2/guide/class-and-style.html#Binding-Inline-Styles – Djave Oct 20 '20 at 21:33
  • @Djave could you describe it more? I'm just learning css and have no idea how to use your advice. – Gabilcious Oct 20 '20 at 21:49

2 Answers2

0

v-if will remove the element from the DOM, so you can't animate your disappearing components.

You should use v-show instead if you want to animate them (they will stay hidden in the DOM).

devdgehog
  • 575
  • 2
  • 8
0

I think that you need "from" and "to" values to create this animation. When you remove the elemnt from DOM, the other elements will be placed based on a "inline" position, so there is no value reference to create a transition. There is a similar problem here, where a transition with height:0 and height: auto

How can I transition height: 0; to height: auto; using CSS?

I made a sample to solve this using a width (bigger than content) and width 0 with opacity 0 to hide the inner content. To run this sample, simple click in items, it will be "removed" (opacity:0 and width: 0) and the transition works because there is a initial width set (80px).

new Vue({
    el: "#app",
  data: () => ({
    // yes, there is better ways, but let make this sample "simple"
    letters: ['a', 'b', 'c', 'd'],
    visible: {
        a: true,
        b: true,
        c: true,
        d: true,
    }
  })
})
#app {
  /* decoration, you can remove */
  width: 100%; 
  text-align: center;
  background-color: #f4f4f4;
}

.moving {
  /* margin and padding 0 
     because the width content will be set to 0
     if this element has a margin, when removed the margin still display the "space"
  */
  padding: 0; 
  margin: 0;
  font-size: 0; /* remove white space in DOM element */
  display: inline-block;
  opacity: 1;
  transition: width linear .2s;
  
  /* decoration, you can remove */
  width: 80px; 
  border: 1px dotted #ccc;
  cursor: pointer;
}

.moving-content {
  font-size: 18px; /* restore font size */
  display: inline-block;
  
  /* decoration, you can remove */
  background-color: #2af; 
  color: white;
  padding: 20px;
  box-sizing: border-box;
}

.moving.hidden {
  width: 0px;
  opacity: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
    <div v-for="letter in letters"
        :key="letter"
        :class="{ moving: true, hidden: !visible[letter] }" @click="visible[letter] = false">
        <span class="moving-content">
          {{ letter }}
        </span>
    </div>
</div>

References:

https://stackoverflow.com/a/40785144/1724128

https://stackoverflow.com/a/53127208/1724128

Leo
  • 1,990
  • 1
  • 13
  • 20