Supercharge your ng code with some high level animations
By: Matias Niemelä
Eight major AngularJS articles.
Great resource to learn about AngularJS.
Solid CSS3 animation browser support
Great JS animations libraries available on the web
Hardware-accelerated animations
DOM APIs are getting better for animations
Difficult to properly sync animations together
CSS animation detection is tricky
Animations are coupled with HTML code
Very little isolation from the rest of the application
ngClass was your best bet
No way to hook into existing ng directives
You can still make your own directives
Too much of your own JS code is required
HTML code may change due to the presence of animations
Introduced in AngularJS 1.1.4
Enhanced in AngularJS 1.1.5
Remastered & Supercharged in 1.2
Exists within a separate module
Supports CSS3 transition/keyframe animations natively
JavaScript callbacks are also supported
Automatically manages all state within animation events
Various existing ng directives are animation aware
Easy way to disable / enable animations
Uses only CSS classes to reference animations
All animation code is isolated
CSS and JS code is plug and play
Handles all sniffing, detection and computations for you
Highly performant
Include angular-animate.js into your application
Set ngAnimate as a module dep
Place a CSS class in your HTML code to reference the animation(s)
Create an animation using CSS or JavaScript using the CSS class name
Two CSS classes required.
/* starting styles */ .my-class.ng-EVENT { transition:0.5s linear all; transform:scale(0.1); } /* destination styles */ .my-class.ng-EVENT.ng-EVENT-active { transform:scale(1.5); }
Only one CSS class required.
/* animation keyframe CSS */ .my-class.ng-EVENT { animation:0.5s my_animation; } /* animation CSS (from & to) */ @keyframes my_animation { from { background:blue; } to { background:red; } }
Checks CSS classes each time on an element
CSS styles can be changed via media queries
An additional CSS stylesheet can be added/removed
Defined just like a service is defined
ngModule.animation('.my-class', function(inject) { return { EVENT : function(element, done) { /* run your animation using your own JS code */ runMyAnimation(element, done); return function(cancelled) { closeMyAnimation(element); }; } }; });
ngAnimate actives the following directives
Directive Events ngRepeat enter, move, leave ngView enter, leave ngSwitch, ngInclude, ngIf enter, leave ngClass addClass, removeClass ngHide, ngShow addClass('.ng-hide') removeClass('.ng-hide')1. A scope change happens and some directive does its thing
2. A DOM operation is called which is handled by the $animate service
3. An extra digest occurs or the animation starts right away
4. Checks for anything disabled
5. Grabs the CSS classes and triggers the animations that match that CSS class
6. CSS transitions / keyframes + JS animations are triggered one by one
7. getComputedStyle -> animate CSS transitions/animations
8. Close the animation -> perform any required DOM operations
Let's use CSS transitions: enter, leave
.view.ng-enter { transition:1s linear all; position:relative; left:100px; opacity:0; } .view.ng-enter.ng-enter-active { opacity:1; left:0px; }
.view.ng-leave { transition:1s linear all; position:relative; opacity:1; left:0px; } .view.ng-leave.ng-leave-active { opacity:0; left:-100px; }
Let's use CSS3 animations: enter, leave, move
<div ng-repeat="item in items" class="repeater"></div>
.repeater.ng-enter { animation: repeater_enter 1s; } @keyframes repeater_enter { from { opacity:0; } to { opacity:1; } }
.repeater.ng-leave { animation: repeater_leave 1s; } @keyframes repeater_leave { from { opacity:1; } to { opacity:0; } }
.repeater.ng-move { animation: repeater_move 1s; } @keyframes repeater_move { from { opacity:1; } to { opacity:0; } }
Let's use JS Animations enter, leave
<div ng-include="tpl" class="slide"></div>
myModule.animation('.slide', function() { return { enter : function(element, done) { $(element) .css({ opacity:0 }) .animate({ opacity:1 }, done); }, //...
leave : function(element, done) { $(element).animate({ opacity:0 }, done); } }; });
Let's use JS Animations ng-hide-remove, ng-hide-add
<div ng-hide="bool" class="show-hide"></div>
.show-hide.ng-hide-add { transition:0.5s linear all; opacity:1; } .show-hide.ng-hide-add .ng-hide-add-active { opacity:0; }
.show-hide.ng-hide-remove { transition:0.5s linear all; opacity:0; } .show-hide.ng-hide-remove .ng-hide-remove-active { opacity:1; }
Used to trigger animations inside directives
Skips all animations when disabled or when ngAnimate is not included
Event class="klass ng-EVENT" enter $animate.enter(element, parent, after, done) leave $animate.leave(element, done) move $animate.move(element, parent, after, done) addClass $animate.addClass(element, className, done) removeClass $animate.removeClass(element, className, done) enabled $animate.enabled(bool)Skips the former animation is a previous animation is called
Skips all child animations when a parent animation is animating
Caches subsequent getComputedStyle lookups to speed animations
Queues all animations into as few reflows as possible
Test to see that an animation gets fired
1. Create a JS animation within your spec
2. Create a fake CSS style and examine the generated CSS classes
it('should animate', function() { var interceptedClass; module(function($animateProvider) { $animateProvider.register('.animated', function() { return { addClass : function(element, className, done) { interceptedClass = className; done(); } }; }); }); inject(function($compile, $rootScope, $rootElement) { var element = $compile('<div class="animated" ng-class="{on:on}"></div>')($rootScope); $rootElement.append(element); $rootScope.on = true; $rootScope.$digest(); expect(interceptedClass).toBe('on'); }); });
Wait for the entire duration of the animation and compare the styles
Use within the tests if possible to avoid async calls
Improve the compatibility of CSS code
Provide a better API for JS animations
Staggering animations in both CSS AND JS
Please use and experiment with animations so that we can make this tool better
Feel free to contact me via matias@yearofmoo.com