Animating Angular
@NickSeegmiller
About Me
Born and raised in Utah
CS Degree from University of Utah
Married 10 years with 8 year old daughter
Perl web development for Backcountry.com
C/C++ game development for Sensory Sweep
C/Java public safety applications for Spillman Technologies
Angular + Android home automation app development for Vivint
Overview
- History
- Getting started
- CSS based animations
- JS based animations
- Mixing CSS and JS animations
- "Pick best" between CSS and JS animations
ngAnimate history
Initial "unstable" support added in 1.1.4 quantum-manipulation (Apr 2013)
Required ng-animate directive that you passed animation classes to
"Proper" support in 1.2.0 timely-delivery (Nov 2013)
Current version we know and love
"Big" changes coming in 1.4
Getting started
angular.module('angular-examples', ['ngRoute', 'ngAnimate']);
<script src="<cdn-path>/angularjs/1.3.14/angular.js"></script>
<script src="<cdn-path>/angularjs/1.3.14/angular-route.min.js"></script>
<script src="<cdn-path>/angularjs/1.3.14/angular-animate.min.js"></script>
app.js
index.html
Structural transitions
- ngIf
- ngView
- ngInclude
- ngSwitch
- ngRepeat (& move)
- ngMessage
enter & leave
HTML
<div ng-if="ngif" class="example-block fade-in-out">
<img src="img/plainDoge.jpg">
<h1>ng-if</h1>
</div>
ng-if
<div ng-include="nginclude" class="fade-in-out"></div>
ng-include
<div ng-view class="full-height no-overflow fade-in-out"></div>
ng-view
etc
Transitions (pure CSS)
// Fade in and out done with pure CSS transitions
.fade-in-out.ng-enter, .fade-in-out.ng-leave {
-webkit-transition: 0.5s linear all;
transition: 0.5s linear all;
}
.fade-in-out.ng-enter {
opacity: 0;
}
.fade-in-out.ng-leave {
opacity: 1;
}
.fade-in-out.ng-enter.ng-enter-active {
opacity: 1;
}
.fade-in-out.ng-leave.ng-leave-active {
opacity: 0;
}
Transitions (Compass/SCSS)
$transition-time: .5s;
.opacity-fade {
&.ng-enter {
opacity: 0;
@include transition(opacity $transition-time);
&.ng-enter-active {
opacity: 1;
}
}
&.ng-leave {
opacity: 1;
@include transition(opacity $transition-time);
&.ng-leave-active {
opacity: 0;
}
}
}
Animations
@keyframes fade-in {
0% { opacity: 0; }
90% { opacity: .5; }
100% { opacity: 1; }
}
@-webkit-keyframes fade-in {
0% { opacity: 0; }
90% { opacity: .5; }
100% { opacity: 1; }
}
@keyframes fade-out {
0% { opacity: 1; }
90% { opacity: .5; }
100% { opacity: 0; }
}
@-webkit-keyframes fade-out {
0% { opacity: 1; }
90% { opacity: .5; }
100% { opacity: 0; }
}
$transition-time: .5s;
.keyframe-fade-in-out {
&.ng-enter {
-webkit-animation: $transition-time fade-in;
animation: $transition-time fade-in;
}
&.ng-leave {
-webkit-animation: $transition-time fade-out;
animation: $transition-time fade-out;
}
}
Gotcha the first - ngView
Both views are in the DOM at the same time
Consider "position: absolute;"
ngView
.opacity-fade-absolute {
&.ng-enter {
opacity: 0;
position: absolute; // Here
@include transition(opacity $transition-time);
&.ng-enter-active {
opacity: 1;
}
}
&.ng-leave {
opacity: 1;
position: absolute; // Here
@include transition(opacity $transition-time);
&.ng-leave-active {
opacity: 0;
}
}
}
ngView - Unique Animations
$scope.go = function (page, transitionClass) {
if (transitionClass !== undefined) {
$rootScope.transitionClass = transitionClass;
}
$location.path(page);
};
<div class="full-height no-overflow {{transitionClass}}" ng-view></div>
index.html
Controller
<li ng-click="go('/other-page', 'opacity-fade-absolute')">Other Page</li>
View
Class-based transitions
- ngClass
- ngShow & ngHide
- form & ngModel
- ngMessages
add & remove
Gotcha the second - ngShow
.opacity-fade {
&.ng-show {
opacity: 0;
@include transition(opacity $transition-time);
&.ng-show-active {
opacity: 1;
}
}
&.ng-hide {
opacity: 1;
@include transition(opacity $transition-time);
&.ng-hide-active {
opacity: 0;
}
}
}
This doesn't work!
ngShow/ngHide
Toggling ng-hide class
.fade-show {
@include transition(opacity $transition-time);
// Removing hide is showing the element
&.ng-hide-remove {
opacity: 0;
&.ng-hide-remove-active {
opacity: 1;
}
}
// Adding hide is hiding the element
&.ng-hide-add {
opacity: 1;
&.ng-hide-add-active {
opacity: 0;
}
}
}
Gotcha the third - .ng-hide
.ng-hide {
display: none !important;
}
.fade-show {
@include transition(opacity $transition-time);
// Removing hide is showing the element
&.ng-hide-remove {
opacity: 0;
display: block !important; // Here
&.ng-hide-remove-active {
opacity: 1;
}
}
// Adding hide is hiding the element
&.ng-hide-add {
opacity: 1;
display: block !important; // Here
&.ng-hide-add-active {
opacity: 0;
}
}
}
ngAnimate with JavaScript
Specifically the GreenSock Animation Platform (GSAP)
- Super fast
- Feature rich & robust
- Works everywhere
- Sequencing
- Pausing animations
- Lightweight
- Good license
http://greensock.com/gsap
Setup
<div ng-if="fade-in" class="example-block fade-in-out-js">
<img src="img/plainDoge.jpg">
<h1>fade in/out js</h1>
</div>
View
angular.module(
'angular-examples',
['angular-examples.animations', 'ngRoute', 'ngAnimate']
)
.constant('TweenMax', TweenMax)
app.js
JavaScript
angular.module('angular-examples.animations')
.animation('.fade-in-out-js', ['TweenMax',
function(TweenMax) {
return {
enter: function(element, done) {
// Some sort of animation
},
leave: function(element, done) {
// Some sort of animation
}
}
}
])
;
Enter & Leave
enter: function(element, done) {
TweenMax.fromTo(
element,
fadeTime,
{opacity: 0},
{opacity: 1, onComplete: done}
);
},
leave: function(element, done) {
TweenMax.to(
element,
fadeTime,
{opacity: 0, onComplete: done}
);
}
Don't forget to call the done function!
Gotcha the fourth - show/hide
show: function(element, done) {
// This is wrong
},
hide: function(element, done) {
// This is wrong
},
add: function(element, done) {
// And this
},
remove: function(element, done) {
// This too
},
ngHideAdd: function(element, done) {
// Nope
},
ngHideRemove: function(element, done) {
// Still nope
},
addClass/removeClass
//animation that can be triggered before the class is added
beforeAddClass: function(element, className, done) { },
//animation that can be triggered after the class is added
addClass: function(element, className, done) { },
//animation that can be triggered before the class is removed
beforeRemoveClass: function(element, className, done) { },
//animation that can be triggered after the class is removed
removeClass: function(element, className, done) { }
Show/Hide
removeClass: function(element, className, done) {
if (className === 'ng-hide') {
element.removeClass('ng-hide');
TweenMax.fromTo(
element,
fadeTime,
{opacity: 0},
{opacity: 1, onComplete: done}
);
}
else {
done();
}
},
beforeAddClass: function(element, className, done) {
if (className === 'ng-hide') {
TweenMax.to(
element,
fadeTime,
{opacity: 0, onComplete: done}
);
}
else {
done();
}
}
Sequencing
enter: function(element, done) {
TweenMax.fromTo(
element,
time,
{opacity: 0},
{opacity: 1, onComplete: function() {
TweenMax.fromTo(
element,
time,
{x: 0, rotationZ: 0},
{x: 200, rotationZ: 90, onComplete: function() {
TweenMax.fromTo(
element,
time,
{y: 0},
{y: 200, onComplete: done}
);
}}
)
}});
},
Lots of properties at once!
enter: function(element, done) {
TweenMax.fromTo(
element,
time,
{
x: 0, y: 0,
scaleX: 1, scaleY: 1,
skewX: 0, skewY: 0,
rotationZ: 0, rotationY: 0
},
{
x: 200, y: 300,
scaleX: 2, scaleY: 3,
skewX: "5deg", skewY: "10deg",
rotationY: 45, rotationZ: 30,
onComplete: done
}
);
},
Mixing the business!
<!-- mix css & js -->
<div ng-if="mixed" class="example-block mixed">
<img src="img/plainDoge.jpg">
<h1>mixed</h1>
</div>
.mixed {
&.ng-enter {
opacity: 0;
@include transition(opacity $transition-time);
&.ng-enter-active {
opacity: 1;
}
}
&.ng-leave {
opacity: 1;
@include transition(opacity $transition-time);
&.ng-leave-active {
opacity: 0;
}
}
}
Mixing - JavaScript
.animation('.mixed', ['TweenMax',
function(TweenMax) {
var transitionTime = 0.5;
return {
enter: function(element, done) {
TweenMax.fromTo(
element,
transitionTime,
{scale: 1, x: 0, y: 0},
{scale: 2, x: element.width(), y: element.height(),
onComplete: done}
);
},
leave: function(element, done) {
TweenMax.to(
element,
transitionTime,
{scale: 1, x: 0, y: 0, onComplete: done}
);
}
}
}
])
Pick the best
$scope.hasTransitions = Modernizr.csstransitions;
Build custom Modernizr
http://modernizr.com/download/
Controller
<div ng-if="pickBest"
class="example-block"
ng-class="{
'fade-in-out': hasTransitions,
'fade-in-out-js': !hasTransitions}"
>
View
Short circuit JS Animation
angular.module('angular-examples.animations')
.animation('.fade-in-out', ['TweenMax', 'Modernizr',
function(TweenMax, Modernizr) {
if (!Modernizr.csstransitions) {
return {
enter: function(element, done) {
// Some sort of animation
},
leave: function(element, done) {
// Some sort of animation
}
}
}
return {};
}
])
;
Then just let CSS animation play
Resources
- https://github.com/nseegmiller/angular-examples/tree/animating-angular
- https://docs.angularjs.org/api/ngAnimate
- http://www.yearofmoo.com/2013/08/remastered-animation-in-angularjs-1-2.html
- http://greensock.com/get-started-js
Thanks!
Animating Angular
By Nick Seegmiller
Animating Angular
When asked about the best thing of Angular, many people are quick to point to its fantastic two-way binding feature. With ngAnimate, it is exceptionally easy to add a little spice to that great feature. I will show everyone how simple it is to get up and running with ngAnimate, demonstrate some of my favorite tips and tricks, and delve into some advanced strategies for combining CSS and JS animations using the GreenSock Animation Platform (GSAP).
- 1,905