In this tutorial , you will learn how to animate DOM elements using angularjs, which translate into underlying CSS animations but are much simpler to use,

Setting up the project

Let’s take a look at what we need to add to the project for the animation to work. First, we need to install angular-animate, which we will do by leveraging Bower. We will also use Bootstrap, just to get some good styling of the button. Inside the project folder, run the following command in the shell:

bower install --save angular-animate
bower install --save bootstrap

This will install the appropriate bower component, and it will also update bower.json accordingly. Next, we load the JavaScript file in index.html and also name the controller for our Tutorial’s project:

<!DOCTYPE html>
<html lang="en">
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, user-scalable=no">
    <title>Angular Animation</title>
    <link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.css">
    <link rel="stylesheet" href="css/main.css">
<body ng-app="myApp">

<div ng-controller="NgAnimateCtrl">

<!--(if target dev)><!-->
<script src="bower_components/jquery/dist/jquery.js"></script>
<script src="bower_components/angular/angular.js"></script>
<script src="bower_components/angular-animate/angular-animate.js"></script>
<script src="js/app.js"></script>
<script src="js/controllers.js"></script>
<script src="js/filters.js"></script>
<!--(if target dist)>
<script src="js/fun-with-ng-animate.js"></script>

Then, we update controllers.js and we are good to go:

'use strict';
angular.module('myApp.controllers', []).controller('NgAnimateCtrl', function ($scope) {

Going forward, let’s explore it with cool examples. In this first example, we will create a simple to-do list with a text input field to add items to the list. Each new item will then be animated upon entering and also upon checking off the item, which will result in the item fading out. For this to work, we first need a text input box. Modify the NgAnimateCtrl div inside index.html, as follows:

<div ng-controller="NgAnimateCtrl">
    <input ng-model="inputText" />
    <button ng-click="addItem()">add</button>

    <div class="item" ng-repeat="item in items | active"><input type="checkbox" ng-model="item.completed" />&nbsp;
        <span ng-bind="item.text"></span>

Then, we will need a custom active filter, which as the name suggests, filters our items such that only the active (noncompleted) items are shown on the page. For this, we modify filters.js:

'use strict';
angular.module('myApp.filters', [])
    .filter('active', function() {
        return function(items) {
            var filteredItems = [];
            for (var i = 0; i < items.length; i++) {
                if (!items[i].completed) {
            return filteredItems;

Just so the items look a little nicer, we will add some styling in main.css. Modify the file as follows:

body {
    margin: 20px;
    font-family: sans-serif;

.item {
    width: 230px;
    padding: 10px;
    margin: 10px 0;
    background: #cccccc;
    box-shadow: 5px 5px 5px #888888;
    font-weight: bold;

Now, we need to modify controllers.js in order to make the to-do list functional:

'use strict';
angular.module('myApp.controllers', ['myApp.filters'])
    .controller('NgAnimateCtrl', ['$scope', function ($scope) {
        $scope.items = [
            { text: "call mum", completed: false },
            { text: "do laundry", completed: false }
        $scope.inputText = "";
        $scope.addItem = function () {
                text: $scope.inputText,
                completed: false
            $scope.inputText = "";

With these modifications, we have a fully functional to-do list application. Nothing is animated yet though. Let’s change this by defining a CSS-based animation. First, add the following lines to main.css: {
    -webkit-transition: 1.2s linear all;
    transition: 1.2s linear all;
    opacity: 0;
} {
    opacity: 1;
} {
    -webkit-transition: 1.2s linear all;
    transition: 1.2s linear all;
    opacity: 1;
} {
    opacity: 0;

Next, we need to modify the declaration of the app module to actually use the angular-animate module. For this, we modify app.js:

'use strict';
angular.module('myApp', ['myApp.controllers', 'myApp.filters', 'ngAnimate']);

This is all that there is to a simple fade-in / fade-out animation. The following screenshot shows how our application will look during fade-out after you check off the prepare taxes task:

Creating our first animation – a simple to-do list
So, what actually happens in the preceding example? Let’s walk through it step by step:

  1. We load the angular-animate library.
  2. The angular-animate library now attaches certain CSS classes, for example, when elements enter or leave, and detaches the classes again when the animation is done.
  3. These classes are prefixed with ng-, for example, ng-enter for the initial state of the element and for the target state of the animation.
  4. All that we needed to do then was to define these classes in CSS, as we have done in main.css in the case of our to-do list application.
  5. We have defined pairs of these classes for our specific item class:, which defines the initial state of the element, with the duration of how long the animation is supposed to run, and then the class, which defines the target state of the same animation. The initial state has an opacity of 0, making it fully transparent, and the target state has an opacity of 1, making it fully visible. During the animation, the specific property transitions between these two values.
  6. We have defined the transition to be linear, which means that the changes in the value(s) are distributed evenly over the duration of time. This is called an easing function, and it is specified in this line:
    transition: 1.2s linear all;
  7. The transition we have used applies to all the eligible properties that can transition. We can omit this, but explicitly specifying this makes it immediately obvious that there are different transitions for different properties. We will see in the next section how to animate different properties differently.
  8. Note that we have defined the transition time twice, once in prefixed and again in the nonprefixed form. This is because new CSS features first appear in vendor-specific prefixed form before they are generally adopted into the standard. Check to find out more about browser support for this feature (and many others):

Let’s try an other easing function instead. We could try ease, but the difference will not be very noticeable in the case of opacity transitions, much more so when changing the size or position. However, distinct steps are very noticeable:

transition: 1.2s steps(5) all;

In this case, the value transition is not even, but instead takes place in the number of specified, distinct steps, which can give an interesting effect as well.


There are different events/hooks used by various directives that you can learn more about in the documentation.

I have claimed that angularjs assigns these classes automatically, but don’t take my word for it; instead let’s see this in action by opening the web inspector. In Chrome, for example, right-click on an element and choose the inspect element. We will find that at this moment, when no animation is active, the classes assigned to the element are item and ng-scope, as shown in the following screenshot:

Creating our first animation – a simple to-do list
Now, when we mark an item as completed, we will see that the CSS classes we have defined are automatically assigned to the element for the duration of the animation, as shown in the following screenshot:

Creating our first animation – a simple to-do list
This is whatangular-animate does by adding or removing such CSS classes dynamically to enable the animation in angularjs applications.


Increase the duration of a transition when you want to inspect what is actually happening under the hood. In this case, I have increased the duration to 20 seconds instead of one second; otherwise, I would not have been able to observe and capture this behavior: {
    -webkit-transition: 20s linear all;
    transition: 20s linear all;
    opacity: 1;