Introduction to AngularJS Animations

If you are interested to learn Angularjs Includes

AngularJS provides animated transitions, with help from CSS.

What is an Animation?

An animation is when the transformation of an HTML element gives you an illusion of motion. These animation hooks are set in place to trigger animations during the life cycle of various directives and when triggered, will attempt to perform a CSS Transition, CSS Keyframe Animation or a JavaScript callback Animation (depending on whether an animation is placed on the given directive). Animations can be placed using vanilla CSS by following the naming conventions set in place by AngularJS or with JavaScript code, defined as a factory.

Example:

Check the checkbox to hide the DIV:

<body ng-app="ngAnimate">

Hide the DIV: <input type="checkbox" ng-model="myCheck">

<div ng-hide="myCheck"></div>

</body>

What do I Need?

To make your applications ready for animations, you must include the AngularJS Animate library:

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular-animate.js"></script>

Then you must refer to the ngAnimate module in your application:

<body ng-app="ngAnimate">

Or if your application has a name, add ngAnimate as a dependency in your application module:

Example

<body ng-app="myApp">

<h1>Hide the DIV: <input type="checkbox" ng-model="myCheck"></h1>

<div ng-hide="myCheck"></div>

<script>
var app = angular.module('myApp', ['ngAnimate']);
</script>

What Does ngAnimate Do?

The ngAnimate module adds and removes classes. The ngAnimate module does not animate your HTML elements, but when ngAnimate notice certain events, like hide or show of an HTML element, the element gets some pre-defined classes which can be used to make animations. The directives in AngularJS who add/remove classes are:

  • ng-show
  • ng-hide
  • ng-class
  • ng-view
  • ng-include
  • ng-repeat
  • ng-if
  • ng-switch

The ng-show and ng-hide directives adds or removes a ng-hide class value. The other directives adds a ng-enter class value when they enter the DOM, and a ng-leave attribute when they are removed from the DOM. The ng-repeat directive also adds a ng-move class value when the HTML element changes position.

In addition, during the animation, the HTML element will have a set of class values, which will be removed when the animation has finished. Example: the ng-hide directive will add these class values:

  • ng-animate
  • ng-hide-animate
  • ng-hide-add (if the element will be hidden)
  • ng-hide-remove (if the element will be showed)
  • ng-hide-add-active (if the element will be hidden)
  • ng-hide-remove-active (if the element will be showed)

How they work?

Animations in AngularJS are completely based on CSS classes. As long as you have a CSS class attached to an HTML element within your application, you can apply animations to it. Let’s say for example that we have an HTML template with a repeater like so:

<div ng-repeat="item in items" class="repeated-item">
  {{ item.id }}
</div>

As you can see, the repeated-item class is present on the element that will be repeated and this class will be used as a reference within our application’s CSS and/or JavaScript animation code to tell AngularJS to perform an animation. As ngRepeat does its thing, each time a new item is added into the list, ngRepeat will add an ng-enter class to the element that is being added. When removed it will apply an ng-leave class and when moved around it will apply an ng-move class. Taking a look at the following CSS code, we can see some transition and keyframe animation code set up for each of those events that occur when ngRepeat triggers them:

/*
  We are using CSS transitions for when the enter and move events
  are triggered for the element that has the `repeated-item` class
*/
.repeated-item.ng-enter, .repeated-item.ng-move {
  transition: all 0.5s linear;
  opacity: 0;
}

/*
  `.ng-enter-active` and `.ng-move-active` are where the transition destination
  properties are set so that the animation knows what to animate
*/
.repeated-item.ng-enter.ng-enter-active,
.repeated-item.ng-move.ng-move-active {
  opacity: 1;
}

/*
  We are using CSS keyframe animations for when the `leave` event
  is triggered for the element that has the `repeated-item` class
*/
.repeated-item.ng-leave {
  animation: 0.5s my_animation;
}

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

Animations Using CSS

We can use CSS transitions or CSS animations to animate HTML elements. This tutorial will show you both. To learn more about CSS Animation, study our CSS Transition Tutorial and our CSS Animation Tutorial.

CSS Transitions

CSS transitions allows you to change CSS property values smoothly, from one value to another, over a given duration:

Example:

When the DIV element gets the .ng-hide class, the transition will take 0.5 seconds, and the height will smoothly change from 100px to 0:

<style>
div {
  transition: all linear 0.5s;
  background-color: lightblue;
  height: 100px;
}

.ng-hide {
  height: 0;
}
</style>

CSS Animations

CSS Animations allows you to change CSS property values smoothly, from one value to another, over a given duration:

Example:

When the DIV element gets the .ng-hide class, the myChange animation will run, which will smoothly change the height from 100px to 0:

<style>
@keyframes myChange {
  from {
    height: 100px;
  } to {
    height: 0;
  }
}

div {
  height: 100px;
  background-color: lightblue;
}

div.ng-hide {
  animation: 0.5s myChange;
}
</style>

Class and ngClass animation hooks

AngularJS also pays attention to CSS class changes on elements by triggering the add and remove hooks. This means that if a CSS class is added to or removed from an element then an animation can be executed in between, before the CSS class addition or removal is finalized. (Keep in mind that AngularJS will only be able to capture class changes if an interpolated expression or the ng-class directive is used on the element.)

<p>
  <button ng-click="myCssVar='css-class'">Set</button>
  <button ng-click="myCssVar=''">Clear</button>
  <br>
  <span ng-class="myCssVar">CSS-Animated Text</span>
</p>

Output:

Set Clear
CSS-Animated Text

Although the CSS is a little different than what we saw before, the idea is the same.

Which directives support animations?

A handful of common AngularJS directives support and trigger animation hooks whenever any major event occurs during their life cycle. The table below explains in detail which animation events are triggered:

DirectiveSupported Animations
form / ngFormadd and remove (various classes)
ngAnimateSwapenter and leave
ngClass / {{class}‚Äč}add and remove
ngClassEvenadd and remove
ngClassOddadd and remove
ngHideadd and remove (the ng-hide class)
ngIfenter and leave
ngIncludeenter and leave
ngMessage / ngMessageExpenter and leave
ngMessagesadd and remove (the ng-active/ng-inactive classes)
ngModeladd and remove (various classes)
ngRepeatenter, leave, and move
ngShowadd and remove (the ng-hide class)
ngSwitchenter and leave
ngViewenter and leave

(More information can be found by visiting the documentation associated with each directive.) For a full breakdown of the steps involved during each animation event, refer to the $animate API docs.

How do I use animations in my own directives?

Animations within custom directives can also be established by injecting $animate directly into your directive and making calls to it when needed.

myModule.directive('my-directive', ['$animate', function($animate) {
  return function(scope, element) {
    element.on('click', function() {
      if (element.hasClass('clicked')) {
        $animate.removeClass(element, 'clicked');
      } else {
        $animate.addClass(element, 'clicked');
      }
   

Animations on app bootstrap / page load

By default, animations are disabled when the AngularJS app bootstraps. If you are using the ngApp directive, this happens in the DOM Content Loaded event, so immediately after the page has been loaded. Animations are disabled, so that UI and content are instantly visible. Otherwise, with many animations on the page, the loading process may become too visually overwhelming, and the performance may suffer. Internally, ngAnimate waits until all template downloads that are started right after bootstrap have finished. Then, it waits for the currently running $digest and one more after that, to finish. This ensures that the whole app has been compiled fully before animations are attempted.

If you do want your animations to play when the app bootstraps, you can enable animations globally in your main module’s run function:

myModule.run(function($animate) {
  $animate.enabled(true);
});

How to (selectively) enable, disable and skip animations

There are several different ways to disable animations, both globally and for specific animations. Disabling specific animations can help to speed up the render performance, for example for large ngRepeat lists that don’t actually have animations. Because ngAnimate checks at runtime if animations are present, performance will take a hit even if an element has no animation.

This function can be called during the config phase of an app. It takes a filter function as the only argument, which will then be used to “filter” animations (based on the animated element, the event type, and the animation options). Only when the filter function returns true, will the animation be performed. This allows great flexibility – you can easily create complex rules, such as allowing specific events only or enabling animations on specific subtrees of the DOM, and dynamically modify them, for example disabling animations at certain points in time or under certain circumstances.

app.config(function($animateProvider) {
  $animateProvider.customFilter(function(node, event, options) {
    // Example: Only animate `enter` and `leave` operations.
    return event === 'enter' || event === 'leave';
  });
});

The customFilter approach generally gives a big speed boost compared to other strategies, because the matching is done before other animation disabling strategies are checked .Best Practice: Keep the filtering function as lean as possible, because it will be called for each DOM action (e.g. insertion, removal, class change) performed by “animation-aware” directives. See here for a list of built-in directives that support animations. Performing computationally expensive or time-consuming operations on each call of the filtering function can make your animations sluggish.

This function too can be called during the configure phase of an app. It takes a regex as the only argument, which will then be matched against the classes of any element that is about to be animated. The regex allows a lot of flexibility – you can either allow animations for specific classes only (useful when you are working with 3rd party animations), or exclude specific classes from getting animated.

app.config(function($animateProvider) {
  $animateProvider.classNameFilter(/animate-/);
});
/* prefixed with `animate-` */
.animate-fade-add.animate-fade-add-active {
  transition: all 1s linear;
  opacity: 0;
}

The classNameFilter approach generally gives a big speed boost compared to other strategies, because the matching is done before other animation disabling strategies are checked. However, that also means it is not possible to override class name matching with the two following strategies. It’s of course still possible to enable / disable animations by changing an element’s class name at runtime.

This function can be used to enable / disable animations in two different ways:

With a single boolean argument, it enables / disables animations globally: $animate.enabled(false) disables all animations in your app. When the first argument is a native DOM or jqLite/jQuery element, the function enables / disables animations on this element and all its children$animate.enabled(myElement, false). You can still use it to re-enable animations for a child element, even if you have disabled them on a parent element. And compared to the classNameFilter, you can change the animation status at runtime instead of during the config phase.

Note however that the $animate.enabled() state for individual elements does not overwrite disabling rules that have been set in the classNameFilter.

Via CSS styles: overwriting styles in the ng-animate CSS class

Whenever an animation is started, ngAnimate applies the ng-animate class to the element for the whole duration of the animation. By applying CSS transition / animation styling to that class, you can skip an animation:

.my-class {
  transition: transform 2s;
}

.my-class:hover {
  transform: translateX(50px);
}

my-class.ng-animate {
  transition: 0s;
}

By setting transition: 0sngAnimate will ignore the existing transition styles, and not try to animate them (Javascript animations will still execute, though). This can be used to prevent issues with existing animations interfering with ngAnimate.

Preventing flicker before an animation starts

When nesting elements with structural animations, such as ngIf, into elements that have class-based animations such as ngClass, it sometimes happens that before the actual animation starts, there is a brief flicker or flash of content where the animated element is briefly visible. To prevent this, you can apply styles to the ng-[event]-prepare class, which is added as soon as an animation is initialized, but removed before the actual animation starts (after waiting for a $digest). This class is only added for structural animations (entermove, and leave).

Here’s an example where you might see flickering:

<div ng-class="{red: myProp}">
  <div ng-class="{blue: myProp}">
    <div class="message" ng-if="myProp"></div>
  </div>
</div>

It is possible that during the enter event, the .message div will be briefly visible before it starts animating. In that case, you can add styles to the CSS that make sure the element stays hidden before the animation starts:

.message.ng-enter-prepare {
  opacity: 0;
}

/* Other animation styles ... */

Preventing collisions with existing animations and third-party libraries

By default, any ngAnimate-enabled directives will assume that transition / animation styles on the element are part of an ngAnimate animation. This can lead to problems when the styles are actually for animations that are independent of ngAnimate.

For example, an element acts as a loading spinner. It has an infinite css animation on it, and also an ngIf directive, for which no animations are defined:

.spinner {
  animation: rotating 2s linear infinite;
}

@keyframes rotating {
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
}

Now, when the ngIf expression changes, ngAnimate will see the spinner animation and use it to animate the enter/leave event, which doesn’t work because the animation is infinite. The element will still be added / removed after a timeout, but there will be a noticeable delay. This might also happen because some third-party frameworks place animation duration defaults across many element or className selectors in order to make their code small and reusable.

You can prevent this unwanted behavior by adding CSS to the .ng-animate class, that is added for the whole duration of each animation. Simply overwrite the transition / animation duration. In the case of the spinner, this would be:

.spinner.ng-animate {
  animation: 0s none;
  transition: 0s none;
}

If you do have CSS transitions / animations defined for the animation events, make sure they have a higher priority than any styles that are not related to ngAnimate. You can also use one of the other strategies to disable animations.

Before animating, ngAnimate checks if the animated element is inside the application DOM tree. If not, no animation is run. Usually, this is not a problem since most apps use the html or body elements as their root. Problems arise when the application is bootstrapped on a different element, and animations are attempted on elements that are outside the application tree, e.g. when libraries append popup or modal elements to the body tag. You can use $animate.pin(element, parentHost) to associate an element with another element that belongs to your application. Simply call it before the element is added to the DOM / before the animation starts, with the element you want to animate, and the element which should be its assumed parent.

Introduction to AngularJS Animations
Show Buttons
Hide Buttons