Menu Close

Vue.js Transition Effects — Transitioning Between Elements

Transitioning Between Elements

With Vue, we can transition elements between v-if and v-else .

When toggling between elements that have the same tag name, we must tell Vue that they’re distinct elements giving them unique key attributes. Otherwise, the Vue compiler will only replace the content of the element for efficiency.

It’s always a good idea to key multiple items within a transition element.

For example, we can transition between 2 p elements as follows:

src/index.js :

new Vue({  
  el: "#app",  
  data: { show: false }  
});

src/style.css :

.fade-enter-active,  
.fade-leave-active {  
  transition: opacity 0.5s;  
}  
.fade-enter,  
.fade-leave-to {  
  opacity: 0;  
}

index.html :

<!DOCTYPE html>  
<html>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>  
    <link  
      rel="stylesheet"  
      type="text/css"  
      href="./src/styles.css"  
      media="screen"  
    />  
  </head>  
  <body>  
    <div id="app">  
      <button v-on:click="show = !show">  
        Toggle  
      </button>  
      <transition name="fade">  
        <p v-if="show" key="hi">hi</p>  
        <p v-else key="bye">bye</p>  
      </transition>  
    </div>  
    <script src="src/index.js"></script>  
  </body>  
</html>

Then we should see fade effects when we transition between ‘hi’ and ‘bye’.

We added key attributes to each so they’ll always be rendered from scratch.

Also, we can use the key attribute to transition between different states of the same element.

We can do that as follows:

src/index.js :

new Vue({  
  el: "#app",  
  data: { show: false }  
});

src/styles.css :

.fade-enter-active,  
.fade-leave-active {  
  transition: opacity 0.5s;  
}  
.fade-enter,  
.fade-leave-to {  
  opacity: 0;  
}

index.html :

<!DOCTYPE html>  
<html>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>  
    <link  
      rel="stylesheet"  
      type="text/css"  
      href="./src/styles.css"  
      media="screen"  
    />  
  </head>  
  <body>  
    <div id="app">  
      <button v-on:click="show = !show">  
        Toggle  
      </button>  
      <transition name="fade">  
        <p v-bind:key="show">  
          {{ show ? 'hi' : 'bye' }}  
        </p>  
      </transition>  
    </div>  
    <script src="src/index.js"></script>  
  </body>  
</html>

In the code above, we replaced:

<transition name="fade">  
  <p v-if="show" key="hi">hi</p>  
  <p v-else key="bye">bye</p>  
</transition>

with:

<transition name="fade">  
  <p v-bind:key="show">  
    {{ show ? 'hi' : 'bye' }}  
  </p>  
</transition>

Everything else remained the same.

The transition can be done between any number of elements with v-if or binding a single element to a dynamic property.

For example, we can write:

src/index.js :

new Vue({  
  el: "#app",  
  data: { states: ["foo", "bar", "baz"], state: "", index: 0 },  
  methods: {  
    rotate() {  
      this.index = (this.index + 1) % this.states.length;  
      this.state = this.states[this.index];  
    }  
  }  
});

src/styles.css :

.fade-enter-active,  
.fade-leave-active {  
  transition: opacity 0.5s;  
}  
.fade-enter,  
.fade-leave-to {  
  opacity: 0;  
}

index.html :

<!DOCTYPE html>  
<html>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>  
    <link  
      rel="stylesheet"  
      type="text/css"  
      href="./src/styles.css"  
      media="screen"  
    />  
  </head>  
  <body>  
    <div id="app">  
      <button v-on:click="rotate">  
        Rotate  
      </button>  
      <transition name="fade">  
        <p v-bind:key="state">  
          {{ state }}  
        </p>  
      </transition>  
    </div>  
    <script src="src/index.js"></script>  
  </body>  
</html>

We can rotate to different state value from the states array. Then we see transition each time the value of state changes.

Also, we can write out the v-if statements for each item as follows:

index.html :

<!DOCTYPE html>  
<html>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>  
    <link  
      rel="stylesheet"  
      type="text/css"  
      href="./src/styles.css"  
      media="screen"  
    />  
  </head>  
  <body>  
    <div id="app">  
      <button v-on:click="rotate">  
        Rotate  
      </button>  
      <transition name="fade">  
        <p v-if="state === 'foo'" key="foo">  
          foo  
        </p>  
        <p v-if="state === 'bar'" key="bar">  
          bar  
        </p>  
        <p v-if="state === 'baz'" key="baz">  
          baz  
        </p>  
      </transition>  
    </div>  
    <script src="src/index.js"></script>  
  </body>  
</html>

The rest are unchanged.

Transition Modes

We can set the transition modes to control how old elements are transition to new elements.

There’re 2 transition modes:

  • in-out — new element transitions in first, then the current element transitions out
  • out-in — current element transitions our first and then the new element transitions in

For example, we can use it as follows:

src/index.js :

new Vue({  
  el: "#app",  
  data: { states: ["foo", "bar", "baz"], state: "", index: 0 },  
  methods: {  
    rotate() {  
      this.index = (this.index + 1) % this.states.length;  
      this.state = this.states[this.index];  
    }  
  } 
});

src/styles.css :

.fade-enter-active,  
.fade-leave-active {  
  transition: opacity 0.5s;  
}  
.fade-enter,  
.fade-leave-to {  
  opacity: 0;  
}

index.html :

<!DOCTYPE html>  
<html>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>  
    <link  
      rel="stylesheet"  
      type="text/css"  
      href="./src/styles.css"  
      media="screen"  
    />  
  </head>  
  <body>  
    <div id="app">  
      <button v-on:click="rotate">  
        Rotate  
      </button>  
      <transition name="fade" mode="out-in">  
        <p v-bind:key="state">  
          {{ state }}  
        </p>  
      </transition>  
    </div>  
    <script src="src/index.js"></script>  
  </body>  
</html>

With the mode attribute added, we no longer have the jumpiness that the previous example has when transitioning between elements since old and new elements won’t exist on the same screen.

Conclusion

We can add the transition mode to prevent undesirable results from the old and new elements existing together.

Also, we should add a key attribute to each element that’s transitioned to that they’re rendered from scratch. It works for v-if and dynamic elements.

Posted in vue