Create your account

Already have an account? Login here

Create account

Creating Stagger Animations in Angular 4

By Gary simon - Jun 28, 2017

Whether it's a list you're displaying, or you certain elements within your UI, sometimes you want the ability to animate these elements through a sequential fashion. This is where staggering animations come in handy. 

Fortunately, as of Angular 4.2+, we have the new animation-specific stagger() method to use. 

It allows us to specify a duration in milliseconds that designates when each element will begin to animate. In this tutorial, I will walk you through two of the most common use-cases for applying stagger animations in Angular.

If you prefer watching a video..

Be sure to Subscribe to the Official Coursetro Channel for more awesome videos.

Starting a Project

I'm going to assume you already have the prerequisites Nodejs and NPM installed. But you do need to sure you have the latest version of the Angular CLI (Command Line Interface) installed.

To do that, visit your console and type:

$ ng -v

If the output states 'ng' is an unrecognized command, or the @angular/core version is less than 4.2.x, then you need to run the following command:

$ npm install @angular/cli@latest -g

Great! Now, let's use the CLI to start our new project:

$ ng new stagger --style=scss
$ cd stagger

After installation and hopping into the new project folder, we need to install the animations package:

$ npm install @angular/animations@latest --save

After that's done, run the serve command so we can revert back to the browser as we're creating our UI and animations:

$ ng serve

The final step requires importing the BrowserAnimationsModule to our /src/app/app.module.ts file:

// other imports
import {BrowserAnimationsModule} from "@angular/platform-browser/animations";

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    BrowserAnimationsModule // Added here
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Now we're ready to begin coding!

Creating a Staggered Animation on a List

We're going to create a simple array within our component file. Ordinarily, this would come from an API of some sort, but just to keep things quick and easily, we'll hardcode an array.

Hop into the /src/app/app.component.ts file and head down to the class section. Add this:

export class AppComponent {
  items = [];

  constructor() {
    this.items = ['Hey this is an item', 'Here is another one','This is awesome'];
  }

  pushItem() {
    this.items.push('Oh yeah that is awesome');
  }
  removeItem() {
    this.items.pop();
  }
}

Fairly simple. In the constructor, we define a simple array, and then create two methods for adding and removing items from our list. 

In /src/app/app.component.html add the following HTML:

  <div id="container" [@listAnimation]="items.length">

    <button (click)="pushItem()">Add item</button> 
    <button (click)="removeItem()">Remove item</button>
    
    <div id="list" *ngFor="let item of items">
      {{ item }}
    </div>

  </div>

Notice [@listAnimation]='items.length'. In a bit, we'll create an animation named listAnimation, and we're binding it to the length of the items object.

Then we're defining some buttons tied to our methods, and using *ngFor to iterate through our list as usual.

Before we head back to our component file, let's define some CSS in /src/app/app.component.scss:

#container {
    padding:2em;

    #list {
        padding:1em;
        display:block;
        background:#fff;
        margin-bottom:10px;
    }

    button {
        border:none;
        margin-bottom:20px;
        padding:10px;
        background: blueviolet;
        color: #fff;
    }

    .col {
        width:33%;
        display:inline-block;
        padding:40px 0;
        color:gray;
    }
}

Also add a ruleset in /src/styles.scss:

body {
    background: #ececec;
    padding:4em;
    font-family:arial;
}

Now if you save all of the files we just modified, you'll see that we have our UI without any animation. So, let's set that up now.

Head back to the /src/app/app.component.ts file and add the following to the imports at the top:

import { Component } from '@angular/core';
import { trigger,style,transition,animate,keyframes,query,stagger } from '@angular/animations';

In the @Component decorator, let's add the following:

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
  animations: [

    trigger('listAnimation', [
      transition('* => *', [

        query(':enter', style({ opacity: 0 }), {optional: true}),

        query(':enter', stagger('300ms', [
          animate('1s ease-in', keyframes([
            style({opacity: 0, transform: 'translateY(-75%)', offset: 0}),
            style({opacity: .5, transform: 'translateY(35px)',  offset: 0.3}),
            style({opacity: 1, transform: 'translateY(0)',     offset: 1.0}),
          ]))]), {optional: true})
      ])
    ])

  ]
})

First, we define a trigger, and then a transition of * => *, meaning, any state to any state.

Inside, we use the query method to grab all of the elements (in our case, it's the #list div's), and on :enter ,(when they enter the DOM),we first set the styles to a 0 opacity.

This allows us to hide them initially, and we also pass in {optional: true}, otherwise later on when we remove items, the app will error if it finds no items.

We have a second query call after we've hidden the initial items, where we call stagger for a duration of 300ms, and then pass in our normal animate and keyframes functions to create a sequence-based animation.

Now, if take a look at the browser, you will see the list items animate based on our stagger!

If you click Add Item, it will animate as intended. Remove Item does not yet work, because we have to define a third query with the :leave value. 

Let's do that now, add a comma after the last query statement: 

,
        query(':leave', stagger('300ms', [
          animate('1s ease-in', keyframes([
            style({opacity: 1, transform: 'translateY(0)', offset: 0}),
            style({opacity: .5, transform: 'translateY(35px)',  offset: 0.3}),
            style({opacity: 0, transform: 'translateY(-75%)',     offset: 1.0}),
          ]))]), {optional: true})

Here, I'm simply reversing the animation styles going from an opacity of 1, and reversing the order of the translateY values.

Save it, and then click Remove Item. It should not work in the reverse order.

Adding Stagger Animations to UI Elements

Apart from adding stagger animations to iterables, you may also want to stagger the animation of various UI elements. This could be anything from headings & subheadings, to graphics or panels of content.

In our case, we're going to add staggered animation to three paragraph elements.

Paste the following inside of the /src/app/app.component.html file:

  <div id="container" [@listAnimation]="items.length">

    <button (click)="pushItem()">Add item</button> 
    <button (click)="remvoeItem()">Remove item</button>
    
    <div id="list" *ngFor="let item of items">
      {{ item }}
    </div>

    <div @explainerAnim>
      <div class="col">
        <p>I think Coursetro is one of the best learning resources online for Angular</p>
      </div>
      <div class="col">
        <p>I think Coursetro is one of the best learning resources online for Angular</p>
      </div>
      <div class="col">
        <p>I think Coursetro is one of the best learning resources online for Angular</p>
      </div>
    <div>
  </div>

Notice we have @explainerAnim? This is the animation trigger we will use to access the child elements of .col and animate them in as needed.

Inside of app.component.ts and underneath our listAnimation trigger, let's add a new trigger:

,

    trigger('explainerAnim', [
      transition('* => *', [
        query('.col', style({ opacity: 0, transform: 'translateX(-40px)' })),

        query('.col', stagger('500ms', [
          animate('800ms 1.2s ease-out', style({ opacity: 1, transform: 'translateX(0)' })),
        ])),

        query('.col', [
          animate(1000, style('*'))
        ])
        
      ])
    ])

 First, we define a transition and then reference the query class three times.

The first time is to set all .col elements inside of the parent container to an opacity of 0, and move each over -40px to the left.

Then, we use the stagger method with a 500ms separation between each subsequent animation, and animate the opacity to 1 and the horizontal position to 0.

Finally, we reset the state of each element. You can try removing the first and last query (either/or) to see how they're both needed in order for the animation to work correctly.

Conclusion

As you can see, we're now provided with a tremendous amount of control over how we can apply stagger animations in Angular 4.2+. 

I know I will certainly use stagger in my upcoming projects!


Share this post




Say something about this awesome post!