JavaScript-defined animations
We may have to rely on JavaScript sometimes to perform the animation because of browser compatibility issues with CSS animations/transitions. As you know, the moment we introduce ngAnimate
as a dependency to our application, the $animate
service starts adding/removing the ng-*
classes on/off the elements during the course of the animation, but within 0 ms, in case we do not have the CSS animations in place. This gives us a control over how we want to enable the animation, that is, either using CSS or JavaScript.
Let’s create a JavaScript fallback for the example from the Using animate.css section. Add the following to controllers.js
:
.animation('.item', function() { return { enter: function(element, done) { element.css({'opacity': 0, 'margin-left': '-230px'}); element.animate({'opacity': 1, 'margin-left': 0}, 500, done); }, leave: function(element, className, done) {element.animate({'opacity': 0, 'margin-left': -230}, 500, done); } } });
So, what’s happening here?
- First of all, we’ve used the
animation
service (such as controller) in angularjs to enable class-based animation in JavaScript. - The animation name (
.item
) should begin with a dot, resembling a CSS class definition. - As we are dealing with
ngRepeat
, we have to take care of theenter
andleave
events for this particular example. This is applicable to other directives too, such asngView
,ngInclude
,ngSwitch
, andngIf
. For the remaining directives, such asngClass
,ngShow
,form
, andngModel
, there are theaddClass
andremoveClass
events to consider. - Going ahead with the
enter
event, we mentioned thestart
andend
states. Initially, we want an element to be out of the viewport, with a negative margin, and hidden with an opacity of0
. Moving towards the destination, we reduced the negative margin to0
by animating within the viewport and making it visible. - For the
leave
event, it is reversed. - Note that the
done
callback is extremely important to be triggered after the animation is over. For CSS animations, angularjs calls it internally. The purpose of thedone
callback is to remove theng-*
classes on the element upon the completion of the animation. angularjs may not remove the element (mainly inngRepeat
,ngIf
, andngSwitch
), assuming that the animation is still in progress if we fail to call thedone
method, and may not detach those classes.
However, there is one problem: when a CSS animation is supported by the browser, both the CSS and JS animations will collapse. To fix this, we have to know whether the browser has support for transitions/animations. For this, we’ll use Modernizr, a feature detection library for HTML5/CSS3.
We’ll install Modernizr using the following in the command line:
bower install --save modernizr
This will install the latest version of Modernizr in the bower_components/
directory and then we’ll have to insert it in index.html
:
<head>
<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="bower_components/animate.css/animate.css">
<link rel="stylesheet/less" type="text/css" href="css/main.less">
<script src="bower_components/less/dist/less-1.5.1.js"></script>
<script src="bower_components/modernizr/modernizr.js"></script>
</head>
Next, we’ll slightly update the defined JavaScript animation to seamlessly fall back. Modernizr has a bunch of tests to check browser support for various features. One of them is cssanimations
. Let’s add it, as follows:
.animation('.item', function() { if (Modernizr.cssanimations) return angular.noop; return { enter: function(element, done) { element.css({'opacity': 0, 'margin-left': '-230px'}); element.animate({'opacity': 1, 'margin-left': 0}, 500, done); }, leave: function(element, className, done) { element.animate({'opacity': 0, 'margin-left': -230}, 500, done); } } });
In this case, we do the following steps:
- We first check for the support of
cssanimations
in a browser usingModernizr.cssanimations
—it will returntrue
if there is support; otherwise, it will returnfalse
. - The
animation
service should return an empty object/function if we do not want to overwrite CSS animations. We’ve returnedangular.noop
to save some keystrokes: a built-in method in angularjs that performs no operation, which is similar tofunction() { }
. - If you use the older version of Internet Explorer, that is, version 8 or 9, then you can catch the JavaScript animation in motion.
Now, our animation will work seamlessly in evergreen browsers that support CSS3 animations as well as old browsers with JS fallback.
Wrapping up
Summary
In this Tutorial, we took a look at the animation functionality in angularjs and how easy it is to simply use CSS transitions/animations in order to make elements dance or fall back to JavaScript animations otherwise. First, we created a simple to-do list application with an animated entry and exit of items using a CSS-based animation. In this application, we also created a custom filter to only show the items that are not marked as completed. We looked at what angular-animate
is doing under the hood to facilitate CSS animations. We created motion on the page inside the ng-enter
and ng-leave
phases. We created more interesting motion paths by making use of the bezier-curve
function in CSS. We started using LESS to make our lives easier by introducing variables into CSS and letting us specify values in a central place. We used animate.css
as a simple way of importing rather sophisticated animations. We also created a staggering animation to space out multiple elements being animated simultaneously and learned how angularjs manages to do so. Finally, we set up a fallback for CSS transitions/animations in JavaScript. Also, we trained Modernizr to detect the HTML5/CSS3 feature with simple to-use utility functions. In the next Tutorial, we will be covering data-driven charts.