Categories
Vue 3

Vue 3 — Dynamic Transitions and State Animations

Spread the love

Vue 3 is the up and coming version of Vue front end framework.

It builds on the popularity and ease of use of Vue 2.

In this article, we’ll look at creating dynamic transitions and state change animation.

Dynamic Transitions

We can create dynamic transitions by passing in a dynamic string to the name prop.

For example, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/velocity/1.2.3/velocity.min.js"></script>
  </head>
  <body>
    <div id="app">
      Fade in
      <input type="range" v-model="fadeDuration" min="0" :max="5000" /> <button @click="show = !show">
        toggle
      </button>
      <transition
        :css="false"
        @before-enter="beforeEnter"
        @enter="enter"
        @leave="leave"
      >
        <p v-if="show">hello</p>
      </transition>
    </div>
    <script>
      const app = Vue.createApp({
        data() {
          return {
            show: false,
            fadeDuration: 1000
          };
        },
        methods: {
          beforeEnter(el) {
            el.style.opacity = 0;
          },
          enter(el, done) {
            Velocity(
              el,
              { opacity: 1 },
              {
                duration: this.fadeDuration,
                complete: done
              }
            );
          },
          leave(el, done) {
            Velocity(
              el,
              { opacity: 0 },
              {
                duration: this.fadeDuration,
                complete: done
              }
            );
          }
        }
      }); app.mount("#app");
    </script>
  </body>
</html>

We added the Velocity library to add transitions effects of our choice.

The fadeDuration can be adjusted so that we change the length of the enter and leave effects.

We just called Velocity to change the opacity to what we want.

State Transitions

Vue can animate state changes.

They include things like numbers and calculations, colors displayed, SVG nodes, and other properties.

To do that, we can animate state change with watches.

For instance, we can make a simple state transition by writing:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.2.4/gsap.min.js"></script>
  </head>
  <body>
    <div id="app">
      <input v-model.number="number" type="number" step="20" />
      <p>{{ animatedNumber }}</p>
    </div>
    <script>
      const app = Vue.createApp({
        data() {
          return {
            number: 0,
            tweenedNumber: 0
          };
        },
        computed: {
          animatedNumber() {
            return this.tweenedNumber.toFixed(0);
          }
        },
        watch: {
          number(newValue) {
            gsap.to(this.$data, { duration: 1.5, tweenedNumber: newValue });
          }
        }
      }); app.mount("#app");
    </script>
  </body>
</html>

When we type in a number, then it’ll be animated.

The animation is don’t in the number watcher.

We call Greensock’s to method to animate towards the final value.

this.$data has the value of the final number.

tweenedNumber is the value displayed as the animation is being done.

animatedNumber takes the tweenedNumber value, return it and display it.

Organizing Transitions into Components

We can organize animations into their own components.

For instance, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.2.4/gsap.min.js"></script>
  </head>
  <body>
    <div id="app">
      <input v-model.number="x" type="number" step="20" /> +
      <input v-model.number="y" type="number" step="20" /> = {{ result }}
      <p>
        <animated-integer :value="x"></animated-integer> +
        <animated-integer :value="y"></animated-integer> =
        <animated-integer :value="result"></animated-integer>
      </p>
    </div> <script>
      const app = Vue.createApp({
        data() {
          return {
            x: 400,
            y: 373
          };
        },
        computed: {
          result() {
            return this.x + this.y;
          }
        }
      }); app.component("animated-integer", {
        template: "<span>{{ fullValue }}</span>",
        props: {
          value: {
            type: Number,
            required: true
          }
        },
        data() {
          return {
            tweeningValue: 0
          };
        },
        computed: {
          fullValue() {
            return Math.floor(this.tweeningValue);
          }
        },
        methods: {
          tween(newValue, oldValue) {
            gsap.to(this.$data, {
              duration: 0.5,
              tweeningValue: newValue,
              ease: "sine"
            });
          }
        },
        watch: {
          value(newValue, oldValue) {
            this.tween(newValue, oldValue);
          }
        },
        mounted() {
          this.tween(this.value, 0);
        }
      }); app.mount("#app");
    </script>
  </body>
</html>

We created the animated-integer component to animate our number.

It still animates the number, but we have a tween method to create the animation so that we can simplify our value watcher method.

fullValue is a computed property to round this.tweeningValue down to the nearest integer.

this.tweeningValue is the values that’ll be displayed during the animation.

So when we type in our numbers, we’ll see all the numbers being animated.

Conclusion

One good use of watchers is to animate state changes.

We made this easy with the Greensock library.

Also, we can change the settings of the transition on the fly.

By John Au-Yeung

Web developer specializing in React, Vue, and front end development.

Leave a Reply

Your email address will not be published. Required fields are marked *