Go back | Day 18

Staggering animations with ngAnimate

After getting a glimpse at how to perform animations with ngAnimate in last week's ng-newsletter entry: AngularJS with ngAnimate, let's expand our knowledge and learn how to supercharge our animations even further. What are we going to learn? Staggering Animations!

AngularJS 1.2 introduces a hidden, but powerful, CSS trick that informs $animate to stagger all successive CSS animations into a curtain-like effect.

It is best to stick to using AngularJS 1.2.4 or higher to make use of the best animation features.

How can we put this to use?

Let's say we have a ngRepeat animation and, instead of everything animating at the same time, we would like there to be a 50 millisecond delay between each enter operation. This would be quite difficult to do on our own using JavaScript with timeout delays, but luckily it is very easy to do using ngAnimate by adding some additional CSS code that the `$animate service understands.

Let's continue off our our code from last week's article and apply some stagger animations to it. What did we have? Just a few simple transition animations for the enter, leave and move events on a list of items rendered by ngRepeat.

/*
 * ngRepeat triggers three animation events: enter, leave and move.
 */
.repeat-animation.ng-enter,
.repeat-animation.ng-leave,
.repeat-animation.ng-move {
  -webkit-transition:0.5s linear all;
  transition:0.5s linear all;
}

/* ending enter and move styling
   (this is what the element will animate from */
.repeat-animation.ng-enter,
.repeat-animation.ng-move { opacity:0; }

/* ending enter and move styling
   (this is what the element will animate towards */
.repeat-animation.ng-enter.ng-enter-active,
.repeat-animation.ng-move.ng-move-active { opacity:1; }

/* starting leave animation */
.repeat-animation.ng-leave { opacity:1; }

/* ending leave animation */
.repeat-animation.ng-leave.ng-leave-active { opacity:0; }

Click here to see the full HTML + CSS code from last week for this example.

Now let's enhance this by adding in a stagger effect to the enter, leave * and *move animations.

.repeat-animation.ng-enter,
.repeat-animation.ng-leave,
.repeat-animation.ng-move {
  -webkit-transition:0.5s linear all;
  transition:0.5s linear all;
}

.repeat-animation.ng-enter-stagger,
.repeat-animation.ng-leave-stagger,
.repeat-animation.ng-move-stagger {
  /* 50ms between each item being animated after the other */
  -webkit-transition-delay:50ms;
  transition-delay:50ms;

  /* this is required here to prevent any CSS inheritance issues */
  -webkit-transition-duration:0;
  transition-duration:0;
}

/* the rest of the CSS code... */

And voila! We have a nice delay gap between each item being entered. To see this demo in action, click on the link below.

http://plnkr.co/edit/YjfxV1p4azbtSBkmBR6m?p=preview

What about CSS3 Keyframe Animations?

The code above was crafted together using CSS transitions, but CSS keyframe animations can also be used to trigger the animation. Let's expand our code from above, change the CSS styling to use keyframe animations instead of transitions.

The keyframe animation code is very similar to our transition code, but instead of using transition we use animation.

.repeat-animation.ng-enter {
  -webkit-animation: enter_animation 0.5s;
  animation: enter_animation 0.5s;
}
.repeat-animation.ng-leave {
  -webkit-animation: leave_animation 0.5s;
  animation: leave_animation 0.5s;
}

@-webkit-keyframes enter_animation {
  from { opacity:0; }
  to { opacity:1; }
}

@keyframes enter_animation {
  from { opacity:0; }
  to { opacity:1; }
}

@-webkit-keyframes leave_animation {
  from { opacity:1; }
  to { opacity:0; }
}

@keyframes leave_animation {
  from { opacity:1; }
  to { opacity:0; }
}

(click here to get learn the basics of ngAnimate with keyframe animations).

So far so good. Now let's enhance that yet again using some stagger CSS code

.repeat-animation.ng-enter-stagger,
.repeat-animation.ng-leave-stagger,
.repeat-animation.ng-move-stagger {
  /* notice how we're using animation instead of transition here */ 
  -webkit-animation-delay:50ms;
  animation-delay:50ms;

  /* yes we still need to do this too */
  -webkit-animation-duration:0;
  animation-duration:0;
}

/* the rest of the CSS code... */

Keep in mind, however, that CSS Keyframe animations are triggered only after the "reflow" operation has run within an animation triggered by ngAnimate. This means that, depending on how many animations are being run in parallel, the element may appear in a pre-animated state for a fraction of a second. Why? Well this is a performance boost that the $animate service uses internally to queue up animations together to combine everything to render under one single reflow operation. So if our animation code starts off with the element being hidden (say opacity:0) then the element may appear as visible before the animation starts. To inform $animate to style the element beforehand, we can just place some extra CSS code into our setup CSS class (the same CSS class where we reference the actual keyframe animation).

.repeat-animation.ng-enter {
  /* pre-reflow animation styling */
  opacity:0;

  /* the animation code itself */
  -webkit-animation: enter_animation 0.5s;
  animation: enter_animation 0.5s;
}

And finally, here's a link to our amazing, stagger-enhanced, CSS keyframe animation code: http://plnkr.co/edit/7W1WomseYatlWCaBrlkY?p=preview.

Ahh this is sweet isn't it?

What directives support this?

Both ngRepeat and ngClass support this, however, any angular directive can support it as well. It isn't actually the directive that triggers this animation, but instead, a stagger animation will be triggered if two or more animations of the same animation event residing within the same element container (the parent element) are issued at the same time. And with ngRepeat this happens automatically.

To get this to work with a directive like ngClass then it is just a matter of triggering a series of CSS class changes on a series of elements all under the same parent class. Please visit the yearofmoo article at the end of this article to view some awesome examples to issue animations using ngClass.

Understanding how this works can help us make stagger effects in our own directive code by using the helpful DOM operations available on the $animate method.

Click here to get a full understanding of how stagger animations are triggered by the $animate service.

But what about JS animations?

Right now staggering animations are not provided within JavaScript animations. This is because the feature itself is very new and young so it's best to see how popular and well this feature plays out on the web before cramming more features into AngularJS.

However, all is not lost, the helpful yearofmoo article below goes into detail about how to piece together a stagger-like animation using JavaScript animations in ngAnimate.

Where can I learn more?

An in-depth article, more examples and an advanced demo repo is available on www.yearofmoo.com in the brand new article titled: Staggering Animations in AngularJS. This article goes into detail on how to make use of staggering animations with ngAnimate, how to create custom directives to hook into staggering animations, and also how to wire them together with the amazing animate.css CSS animation library.

This article was written by Matias Niemela (aka @yearofmoo) of www.yearofmoo.com and edited by Ari Lerner (aka @auser).

Enjoy this snippet?

Check out our book that's heading to print this week at ng-book.com

The 600+ page book is packed full of Angular content written and designed to get you up to speed with Angular from beginner to expert.

Independently published with content just like what you've just read.

Brought to you by the team behind ng-newsletter