Categories
Vue 3

Vue 3 — Transitions and Animations

Vue 3 is in beta and it’s subject to change.

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 CSS transitions and animations.

CSS Transitions

The transition a component is most commonly used with CSS transitions.

For instance, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
    <style>
      .fade-enter-active {
        transition: all 0.3s ease-out;
      }

      .fade-leave-active {
        transition: all 0.8s cubic-bezier(1, 0.5, 0.8, 1);
      }

      .fade-enter-from,
      .fade-leave-to {
        transform: translateX(20px);
        opacity: 0;
      }
    </style>
  </head>
  <body>
    <div id="app">
      <button @click="show = !show">
        Toggle
      </button>

      <transition name="fade">
        <p v-if="show">hello</p>
      </transition>
    </div>
    <script>
      const app = Vue.createApp({
        data() {
          return {
            show: false
          };
        }
      });
      app.mount("#app");
    </script>
  </body>
</html>

We have the transition component that has the name prop to set the prefix for the transition class names.

We set the transition property in the CSS to look let us set the transition effects.

ease-out is for entering effects.

And cubic-bezier is our easing curve to adjust the speed of the transition as it progresses.

CSS Animations

CSS animations are applied in the same way as CSS transitions.

The different between transitions and animations is that v-enter-from isn’t removed immediately after the element is inserted.

It’s instead removed after the animationend event is triggered.

For instance, we can create animations with keyframes by writing:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
    <style>
      .bounce-enter-active {
        animation: bounce-in 1.5s;
      }
      .bounce-leave-active {
        animation: bounce-in 1.5s reverse;
      }
      @keyframes bounce-in {
        0% {
          transform: scale(0);
        }
        50% {
          transform: scale(1.5);
        }
        100% {
          transform: scale(1);
        }
      }
    </style>
  </head>
  <body>
    <div id="app">
      <button @click="show = !show">
        Toggle
      </button>

      <transition name="bounce">
        <p v-if="show">hello</p>
      </transition>
    </div>
    <script>
      const app = Vue.createApp({
        data() {
          return {
            show: false
          };
        }
      });
      app.mount("#app");
    </script>
  </body>
</html>

We have the keyframes in our CSS so that we can define our animation effects at the stages we want.

We have a transform effect during the start, when it’s halfway through, and when it ends.

Custom Transition Classes

We can set custom transition classes instead of using the preset class names.

To do that, we just have to set a few props.

The props we can set are:

  • enter-from-class
  • enter-active-class
  • enter-to-class
  • leave-from-class
  • leave-active-class
  • leave-to-class

For instance, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
    <style>
      .enter-active {
        animation: bounce-in 1.5s;
      }
      .leave-active {
        animation: bounce-in 1.5s reverse;
      }
      @keyframes bounce-in {
        0% {
          transform: scale(0);
        }
        50% {
          transform: scale(1.5);
        }
        100% {
          transform: scale(1);
        }
      }
    </style>
  </head>
  <body>
    <div id="app">
      <button @click="show = !show">
        Toggle
      </button>

      <transition
        enter-active-class="enter-active"
        leave-active-class="leave-active"
      >
        <p v-if="show">hello</p>
      </transition>
    </div>
    <script>
      const app = Vue.createApp({
        data() {
          return {
            show: false
          };
        }
      });
      app.mount("#app");
    </script>
  </body>
</html>

We have the enter-active and leave-active classes to instead of the default class names because we set the enter-active-clas and leave-active-class props.

They override the default class names.

Conclusion

We can change the name of animations and transitions.

There’s a slight difference between transitions and animations.

Categories
Vue 3

Vue 3 — Transition Modes

Vue 3 is in beta and it’s subject to change.

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 various transition effects.

Transitions on Initial Render

We can apply a transition on initial render of a node with the appear attribute.

For instance, we can use it by writing:

<!DOCTYPE html>  
<html lang="en">  
  <head>  
    <title>App</title>  
    <script src="https://unpkg.com/vue@next"></script>  
    <style>  
      .bounce-enter-active {  
        animation: bounce-in 1.3s;  
      }  
      .bounce-leave-active {  
        animation: bounce-in 1.3s reverse;  
      }  
      @keyframes bounce-in {  
        0% {  
          transform: scale(0);  
        }  
        50% {  
          transform: scale(1.8);  
        }  
        100% {  
          transform: scale(1);  
        }  
      }  
    </style>  
  </head>  
  <body>  
    <div id="app">  
      <transition name="bounce" appear>  
        <p>hello</p>  
      </transition>  
    </div>  
    <script>  
      const app = Vue.createApp({});  
      app.mount("#app");  
    </script>  
  </body>  
</html>

We just add the appear attribute to our transition component to make the transition appear when we load the element.

Transitioning Between Elements

We can add a transition between elements that are with the v-if and v-else directives.

For example, we can write:

<!DOCTYPE html>  
<html lang="en">  
  <head>  
    <title>App</title>  
    <script src="https://unpkg.com/vue@next"></script>  
    <style>  
      .bounce-enter-active {  
        animation: bounce-in 1.3s;  
      }  
      .bounce-leave-active {  
        animation: bounce-in 1.3s reverse;  
      }  
      @keyframes bounce-in {  
        0% {  
          transform: scale(0);  
        }  
        50% {  
          transform: scale(1.8);  
        }  
        100% {  
          transform: scale(1);  
        }  
      }  
    </style>  
  </head>  
  <body>  
    <div id="app">  
      <button @click="show = !show">toggle</button>  
      <transition name="bounce">  
        <p v-if="show">hello</p>  
        <p v-else>bye</p>  
      </transition>  
    </div>  
    <script>  
      const app = Vue.createApp({  
        data() {  
          return {  
            show: false  
          };  
        }  
      });  
      app.mount("#app");  
    </script>  
  </body>  
</html>

We create a transition effect that transitions between the 2 p elements in the transition component.

The transition automatically with the v-if and v-else directives.

We can also transition between any number of elements.

For example, we can write:

<!DOCTYPE html>  
<html lang="en">  
  <head>  
    <title>App</title>  
    <script src="https://unpkg.com/vue@next"></script>  
    <style>  
      .bounce-enter-active {  
        animation: bounce-in 0.8s;  
      }  
      .bounce-leave-active {  
        animation: bounce-in 0.8s reverse;  
      }  
      @keyframes bounce-in {  
        0% {  
          transform: scale(0);  
        }  
        50% {  
          transform: scale(1.8);  
        }  
        100% {  
          transform: scale(1);  
        }  
      }  
    </style>  
  </head>  
  <body>  
    <div id="app">  
      <button @click="count++">increment</button>  
      <transition name="bounce">  
        <p v-if="count % 3 === 0" key="0">foo</p>  
        <p v-else-if="count % 3 === 1" key="1">bar</p>  
        <p v-else key="2">baz</p>  
      </transition>  
    </div>  
    <script>  
      const app = Vue.createApp({  
        data() {  
          return {  
            count: 0  
          };  
        }  
      });  
      app.mount("#app");  
    </script>  
  </body>  
</html>

We have the v-else-if and v-else directives to make sure that only one is rendered at the same time.

This way, we can keep using the transition component to animate between multiple elements.

Transition Modes

If we deal with more complex animations we can set the mode prop to set the transition effect that’s run.

By default, the in-out and out-in transitions are run simultaneously.

With this prop, we can make our component apply one effect or the other.

Usually, the out-in mode is what we want.

For example, we can write:

<!DOCTYPE html>  
<html lang="en">  
  <head>  
    <title>App</title>  
    <script src="https://unpkg.com/vue@next"></script>  
    <style>  
      .bounce-enter-active {  
        animation: bounce-in 1.3s;  
      }  
      .bounce-leave-active {  
        animation: bounce-in 1.3s reverse;  
      }  
      @keyframes bounce-in {  
        0% {  
          transform: scale(0);  
        }  
        50% {  
          transform: scale(1.8);  
        }  
        100% {  
          transform: scale(1);  
        }  
      }  
    </style>  
  </head>  
  <body>  
    <div id="app">  
      <button @click="show = !show">toggle</button>  
      <transition name="bounce" mode="out-in">  
        <p v-if="show">hello</p>  
        <p v-else>bye</p>  
      </transition>  
    </div>  
    <script>  
      const app = Vue.createApp({  
        data() {  
          return {  
            show: false  
          };  
        }  
      });  
      app.mount("#app");  
    </script>  
  </body>  
</html>

With the mode prop set, we won’t see multiple elements being animated at the same time.

Conclusion

We can create transitions between multiple elements with a few props and directives.

Categories
Vue 3

Vue 3 — Refs and Edge Cases

Vue 3 is in beta and it’s subject to change.

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 how to use template refs and edge cases with Vue 3.

Template refs

Sometimes we may need to directly access the DOM.

To let us do that, we can assign a ref to the DOM element we want to access and then we can access it in our component code.

For example, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      <input ref="input" />
    </div>
    <script>
      const app = Vue.createApp({
        methods: {
          focusInput() {
            this.$refs.input.focus();
          }
        },
        mounted() {
          this.focusInput();
        }
      });

      app.mount("#app");
    </script>
  </body>
</html>

to focus an input when the Vue instance mounts.

We assigned the input ref to our input.

Then we called focus on it to focus on the input.

We can also add a ref to component itself and use that to call focusInput .

For instance, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      <custom-input ref="customInput"></custom-input>
    </div>
    <script>
      const app = Vue.createApp({
        mounted() {
          this.$refs.customInput.focusInput();
        }
      });

      app.component("custom-input", {
        methods: {
          focusInput() {
            this.$refs.input.focus();
          }
        },
        template: `<input ref='input' />`
      });

      app.mount("#app");
    </script>
  </body>
</html>

We put our input into the custom-input component.

And we have the focusInput method that calls focus on the input.

We assigned a ref to the custom-input component so that we can call the focusInput method of it.

And so we get the same result as we did before.

$refs are only populated after the component is rendered.

It’s the last resort for direct child component manipulation.

We should avoid accessing refs from templates and computed properties.

Edge Cases

There’re edge cases in Vue 3 that aren’t addressed by the usual constructs.

Controlling Updates

Usually, we rely on Vue’s reactivity system to update our component.

But sometimes, we may need to control how components are updated.

Vue provides ways to do that.

Forcing Updates

We can force updates by using the $forceUpdate method on a component.

For example, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      <button @click="forceUpdate">update</button>
      <p>{{new Date()}}</p>
    </div>
    <script>
      const app = Vue.createApp({
        methods: {
          forceUpdate() {
            this.$forceUpdate();
          }
        }
      });
      app.mount("#app");
    </script>
  </body>
</html>

to do that.

We have a non-reactive expression in our template, so we can call $forceUpdate to update the component with a new date.

Create Static Components with v-once

We can create static component with the v-once directive.

It makes the content inside the element with the directive only update once.

For instance, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      <div v-once>
        hello world
      </div>
    </div>
    <script>
      const app = Vue.createApp({});

      app.mount("#app");
    </script>
  </body>
</html>

then Vue will never try to update the component once it’s rendered once.

Conclusion

Template refs let us access component methods and DOM elements directly.

We can also force updates with the $forceUpdate method and the v-once directive to display static content.

Categories
Vue 3

Vue 3 — JavaScript Transitions

Vue 3 is in beta and it’s subject to change.

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 setting transition durations and creating JavaScript transitions.

Transition Durations

We can set transition durations with the duration prop.

For example, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
    <style>
      .bounce-enter-active {
        animation: bounce-in 1.5s;
      }
      .bounce-leave-active {
        animation: bounce-in 1.5s reverse;
      }
      @keyframes bounce-in {
        0% {
          transform: scale(0);
        }
        50% {
          transform: scale(1.5);
        }
        100% {
          transform: scale(1);
        }
      }
    </style>
  </head>
  <body>
    <div id="app">
      <button @click="show = !show">
        Toggle
      </button>

      <transition name="bounce" :duration="1000">
        <p v-if="show">hello</p>
      </transition>
    </div>
    <script>
      const app = Vue.createApp({
        data() {
          return {
            show: false
          };
        }
      });
      app.mount("#app");
    </script>
  </body>
</html>

We just add the duration prop to the transition component to set the duration.

The duration is in milliseconds.

We can also set separate values for enter and leave transitions:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
    <style>
      .bounce-enter-active {
        animation: bounce-in 1.5s;
      }
      .bounce-leave-active {
        animation: bounce-in 1.5s reverse;
      }
      [@keyframes](http://twitter.com/keyframes "Twitter profile for @keyframes") bounce-in {
        0% {
          transform: scale(0);
        }
        50% {
          transform: scale(1.5);
        }
        100% {
          transform: scale(1);
        }
      }
    </style>
  </head>
  <body>
    <div id="app">
      <button @click="show = !show">
        Toggle
      </button>

      <transition name="bounce" :duration="{ enter: 500, leave: 1000 }">
        <p v-if="show">hello</p>
      </transition>
    </div>
    <script>
      const app = Vue.createApp({
        data() {
          return {
            show: false
          };
        }
      });
      app.mount("#app");
    </script>
  </body>
</html>

We have the enter and leave properties again in milliseconds to set the duration of each effect.

JavaScript Hooks

There’re JavaScript hooks for each stage of a transition.

They include:

<transition
  @before-enter="beforeEnter"
  @enter="enter"
  @after-enter="afterEnter"
  @enter-cancelled="enterCancelled"
  @before-leave="beforeLeave"
  @leave="leave"
  @after-leave="afterLeave"
  @leave-cancelled="leaveCancelled"
  :css="false"
>
  <!-- ... -->
</transition>

css set to false means we use JavaScript to apply the transition effects.

And the hooks are in the methods property of the component as usual.

The signatures of the are:

methods: {
  beforeEnter(el) {
    // ...
  },

  enter(el, done) {
    // ...
    done()
  },

  afterEnter(el) {
    // ...
  },

  enterCancelled(el) {
    // ...
  },

  beforeLeave(el) {
    // ...
  },

  leave(el, done) {
    // ...
    done()
  },

  afterLeave(el) {
    // ...
  },

  leaveCancelled(el) {
    // ...
  }
}

el is the element and done is a function to indicate that the transition is done.

We can use the Greensock library to make JavaScript transitions.

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.3.4/gsap.min.js"></script>
  </head>
  <body>
    <div id="app">
      <button @click="show = !show">
        Toggle
      </button>

      <transition
        @before-enter="beforeEnter"
        @enter="enter"
        @leave="leave"
        :css="false"
      >
        <p v-if="show">
          hello world
        </p>
      </transition>
    </div>
    <script>
      const app = Vue.createApp({
        data() {
          return {
            show: false
          };
        },
        methods: {
          beforeEnter(el) {
            gsap.set(el, {
              scaleX: 0.9,
              scaleY: 1.6
            });
          },
          enter(el, done) {
            gsap.to(el, {
              duration: 1,
              scaleX: 1.2,
              scaleY: 0.9,
              opacity: 1,
              x: 150,
              ease: "elastic.inOut(2.8, 1)",
              onComplete: done
            });
          },
          leave(el, done) {
            gsap.to(el, {
              duration: 0.5,
              scaleX: 1,
              scaleY: 1,
              x: 300,
              ease: "elastic.inOut(2.5, 1)"
            });
            gsap.to(el, {
              duration: 0.2,
              delay: 0.5,
              opacity: 0,
              onComplete: done
            });
          }
        }
      });
      app.mount("#app");
    </script>
  </body>
</html>

to create our animation with Greensock.

We added Greensock library with a script tag.

Then we call the gsap.set method to set the initial scale of our element before we start the animation.

The enter method has our enter transition effect.

The duration is set to 1 for 1 second

scaleX and scaleY change the sizing of our p element.

opacity changes the opacity.

x is the translation distance to the right in pixels.

ease is the easing effect.

onComplete is a callback which we indicate that the transition is done.

We have similar things in the leave method.

We have 2 transition effects instead of one for the leave transition effect.

The methods are set as the values of the corresponding directives in the transition component so we can use it.

Conclusion

We can use JavaScript to create our transitions instead of CSS.

A library like Greensock makes this an easy process.

Categories
Vue 3

Vue 3 — Dynamic and Async Components

Vue 3 is in beta and it’s subject to change.

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 how to add dynamic and async components with Vue 3.

Dynamic Components with keep-alive

The keep-alive component lets us keep the state of the component even if we switched out of them.

For instance, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      <button @click='currentComponent = "tab-home"'>home</button>
      <button @click='currentComponent = "tab-post"'>post</button>
      <button @click='currentComponent = "tab-archive"'>archive</button>
      <keep-alive>
        <component :is="currentComponent"></component>
      </keep-alive>
    </div>
    <script>
      const app = Vue.createApp({
        data() {
          return {
            currentComponent: "tab-home"
          };
        }
      });

      app.component("tab-home", {
        template: `
          <div>
            home
          </div>
        `
      });

      app.component("tab-post", {
        data() {
          return {
            on: false
          };
        },
        template: `
          <div>
            <button @click='on = !on'>toggle</button>
            <p v-if='on'>post</p>
          </div>
        `
      });

      app.component("tab-archive", {
        data() {
          return {
            on: false
          };
        },
        template: `
          <div>
            <button @click='on = !on'>toggle</button>
            <p v-if='on'>archive</p>
          </div>
        `
      });

      app.mount("#app");
    </script>
  </body>
</html>

We have the keep-alive component to cache the state of dynamic components so that when we display it again after leaving it, we keep the same state as before.

So if the on state is true in tab-post or tab-archive , then on will stay true even if we switch back and forth between any component.

Without it, the on state would be reset to the initial state.

Async Components

In larger apps, we may need to divide our app int smaller chunks and only load a component from the server when it’s needed.

We can do that with the Vue.defineAsyncComponent method.

For example, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      <async-comp></async-comp>
    </div>
    <script>
      const app = Vue.createApp({});

      const AsyncComp = Vue.defineAsyncComponent(
        () =>
          new Promise((resolve, reject) => {
            resolve({
              template: "<div>async component</div>"
            });
          })
      );

      app.component("async-comp", AsyncComp);

      app.mount("#app");
    </script>
  </body>
</html>

to define an async component and use it.

We called Vue.defineAsyncComponent with a callback that returns a promise that resolves to a component object.

We can pass the async component returned to the app.component method with the name as the first argument and the async component object as the 2nd argument.

Then we can use it in our parent Vue instance’s template.

We can also import the component from somewhere else.

For example, we can write:

import { defineAsyncComponent } from 'vue'

const AsyncComp = defineAsyncComponent(() =>
  import('./components/AsyncComponent.vue')
)

app.component('async-component', AsyncComp)

import returns a promise that resolves to the Vue component so we achieve the same result.

We can also register the component locally by putting our async component in the components property:

import { createApp, defineAsyncComponent } from 'vue'

createApp({
  // ...
  components: {
    AsyncComponent: defineAsyncComponent(() =>
      import('./components/AsyncComponent.vue')
    )
  }
})

Use with Suspense

Async components are suspensible by default.

That means we can pass it into the Suspenbse component and use it.

To disable suspension, we can set suspensible to false in the async component.

Conclusion

We can define async components to load them asynchronously.

The keep-alive component lets us keep the component’s state while switching between them.