Last update on Wednesday, July 12th 2017

The Complete Guide To Animations With Ionic

When building a mobile application, the focus is on the features.
Once everything is working, the next step is improving the UX and that's where animations arrive.
This process must be done properly, otherwise it can mess up the application and create some bugs like not showing or obfuscating some important elements.

This tutorial will cover everything about animations in an Ionic mobile application.

We will go through:

  1. CSS animations
  2. Angular Animations
  3. Page Animations
  4. Custom Page Animations

Good Old CSS

As you know Ionic developers have the superpower to use HTML, JS and CSS to build our applications, which allows us to use the CSS animation mechanics!

So let's start our project:

ionic start ionic-animations blank

But first a good old wikipedia definition:

A key frame in animation and filmmaking is a drawing that defines the starting and ending points of any smooth transition. The drawings are called "frames" because their position in time is measured in frames on a strip of film.

Animation is all about timing, a bit like a movie, first the introduction (state A) then some development (state B) and the concluding part (state C).

Let's head to the home.scss file to create the first animation:

@keyframes fadeIn {
  from {
    opacity: 0;
  }

  to {
    opacity: 1;
  }
}

This is the fadeIn animation, it goes from state A (transparent) to state B (opaque).
Following animate.css naming convention, which is one of (if not) the best CSS animation library, this animation is stocked in a new CSS Class that we will use later:

.fadeIn {
  animation-name: fadeIn;
  animation-duration: 4s;
}

The animation duration is set to 4 seconds so we can see how it's occurring.

Great, we can now go to the home.html file to use it:

<ion-content padding>
  <div class="fadeIn">Fade in</div>
</ion-content>

Result:

ionic animations fade in normal

The element is gradually shown.

Let's now influence how the animation occurs:

@keyframes fivePhasesFadeIn {
  0% {
    opacity: 1;
  }
  25% {
    opacity: 0.25;
  }
  50% {
    opacity: 0.5;
  }
  75% {
    opacity: 0.75;
  }
  100% {
    opacity: 1;
  }
}

.fivePhasesFadeIn {
  animation-name: fivePhasesFadeIn;
  animation-duration: 4s;
}

This time we are in control and start by showing the element first, then every 25% the opacity will increase until completion.

Result:

ionic animations fade in sequential

Angular Animations

Ionic uses Angular and Angular does have its own animation system so we can use it!

Before going further, a polyfill is required because iOS doesn't like the Web Animation API and that's what Angular uses.

This file must be installed first:

npm install web-animations-js --save

In the src/app folder, the polyfills.ts will be placed with this content:

import "web-animations-js/web-animations.min";

And the main.ts file will import it:

import { platformBrowserDynamic } from "@angular/platform-browser-dynamic";
import "./polyfills";

import { AppModule } from "./app.module";

platformBrowserDynamic().bootstrapModule(AppModule);

Ok, we are good to go.

The philosophy is to modify a DOM Element's style according to a certain state.

Let's jump in the home.html file:

  <div [@elementState]="state">
    My state changes
  </div>

  <button ion-button (click)="makeOpaque()">
    Make it opaque
  </button>

  <button ion-button (click)="makeTransparent()">
    Make it transparent
  </button>

We have one div which has a State attached to it, the name of this state is elementState and its value will change according to the state property.
The two buttons will trigger methods that will change this state property.

Those methods are in the home.ts file:

export class HomePage {
  state = "transparent";

  makeOpaque() {
    this.state = "opaque";
  }

  makeTransparent() {
    this.state = "transparent";
  }
}

The last touch is creating the animations. This is done at the Component's level.

We start with some imports from the @angular/animations library:

import {
  trigger,
  state,
  style,
  animate,
  transition
} from "@angular/animations";

And create the animation:

@Component({
  selector: 'page-home',
  templateUrl: 'home.html',
  animations: [
    trigger('elementState', [
      state('opaque', style({
        opacity: 1
      })),
      state('transparent', style({
        opacity: 0
      })),
      transition('opaque => transparent', animate('4000ms ease-in')),
      transition('transparent => opaque', animate('4000ms ease-out'))
    ])
  ]
})

The animation matches a TriggerName, in our case it's called elementState.
It will be triggered according to the State's changes ('opaque' or 'transparent') and change the style of the DOM element.
The transition between each state will take 4 seconds.

Result:

ionic animations fade in states

Page Animation

Transitioning from one page to another one can also profit from animations and shouldn't be bland.

A new Page is now required. We will add the about folder in the pages folder.

First the about.html file:

<ion-header>
  <ion-navbar>
    <ion-title>
      Ionic Animations
    </ion-title>
  </ion-navbar>
</ion-header>

<ion-content padding>
  This is the about page
</ion-content>

Then the about.ts file:

import { Component } from '@angular/core';
import { NavController } from 'ionic-angular';

@Component({
  selector: 'page-about',
  templateUrl: 'about.html'
})
export class AboutPage {

  constructor(public navCtrl: NavController) {

  }


}

And the declaration in the app.module.ts file:

import { AboutPage } from '../pages/about/about';

@NgModule({
  declarations: [
    MyApp,
    HomePage,
    AboutPage
  ]
  .
  .
  .
  entryComponents: [
    MyApp,
    HomePage,
    AboutPage
  ]
})

All we need is one button in the home.html file:

  <button ion-button (click)="goToAbout()">
    Go to the about page
  </button>

This button will trigger the navigation to the About Page.

Some modifications to the home.ts file:

import { AboutPage } from "../about/about";
import { NavController } from 'ionic-angular';

export class HomePage {

  constructor(public navCtrl: NavController) {

  }
  .
  .
  .

  goToAbout() {
    const animationsOptions = {
      animation: 'ios-transition',
      duration: 1000
    }

    this.navCtrl.push(AboutPage, {}, animationsOptions);
  }
}

The AboutPage is first imported then the NavController is acquired and stocked in the navCtrl property.
The goToAbout method is where the work is done, the push method from the navCtrl property will move us to the AboutPage, the second parameter is here to pass some parameters to this page, we don’t need any so an empty object is good enough.
Finally the animationsOptions, our bread and butter!

The most important property here is animation.

By default only three values work here:

  1. md-transition: For Android’s Material Design
  2. ios-transition: For iOS
  3. wp-transition: For Windows phones

Which gives us the following results:

Android
ionic animations md-transitioniOS
Android
ionic animations ios-transition
Windows Phone
ionic animations wp-transition

Those are the default animations, but we can create our own!

Custom Page Animation

I created mine by updating the one from this github issue. In case you want more inspiration, Ionic’s core transitions are available there.

The new transition must be declared in the app.module.ts file:

import { FadeTransition } from './transitions/fade-transition';
import { Config } from 'ionic-angular';

.
.
.

export class AppModule {
  constructor(public config: Config) {
    this.config.setTransition('fade-transition', FadeTransition);
  }
}

Nothing special here, the Config Service is imported and the setTransition method is used to declare a new transition named ‘fade-transition’.

Let’s create the fade-transition.ts file in a new transitions folder:

import { Animation } from "ionic-angular/animations/animation";
import { isPresent } from "ionic-angular/util/util";
import { PageTransition } from "ionic-angular/transitions/page-transition";

const DURATION = 500;
const OPACITY = "opacity";
const TRANSPARENT = 0;
const OPAQUE = 1;
const SHOW_BACK_BTN_CSS = "show-back-button";

export class FadeTransition extends PageTransition {}

Starting with some imports and constants.
PageTransition is the Mother Class that has everything related to Transitions and by extending it we save some time.
The Animation class will be used to create the animations and isPresent will help us for some verifications.

Building a new Page Transition is not that easy because there are many cases to handle and the code can quickly become heavy.

The rest of the code only handles one specific case: the back button presence.

It’s all located in the init hook:

 init() {
    super.init();

    const plt = this.plt;
    const enteringView = this.enteringView;
    const opts = this.opts;

    this.duration(isPresent(opts.duration) ? opts.duration : DURATION);
    .
    .
    .
}

Some constants are set first:

  1. The enteringView (About)
  2. The options that are received from the push method

Special mention to the duration verification, if we pass a duration parameter to the push method, this duration will override the default duration (which last 500 ms).

    if (enteringView) {
      const enteringPageEle: Element = enteringView.pageRef().nativeElement;

      const enteringContent = new Animation(this.plt, enteringView.pageRef());
      this.add(enteringContent);

      enteringContent
        .fromTo(OPACITY, TRANSPARENT, OPAQUE, true);
    .
    .
    .
}

When we enter a new View, we grab the whole AboutPage and stock it in the enteringPageEle constant. This constant will help us later to acquire some DOM Elements from the page.
A new Animation is created using a reference from this View and added to the Transition object.
Finally the most important part, using the fromTo method we modify the opacity.

Remember what I said at the beginning about Elements not showing if the animations are badly coded? Well, the back button visibility is handled by the Transition (which is a bit odd) and if we don’t act, this button won’t be shown.

The last part of the code is inspired of Ionic iOS core Transition:

if (enteringView.enableBack()) {
  const enteringNavbarEle = enteringPageEle.querySelector("ion-navbar");

  const enteringBackButton = new Animation(
    plt,
    enteringNavbarEle.querySelector(".back-button")
  );

  this.add(enteringBackButton);

  enteringBackButton.beforeAddClass(SHOW_BACK_BTN_CSS);
}

If the back feature is available on the page, we grab the back button from the navigation bar and create an animation that will be added to the transition, finally a new CSS class is added to show the button.

Result:

ionic animations fade-transition

This transition is just a concise example, from here I’ll let you have some fun and handle every cases (ahah there are so many).

Conclusion

Animations are great and require a bit of time to master. No need to rush, it’s just like coding, one step at a time.
You can use CSS libraries like animate.css or an Angular approach with states management.
Custom Page Transitions require more time and precision because many cases need to be handled at the same time, you can create your own using Page Transition with those ones as example or use the one from other plugins like Ionic Native Transition or the official Native Page Transition if you don’t have any specific requirements.

Introduction to Angular 2 Animations

Parallel Animations in an AR Ionic App using Wikitude

Introduction to Angular 2 Observables

Stay up to date


Join over 4000 other developers who already receive my tutorials and their source code out of the oven + other free stuff!