• No results found

Creating a simple fade in/out animation

AngularJS animations work by integrating CSS animations into a directive class-based finite state machine. In other words, elements in AngularJS that serve to manipulate the DOM have defined class states that can be used to take full advantage of CSS animations, and the system moves between these states on well-defined events. This recipe will demonstrate how to make use of the directive finite state machine in order to create a simple fade in/out animation.

A finite state machine (FSM) is a computational system model defined by

the states and transition conditions between them. The system can only exist in one state at any given time, and the system changes state when triggered by certain events. In the context of AngularJS animations, states are represented by the presence of CSS classes associated with the progress of a certain animation, and the events that trigger the state transformations are controlled by data binding and the directives controlling the classes.

Getting ready

As of AngularJS 1.2, animation comes as a completely separate module in AngularJS— ngAnimate. Your initial files should appear as follows:

(style.css)

.animated-container { padding: 20px;

border: 5px solid black; } (index.html) <div ng-app="myApp"> <div ng-controller="Ctrl"> <label> <button ng-click="boxHidden=!boxHidden"> Toggle Visibility </button> </label>

<div class="animated-container" ng-hide="boxHidden"> Awesome text! </div> </div> </div> (app.js) angular.module('myApp', ['ngAnimate']) .controller('Ctrl', function($scope) { $scope.boxHidden = true; });

You can see that the given code simply provides a button that instantly toggles the visibility of the styled <div> element.

How to do it…

There are several ways to accomplish a fade in/out animation, but the simplest is to use CSS transitions as they integrate very nicely into the AngularJS animation class state machine. The animation CSS classes need to cover both cases, where the element is hidden and needs to fade in, and where the element is shown and needs to fade out. As is the case with CSS transitions, you need to define the initial state, the final state, and the transition parameters. This can be done as follows:

(style.css)

.animated-container { padding: 20px;

border: 5px solid black; }

.animated-container.ng-hide-add, .animated-container.ng-hide-remove { transition: all linear 1s;

} .animated-container.ng-hide-remove, .animated-container.ng-hide-add.ng-hide-add-active { opacity: 0; } .animated-container.ng-hide-add, .animated-container.ng-hide-remove.ng-hide-remove-active { opacity: 1; } JSFiddle: http://jsfiddle.net/msfrisbie/fqxwvyvj/ These CSS classes cover the bi-directional transition to fade between opacity: 0 and opacity: 1 in 1 second. Clicking on the <button> element to toggle the visibility will work to trigger the fade in and fade out of the styled <div> element.

How it works…

Since CSS transitions are triggered by the change of relevant CSS classes, using the AngularJS class state machine allows you to animate when a directive manipulates the DOM. The show/hide state machine is cyclical and operates as shown in the following table (this is a simplified version of the full ng-show/ng-hide state machine, which is provided in detail in the Creating addClass animations with ngShow recipe):

Event Directive state Styled element classes Element state

Initial state ng- hide=true animated- container ng-hide display:none boxHidden=false ng- hide=false animated- container ng-animate ng-hide-remove opacity:0

Event Directive state Styled element classes Element state

Time quanta elapses ng-

hide=false animated- container ng-animate ng-hide-remove ng-hide-remove- active The animation is triggered; transition to opacity:1 occurs Animation completes ng- hide=false animated- container display:block boxHidden=true ng- hide=true animated- container ng-animate ng-hide ng-hide-add opacity:1

Time quanta elapses ng-

hide=true animated- container ng-animate ng-hide ng-hide-add ng-hide-add- active The animation is triggered; transition to opacity:0 occurs Animation completes ng- hide=true animated- container ng-hide display:none

The state machine shown in the preceding table is a

simplified version of the actual animation state machine.

You can now see how the CSS classes utilize the animation class state machine to trigger the animation. When the directive state changes (in this case, the Boolean is negated), AngularJS applies sequential CSS classes to the element, intending them to be used as anchors for a CSS animation. Here, Time quanta elapses refers to the separate addition of ng-hide-add or ng-hide-remove followed by the ng-hide-add-active or ng-hide- remove-active classes. These classes are added sequentially and separately (this appears to be instantaneous, you will be unable to see the separation when watching the classes in a browser inspector), but the nature of the offset addition causes the CSS transition to be triggered properly.

In the case of moving from hidden to visible, the CSS styling defines a transition between the .animated-container.ng-hide-add selector and the .animated-container.ng- hide-add.ng-hide-add-active selector, with the transition definition attached under the .animated-container.ng-hide-remove selector.

In the case of moving from visible to hidden, the styling defines the opposite transition between the .animated-container.ng-hide-add selector and the .animated-container.ng- hide-add.ng-hide-add-active selector, with the transition definition attached under the .animated-container.ng-hide-add selector.

There's more…

As the class state machine is controlled entirely by the ng-hide directive, if you want to invert the animation (initially start as shown and then make the transition to hidden), all that is needed is the use of ng-show on the HTML element instead of ng-hide. These opposing directives will implement the class state machine appropriately for their definition, but will always use the ng-hide class as the default reference. In other words, using the ng-show directive will not utilize ng-show-add or ng-show-remove or anything of the sort; it will still be ng-hide, ng-hide-add or ng-hide-remove, and ng-hide-add-active or ng-hide-remove-active.

Keeping things clean

Since the animation starts as hidden, and you are loading the JS files at the bottom of the body, this is the perfect opportunity to utilize ng-cloak in order to prevent the styled div element from flashing before compilation. Modify your CSS and HTML as follows:

(style.css)

[ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {

display: none !important; }

(index.html) ...

<div class="animated-container" ng-show="boxHidden" ng-cloak> Awesome text!

</div>

No more boilerplate animation styling

Formerly, when animating ng-hide or ng-show, the display property needed to incorporate display:block!important during the animation states. As of AngularJS 1.3, this is no longer necessary; the ngAnimate module will handle this for you.

See also

f The Creating addClass animations with ngShow and Creating removeClass

animations with ngClass recipes go into further depth with the state machines that drive the directive animations

Replicating jQuery's slideUp() and