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!

Made with Slides.com