#2 Defining the CSS Fade Animations
Saturday, June 15, 2019
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.
Parts
- Part 45: Adjusting Shares
- Part 44: Plan Percentages
- Part 43: Home Securities
- Part 42: Updating Plans
- Part 41: Plan Details View
- Part 40: Portfolio Getters
- Part 39: Portfolio Plan
- Part 38: Portfolio Category
- Part 37: Account Securities
- Part 36: Account Transfer
- Part 35: View Account Security
- Part 34: Updating Deposit
- Part 33: View Account Deposit
- Part 32: Display Account Details
- Part 31: Account Getters
- Part 30: Deposits And Securities
- Part 29: Add Accounts Details
- Part 28: Refactoring Accounts
- Part 27: Add Security Models
- Part 26: Edit Security Details
- Part 25: View Security Details
- Part 24: Navigating To Details
- Part 23: Getters Validation
- Part 22: Query Parameters
- Part 21: Tab Entries
- Part 20: Tab Header
- Part 19: List View
- Part 18: Vuex Getters
- Part 17: End Domain Model
- Part 16: Start Domain Model
- Part 15: Pop Routes
- Part 14: Push Routes
- Part 13: Removing Accounts
- Part 12: Vuex (Decorators)
- Part 11: Vuex (Accounts)
- Part 10: The App Bar (Settings)
- Part 9: Remove Consumer Rxjs
- Part 8: The App Bar (Back)
- Part 7: Structuring Our App
- Part 6: Animation Between Views
- Part 5: Navigation Fade
- Part 4: Navigation Requests
- Part 3: Fade Animations (cont.)
- Part 2: Fade Animations
- Part 1: Splash Screen
Common options
- root
- src
- components
- animations
- _animation-options.sass
- animations
- components
- src
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
The fade animation
- root
- src
- components
- animations
- _animation-fade.sass
- animations
- components
- src
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)
All the animations
- root
- src
- components
- animations
- _animation.sass
- animations
- components
- src
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)
Including animations in our project
- root
- src
- App.vue
- src
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>
Back to the splash screen
- root
- src
- components
- TheSplashScreen.vue
- components
- src
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>
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>
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>
At last an animation
- root
- src
- components
- animations
- AnimatableItem.vue
- animations
- components
- src
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>
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>