Home » vue » Vue js bootstrap add animation while collapse

Vue js bootstrap add animation while collapse

Posted by: admin November 26, 2021 Leave a comment

Questions:

I am making a side bar menu with collapse show/hide. With my current css the collapse is not smooth and it seems force full or looks some weird.

I wanted a slide with smooth transition when the item is being closed. But here in my case what’s happening is when some item is already open and next item is clicked(for open). There it looks like toggle is being forcefully and it does not seems smooth collapsing of the list.

What could be the better approach for this please suggest some better ways.

Fiddle Implementation.

I don’t know whether my approach is correct or I am missing something here?

new Vue({
  el: '#app',
  methods: {
    setActiveItemId(itemIndex) {
      if (itemIndex === this.activeItemId) {
        this.activeItemId = ''
        return
      }
      this.activeItemId = itemIndex
    }
  },
  data() {
    return {
      message: 'Hello Vue.js!',
      activeItemId: '',
      sideBar: [{
          name: "Dashboard",
          url: "/dashboard",
          icon: "ti-world",
          children: [{
              name: "Buttons",
              url: "/components/buttons",
              icon: "fa-book",
            },
            {
              name: "Social Buttons",
              url: "/components/social-buttons",
              icon: "icon-puzzle",
            }
          ]
        },
        {
          name: "Components",
          url: "/components",
          icon: "ti-pencil-alt",
          children: [{
              name: "Buttons",
              url: "/components/buttons",
              icon: "fa-book",
            },
            {
              name: "Social Buttons",
              url: "/components/social-buttons",
              icon: "icon-puzzle",
            }
          ]
        },
        {
          name: "Validation",
          url: "/components",
          icon: "ti-pencil-alt",
          children: [{
              name: "Buttons",
              url: "/components/buttons",
              icon: "fa-book",
            },
            {
              name: "Social Buttons",
              url: "/components/social-buttons",
              icon: "icon-puzzle",
            }
          ]
        }
      ]
    }
  },
  computed: {
    isActive() {
      return this.activeItemId !== ''
    }
  }
})
.collapse.show {
  display: block;
}

.collapse {
  display: none;
}

.list-unstyled {
  padding-left: 0;
  list-style: none;
}

.collapse.list-unstyled {
  padding-left: 15px;
}

nav.side-navbar {
  background: #fff;
  min-width: 250px;
  max-width: 250px;
  color: #000;
  -webkit-box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.1);
  box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.1);
  z-index: 999;
}

nav.side-navbar ul a:hover {
  background: orange;
  color: #fff !important;
}

nav.side-navbar ul a {
  padding: 10px 15px;
  text-decoration: none;
  display: block;
  font-weight: 300;
  border-left: 4px solid transparent;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.0.0/css/bootstrap.min.css" rel="stylesheet"/>

<script src="https://unpkg.com/vue"></script>

<div id="app">
  <nav class="side-navbar">
    <ul class="list-unstyled">
      <li>
        <a>
          <i class="ti-home"></i>Home</a>
      </li>
      <li v-for="(x, itemIndex) in sideBar" :key="itemIndex">
        <a @click="setActiveItemId(itemIndex)">
          <i class="fa" :class="x.icon"></i>{{x.name}}
        </a>
        <ul :id="x.id" class="collapse list-unstyled" :class="{'show':activeItemId === itemIndex  && isActive}">
          <li v-for="y in x.children" :key="y.id">
            <a>{{y.name}}</a>
          </li>
        </ul>
      </li>
    </ul>
  </nav>
</div>

Answers:

You can use Vue’s List Transitions (the <transition-group> tag).

Change the sublist ul to:

<ul :id="x.id" class="collapse list-unstyled show">
  <transition-group name="list">
    <li v-for="y in (activeItemId === itemIndex  && isActive ? x.children : [])" :key="y.name">
      <a>{{y.name}}</a>
    </li>
  </transition-group>
</ul>

Mainly instead of hiding the <ul> we are changing the v-for array to/from empty. Notice I also changed the il‘s keys, as you were using an invalid prop.

And add the following CSS for the transitions:

.list-enter {
  opacity: 0;
}
.list-enter-active {
  animation: slide-in .5s ease-out forwards;
}
.list-leave-to, .list-leave-active {
  opacity: 0;
  animation: slide-out .5s ease-out forwards;
}
@keyframes slide-in {
  from { height: 0; } to { height: 40px; }
}
@keyframes slide-out {
  from { height: 40px; } to { height: 0; }
}

Updated JSFiddle here. Demo below.

new Vue({
  el: '#app',
  methods: {
    setActiveItemId(itemIndex) {
      if (itemIndex === this.activeItemId) {
        this.activeItemId = ''
        return
      }
      this.activeItemId = itemIndex
    }
  },
  data() {
    return {
      message: 'Hello Vue.js!',
      activeItemId: '',
      sideBar: [{
          name: "Dashboard",
          url: "/dashboard",
          icon: "ti-world",
          children: [{
              name: "Buttons",
              url: "/components/buttons",
              icon: "fa-book",
            },
            {
              name: "Social Buttons",
              url: "/components/social-buttons",
              icon: "icon-puzzle",
            }
          ]
        },
        {
          name: "Components",
          url: "/components",
          icon: "ti-pencil-alt",
          children: [{
              name: "Buttons",
              url: "/components/buttons",
              icon: "fa-book",
            },
            {
              name: "Social Buttons",
              url: "/components/social-buttons",
              icon: "icon-puzzle",
            }
          ]
        },
        {
          name: "Validation",
          url: "/components",
          icon: "ti-pencil-alt",
          children: [{
              name: "Buttons",
              url: "/components/buttons",
              icon: "fa-book",
            },
            {
              name: "Social Buttons",
              url: "/components/social-buttons",
              icon: "icon-puzzle",
            }
          ]
        }
      ]
    }
  },
  computed: {
    isActive() {
      return this.activeItemId !== ''
    }
  }
})
.collapse.show {
  display: block;
}

.collapse {
  display: none;
}

.list-unstyled {
  padding-left: 0;
  list-style: none;
}

.collapse.list-unstyled {
  padding-left: 15px;
}

nav.side-navbar {
  background: #fff;
  min-width: 250px;
  max-width: 250px;
  color: #000;
  -webkit-box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.1);
  box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.1);
  z-index: 999;
}

nav.side-navbar ul a:hover {
  background: orange;
  color: #fff !important;
}

nav.side-navbar ul a {
  padding: 10px 15px;
  text-decoration: none;
  display: block;
  font-weight: 300;
  border-left: 4px solid transparent;
}

.list-enter {
  opacity: 0;
}
.list-enter-active {
  animation: slide-in .5s ease-out forwards;
}
.list-leave-to, .list-leave-active {
  opacity: 0;
  animation: slide-out .5s ease-out forwards;
}
@keyframes slide-in {
  from { height: 0; } to { height: 40px; }
}
@keyframes slide-out {
  from { height: 40px; } to { height: 0; }
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.0.0/css/bootstrap.min.css" rel="stylesheet" />

<script src="https://unpkg.com/vue"></script>

<div id="app">
  <nav class="side-navbar">
    <ul class="list-unstyled">
      <li>
        <a>
          <i class="ti-home"></i>Home</a>
      </li>
      <li v-for="(x, itemIndex) in sideBar" :key="itemIndex">
        <a @click="setActiveItemId(itemIndex)">
          <i class="fa" :class="x.icon"></i>{{x.name}}
        </a>
        <ul :id="x.id" class="collapse list-unstyled show">
          <transition-group name="list">
            <li v-for="y in (activeItemId === itemIndex  && isActive ? x.children : [])" :key="y.name">
              <a>{{y.name}}</a>
            </li>
          </transition-group>
        </ul>
      </li>
    </ul>
  </nav>
</div>

###

There’s easier solution, try adding this piece of css to your styles.

.show {
  transition: width 0.5s, height 0.5s, transform 0.5s;
}