Advertisement

#2 Defining the CSS Fade Animations

In this article we will define our first set of animations using sass. We will do this in a very modulare way so that we will have the ability to import either all of the animations that we will create or just the few that we want to use at a time. Once the animations are defined we will then import them into our project and modify our splash screen and animatable item so that our splash screen will fade out after a set amount of time.

Common options

  • root
    • src
      • components
        • animations
          • _animation-options.sass

We will start by creating a new file that we can import into all of our different animations that will contain the common options that we usually apply (a).

_animation-options.sass

@mixin animation-options($duration: 200ms, $easing: ease-in-out)
    animation-duration: $duration
    animation-fill-mode: forwards
    animation-timing-function: $easing
    overflow: hidden
(a) The common animation options that we want to apply to all of our animations. We have provided the ability for the options to have some default values already set but we can override them if we wish.

The fade animation

  • root
    • src
      • components
        • animations
          • _animation-fade.sass

Now that we have our options specified we will turn to adding our first animation which will take care of fading and element in and/or out (b). We will be writing everything as separate mixins so that if we were to need to we can just import individual pieces.

_animation-fade.sass

@import "animation-options"

@mixin animation-keyframes-fade-in
    @keyframes fadeIn
        from
            opacity: 0
        to 
            opacity: 1

@mixin animation-keyframes-fade-out
    @keyframes fadeOut
        from
            opacity: 1
        to 
            opacity: 0

@mixin animation-keyframes-fade
    @include animation-keyframes-fade-in
    @include animation-keyframes-fade-out

@mixin animation-classes-fade-in($duration: 200ms, $easing: ease-in-out)
    &.fade-in-active
        @include animation-options($duration, $easing)
        animation-name: fadeIn
    &.fade-in-after
        opacity: 1
    &.fade-in-before
        opacity: 0

@mixin animation-classes-fade-out($duration: 200ms, $easing: ease-in-out)
    &.fade-out-active
        @include animation-options($duration, $easing)
        animation-name: fadeOut
    &.fade-out-after
        opacity: 0
    &.fade-out-before
        opacity: 1

@mixin animation-classes-fade($duration: 200ms, $easing: ease-in-out)
    @include animation-classes-fade-in($duration, $easing)
    @include animation-classes-fade-out($duration, $easing)
(b) Our fade animation consists of mixins that contain the keyframes for fading in and out as well as several other mixins for including them and associating them with various css classes.

All the animations

  • root
    • src
      • components
        • animations
          • _animation.sass

For now we just have our fade animations but in time we will be adding more. To make it easier to import them we will also create a way for us to import all of them instead of just one at a time (c).

_animation.sass

@import "animation-fade"

@mixin animation-keyframes
    @include animation-keyframes-fade

@mixin animation-classes($selector, $duration: 200ms, $easing: ease-in-out)
    #{$selector}
        @include animation-classes-fade($duration, $easing)

@mixin animation-package($selector, $duration: 200ms, $easing: ease-in-out)
    @include animation-keyframes
    @include animation-classes($selector, $duration, $easing)
(c) Adding a few mixins that will make it easier to include all of our animations at once instead of one by one.
Advertisement

Including animations in our project

  • root
    • src
      • App.vue

Since we are going to want to animate several different components we can either import the animation styling in each of those components or just once in our app component. We are going to choose the latter (d). While we are at it we will also make sure that our app takes up the entire vertical space by setting its height. In order for our animations to work what ever html element we are trying to animate will also have to have the animatable css class applied to it as well.

App.vue

<style lang="sass">
...
@import "./components/animations/animation"

@include animation-package(".animatable")
    
#app
    ...
    height: 100%
...
</style>
(d) Importing our animation css classes into our app so that we can use them in any of the components within our application.

Back to the splash screen

  • root
    • src
      • components
        • TheSplashScreen.vue

Next up we return to our splash screen and update the template, the class, and the styling. For the template we will just remove the presentation elements and replace them with some elements from the home view (e). While we are at it we are also going to override the default duration of the animation.

TheSplashScreen.vue

<template lang="pug">
AnimatableItem.splash-screen(
    v-bind:duration="'500ms'"
    v-bind:subject="animationSubject")
    img(alt="Vue logo" src="../assets/logo.png")
    p Welcome to Your Vue.js + TypeScript App
</template>
(e) Updating the presentation elements of our template.

Following the changes to our template we will modify our typescript and remove the unused click method and replace it with the lifecycle hook mounted where we will use a call to setTimeout to simulate a long running operation (f).

TheSplashScreen.vue

<script lang="ts">
...
export default class TheSplashScreen extends Vue {
    ...
    private mounted() {
        setTimeout(() => {
            this.animationSubject.next("fade-out");
        }, 2000);
    }
}
</script>
(f) Updating our typescript code to use setTimeout to defer the animation of our splash screen.

Of course the next step is to update our styling a little bit to make sure our splash screen takes up the available space and all of its content is centered among a few other changes (g).

TheSplashScreen.vue

<style lang="sass" scoped>
@import "../bourbon/bourbon"
@import "../bitters/variables"

.splash-screen
    @include position(absolute, 0 0 0 0)
    z-index: 10000
    display: flex
    flex-direction: column
    justify-content: center
    align-items: center
    background-color: $viewport-background-color
</style>
(g) Last up for our splash screen is to adjust its styling.

At last an animation

  • root
    • src
      • components
        • animations
          • AnimatableItem.vue

To make our animation work we just need to return to our animatable item and make just a few changes. First up, as always, are the changes that we need to make to the template. The animations that we want to play are css based so that means we need to modify the css classes of our elements which we will do by using a computed property. We also want the ability to override the duration which we will do by adding a binding to the style attribute and lastly we will need to run code when the animation ends (h).

AnimatableItem.vue

<template lang="pug">
div.animatable(
    v-bind:class="cssClass"
    v-bind:style="{ animationDuration: duration }"
    v-on:animationend.prevent="animationEnd")
    slot
</template>
(h) Updating the template of our animatable item so that we can apply css classes to it, override the duration of the animations and run code when the animation ends.

Last up is updating the script. As mentioned when we updated the template we will use a computed property to apply the css classes which in this case is just a string interpolation of the three possible classes. With that done we just need to modify the animate method and add a method for when the animation ends. In both we will make use of calls to requestAnimationFrame so that we wait just a little bit of time before removing the previous css class (i). For now we are not making use of the string being passed in and we will take care of that in the next article.

AnimatableItem.vue

<script lang="ts">
...
@Component
export default class AnimatableItem extends Vue {
    @Prop({ default: "200ms" }) private readonly duration!: string;
    ...
    private activeCssClass = "";
    private afterCssClass = "";
    private beforeCssClass = "";
        
    private get cssClass() {
        return `${this.beforeCssClass} ${this.activeCssClass} ${this.afterCssClass}`;
    }
    ...
    private animate(animation: string) {
        this.beforeCssClass = "fade-out-before";
        requestAnimationFrame(() => {
            this.activeCssClass = "fade-out-active";
            requestAnimationFrame(() => {
                this.beforeCssClass = "";
            });
        });
    }

    private animationEnd() {
        this.afterCssClass = "fade-out-after";
        requestAnimationFrame(() => {
            this.activeCssClass = "";
        });
    }
}
</script>
(i) Updating the script so that we can apply our css classes at the appropriate time by making use of several calls to requestAnimationFrame.
Exciton Interactive LLC
Advertisement