Vue Transitions with NativeScript-Vue

The Vue wrapper component 'transition' is one of my favorite features in Vue. It allows us to animate showing and hiding components with 'v…

Vue Transitions with NativeScript-Vue poster

Take control of your career. Build JavaScript mobile apps.

ng atlanta

Catch Dave Coffin, Nathan Walker, and Alex Ziskind at ngAtlanta in February 2020 for an advanced NativeScript with Angular workshop called  Breathe life into mobile UX with solid architecture lessons. You can register now and take your NativeScript skills up a notch.  Register here.

The Vue wrapper component 'transition' is one of my favorite features in Vue. It allows us to animate showing and hiding components with 'v-show' or 'v-if' in an easy and declarative way. Basically, we can do to do something like:


<transition name="fade">
    <label v-if="show" :text="hello" />
</transition>

where fade will refer to CSS classes (such as .fade-enter-active and .fade-leave-active) that will be added and removed when the <Label> component appears and goes away. You can also use <transition> to trigger animation events, as we will see later.


For more details on Vue transitions, check out the official documentation on Vue transitions.


You can use Vue transitions in NativeScript-Vue, but there are some caveats and things to know. This happens because despite NativeScript's amazing efforts, native and web development are still fundamentally different. In this article, I will show you how to use Vue transitions with NativeScript, while showing how to avoid those "gotchas".

The code samples in this article are implemented in this playground, so you can see all this in action. By the way, this the result:



vue transitions



<transition>s with CSS animations

Let's start with the first transition in the sample app. The template is:


<template>
(...)
  <transition name="bounce" appear>
    <Label class="my-label" v-show="show">hello</Label>
  </transition>
(...)
</template>

Notice that this is pretty much the same as you would use a <transition> in the web. We named the transition bounce. The appear tells Vue to animate the component when it first appears on the screen.

A big part of the transition magic happens in the CSS:


.my-label {
    height: 80;
    width: 80;
    color: white;
    (...)
}

.bounce-enter-active {
    animation-name: bounce-in;
    animation-duration: 1s;
    animation-fill-mode: forwards;
    animation-timing-function: ease-in-out;
}

.bounce-leave-active {
    animation-name: bounce-in;
    animation-duration: 0.25s;
    animation-fill-mode: forwards;
    animation-direction: reverse;
    animation-timing-function: ease-in-out;
}

@keyframes bounce-in {
    0% {
        transform: scale(0);
    }

    50% {
        transform: scale(1.2);
    }

    100% {
        transform: scale(1);
    }
}

The <transition> component adds the class .bounce-enter-active to the <Label> when the component appears, and adds the class .bounce-leave-active when the component disappears. The animation, defined in @keyframes bounce-in, is a scale up with a bit of overshoot. Let me add a couple of notes here:

NativeScript seems to trigger the animation twice when adding a class to a component that doesn't have any classes (see this issue). As a workaround, add a class to the animated element (doesn't even need to be a real class).

NativeScript CSS animations have to be completely defined in a single class. We can't, for instance move the common properties animation-name and animation-direction to the .my-label class.

At the time of writing, the animation-direction: reverse property causes the animation to run about 4x slower. While this bug isn't fixed, we reduce the reversed animation-duration to 0.25s (1s/4) and carry on with our lives...



Using animate.css

It is fun to play with animation keyframes, but if you want some ready-to-use effects, animate.css is a great place to find them. This library contains a lots of animations that you can use with NativeScript. In the playground sample we imported the whole animate.css file, but perhaps a better alternative would be to just copy-paste the animations we need.

The template portion turned out like this:


  <transition
    enter-active-class="animated-tada"
    leave-active-class="animated-bounceOutRight"
    appear
  >
    <Label class="my-label" v-if="show">hello</Label>
  </transition>

See that instead of defining the name prop in the <transition>, we are explicitly defining the enter/leave classes. We defined those classes like so:


.animated-tada {
    animation-name: tada;
    animation-duration: 1s;
    animation-fill-mode: forwards;
}

.animated-bounceOutRight {
    animation-name: bounceOutRight;
    animation-duration: 1s;
    animation-fill-mode: forwards;
}

We had to define these classes because, as mentioned earlier, NativeScript requires all animation-* properties to be defined in the same class, and that's not how animate.css defines its classes. Still, we are using the tada and bounceOutRight @keyframe definitions from animate.css.

Also, we had to grab version 3.1 because after that animate.css uses scale3d() instead of scale(), and those are not supported in NativeScript.


<transition>s with JS animations

We can use <transition> with JavaScript animations instead of CSS. As a result, we can leverage NativeScript's imperative animations. This is how we do it in the template:


<transition v-on:enter="enter($refs.label1)" v-on:leave="leave($refs.label1)">
  <Label ref="label1" class="my-label" style="scaleX: 0; scaleY: 0;"
    v-if="show" @loaded="enter($refs.label1)">hello</Label>
</transition>

As you see, <transition> emits events when the component enters and leaves. We implemented the event listeners in the methods section like so:


(...)
enter(view) {
  view.nativeView
    .animate({
      scale: {
        x: 1.2,
        y: 1.2
      },
      duration: 600,
      curve: enums.AnimationCurve.easeIn
    })
    .then(() => {
      view.nativeView.animate({
        scale: {
          x: 1,
          y: 1
        },
        duration: 400,
        curve: enums.AnimationCurve.easeOut
      });
    });
},
leave(view) {
  view.nativeView
    .animate({
      scale: {
        x: 1.2,
        y: 1.2
      },
      duration: 400,
      curve: enums.AnimationCurve.easeIn
    })
    .then(() => {
      view.nativeView.animate({
        scale: {
          x: 0,
          y: 0
        },
        duration: 600,
        curve: enums.AnimationCurve.easeOut
      });
    });
},
(...)

With the NativeScript JS animation library we can craft more complex animations. There are some things to note here too. Firstly, <transition appear> doesn't work. As an alternative, we called the enter function directly in the loaded event so the animation plays when the component is created. We also had to set style="scaleX: 0; scaleY: 0;" because this is the initial (pre-animation) state.



Conclusion

Vue <transition>s are great and I am glad we can use them on NativeScript-Vue. Unfortunately, the path to make them work in NativeScript is much narrower but, if you stick to these samples, you will be able to use them effectively in your project.


Tiago is a NativeScript-Vue contributer and enthusiastic developer. He's also a speaker and community leader.

Did you enjoy this? Share it!

Take control of your career. Build JavaScript mobile apps.