|
| 1 | +@ngdoc overview |
| 2 | +@name Developer Guide: Animations |
| 3 | +@description |
| 4 | + |
| 5 | + |
| 6 | +# Animations |
| 7 | + |
| 8 | +AngularJS 1.2 provides animation hooks for common directives such as `ngRepeat`, `ngSwitch`, and `ngView`, as well as custom directives |
| 9 | +via the `$animate` service. These animation hooks are set in place to trigger animations during the life cycle of various directives and when |
| 10 | +triggered, will attempt to perform a CSS Transition, CSS Keyframe Animation or a JavaScript callback Animation (depending on if an animation is |
| 11 | +placed on the given directive). Animations can be placed using vanilla CSS by following the naming conventions set in place by AngularJS |
| 12 | +or with JavaScript code when it's defined as a factory. |
| 13 | + |
| 14 | +Animations are not available unless you include the {@link api/ngAnimate `ngAnimate` module} as a dependency within your application. |
| 15 | + |
| 16 | +Below is a quick example of animations being enabled for `ngShow` and `ngHide`: |
| 17 | + |
| 18 | +<example animations="true"> |
| 19 | + <file name="index.html"> |
| 20 | + <div ng-init="checked=true"> |
| 21 | + <label> |
| 22 | + <input type="checkbox" ng-model="checked" style="float:left; margin-right:10px;"> Is Visible... |
| 23 | + </label> |
| 24 | + <div class="check-element animate-show-hide" ng-show="checked" style="clear:both;"> |
| 25 | + Visible... |
| 26 | + </div> |
| 27 | + </div> |
| 28 | + </file> |
| 29 | + <file name="animations.css"> |
| 30 | + .animate-show-hide { |
| 31 | + padding:10px; |
| 32 | + border:1px solid black; |
| 33 | + background:white; |
| 34 | + } |
| 35 | + |
| 36 | + .animate-show-hide.ng-hide-add, .animate-show-hide.ng-hide-remove { |
| 37 | + -webkit-transition:all linear 0.5s; |
| 38 | + -moz-transition:all linear 0.5s; |
| 39 | + -o-transition:all linear 0.5s; |
| 40 | + transition:all linear 0.5s; |
| 41 | + display:block!important; |
| 42 | + } |
| 43 | + |
| 44 | + .animate-show-hide.ng-hide-add.ng-hide-add-active, |
| 45 | + .animate-show-hide.ng-hide-remove { |
| 46 | + opacity:0; |
| 47 | + } |
| 48 | + |
| 49 | + .animate-show-hide.ng-hide-add, |
| 50 | + .animate-show-hide.ng-hide-remove.ng-hide-remove-active { |
| 51 | + opacity:1; |
| 52 | + } |
| 53 | + </file> |
| 54 | +</example> |
| 55 | + |
| 56 | +## Installation |
| 57 | + |
| 58 | +See the {@link api/ngAnimate API docs for `ngAnimate`} for instructions on installing the module. |
| 59 | + |
| 60 | +You may also want to setup a separate CSS file for defining CSS-based animations. |
| 61 | + |
| 62 | +## How they work |
| 63 | + |
| 64 | +Animations in AngularJS are completely based on CSS classes. As long as you have a CSS class attached to a HTML element within |
| 65 | +your website, you can apply animations to it. Lets say for example that we have an HTML template with a repeater in it like so: |
| 66 | + |
| 67 | +<pre> |
| 68 | +<div ng-repeat="item in items" class="repeated-item"> |
| 69 | + {{ item.id }} |
| 70 | +</div> |
| 71 | +</pre> |
| 72 | + |
| 73 | +As you can see, the `.repeated-item` class is present on the element that will be repeated and this class will be |
| 74 | +used as a reference within our application's CSS and/or JavaScript animation code to tell AngularJS to perform an animation. |
| 75 | + |
| 76 | +As ngRepeat does its thing, each time a new item is added into the list, ngRepeat will add |
| 77 | +a `ng-enter` class name to the element that is being added. When removed it will apply a `ng-leave` class name and when moved around |
| 78 | +it will apply a `ng-move` class name. |
| 79 | + |
| 80 | +Taking a look at the following CSS code, we can see some transition and keyframe animation code set for each of those events that |
| 81 | +occur when ngRepeat triggers them: |
| 82 | + |
| 83 | +<pre> |
| 84 | +/* |
| 85 | + We're using CSS transitions for when |
| 86 | + the enter and move events are triggered |
| 87 | + for the element that has the .repeated-item |
| 88 | + class |
| 89 | +*/ |
| 90 | +.repeated-item.ng-enter, .repeated-item.ng-move { |
| 91 | + -webkit-transition:0.5s linear all; |
| 92 | + -moz-transition:0.5s linear all; |
| 93 | + -o-transition:0.5s linear all; |
| 94 | + transition:0.5s linear all; |
| 95 | + opacity:0; |
| 96 | +} |
| 97 | + |
| 98 | +/* |
| 99 | + The ng-enter-active and ng-move-active |
| 100 | + are where the transition destination properties |
| 101 | + are set so that the animation knows what to |
| 102 | + animate. |
| 103 | +*/ |
| 104 | +.repeated-item.ng-enter.ng-enter-active, |
| 105 | +.repeated-item.ng-move.ng-move-active { |
| 106 | + opacity:1; |
| 107 | +} |
| 108 | + |
| 109 | +/* |
| 110 | + We're using CSS keyframe animations for when |
| 111 | + the leave event is triggered for the element |
| 112 | + that has the .repeated-item class |
| 113 | +*/ |
| 114 | +.repeated-item.ng-leave { |
| 115 | + -webkit-animation:0.5s my_animation; |
| 116 | + -moz-animation:0.5s my_animation; |
| 117 | + -o-animation:0.5s my_animation; |
| 118 | + animation:0.5s my_animation; |
| 119 | +} |
| 120 | + |
| 121 | +@keyframes my_animation { |
| 122 | + from { opacity:1; } |
| 123 | + to { opacity:0; } |
| 124 | +} |
| 125 | + |
| 126 | +/* |
| 127 | + Unfortunately each browser vendor requires |
| 128 | + its own definition of keyframe animation code... |
| 129 | +*/ |
| 130 | +@-webkit-keyframes my_animation { |
| 131 | + from { opacity:1; } |
| 132 | + to { opacity:0; } |
| 133 | +} |
| 134 | + |
| 135 | +@-moz-keyframes my_animation { |
| 136 | + from { opacity:1; } |
| 137 | + to { opacity:0; } |
| 138 | +} |
| 139 | + |
| 140 | +@-o-keyframes my_animation { |
| 141 | + from { opacity:1; } |
| 142 | + to { opacity:0; } |
| 143 | +} |
| 144 | +</pre> |
| 145 | + |
| 146 | +The same approach to animation can be used using JavaScript code (**jQuery is used within to perform animations**): |
| 147 | + |
| 148 | +<pre> |
| 149 | +myModule.animation('.repeated-item', function() { |
| 150 | + return { |
| 151 | + enter : function(element, done) { |
| 152 | + element.css('opacity',0); |
| 153 | + jQuery(element).animate({ |
| 154 | + opacity: 1 |
| 155 | + }, done); |
| 156 | + |
| 157 | + // optional onDone or onCancel callback |
| 158 | + // function to handle any post-animation |
| 159 | + // cleanup operations |
| 160 | + return function(isCancelled) { |
| 161 | + if(isCancelled) { |
| 162 | + jQuery(element).stop(); |
| 163 | + } |
| 164 | + } |
| 165 | + }, |
| 166 | + leave : function(element, done) { |
| 167 | + element.css('opacity', 1); |
| 168 | + jQuery(element).animate({ |
| 169 | + opacity: 0 |
| 170 | + }, done); |
| 171 | + |
| 172 | + // optional onDone or onCancel callback |
| 173 | + // function to handle any post-animation |
| 174 | + // cleanup operations |
| 175 | + return function(isCancelled) { |
| 176 | + if(isCancelled) { |
| 177 | + jQuery(element).stop(); |
| 178 | + } |
| 179 | + } |
| 180 | + }, |
| 181 | + move : function(element, done) { |
| 182 | + element.css('opacity', 0); |
| 183 | + jQuery(element).animate({ |
| 184 | + opacity: 1 |
| 185 | + }, done); |
| 186 | + |
| 187 | + // optional onDone or onCancel callback |
| 188 | + // function to handle any post-animation |
| 189 | + // cleanup operations |
| 190 | + return function(isCancelled) { |
| 191 | + if(isCancelled) { |
| 192 | + jQuery(element).stop(); |
| 193 | + } |
| 194 | + } |
| 195 | + }, |
| 196 | + |
| 197 | + // you can also capture these animation events |
| 198 | + addClass : function(element, className, done) {}, |
| 199 | + removeClass : function(element, className, done) {} |
| 200 | + } |
| 201 | +}); |
| 202 | +</pre> |
| 203 | + |
| 204 | +With these generated CSS class names present on the element at the time, AngularJS automatically |
| 205 | +figures out whether to perform a CSS and/or JavaScript animation. If both CSS and JavaScript animation |
| 206 | +code is present, and match the CSS class name on the element, then AngularJS will run both animations at the same time. |
| 207 | + |
| 208 | +## Class and ngClass animation hooks |
| 209 | + |
| 210 | +AngularJS also pays attention to CSS class changes on elements by triggering the **add** and **remove** hooks. |
| 211 | +This means that if a CSS class is added to or removed from an element then an animation can be executed in between |
| 212 | +before the CSS class addition or removal is finalized. (Keep in mind that AngularJS will only be |
| 213 | +able to capture class changes if an **expression** or the **ng-class** directive is used on the element.) |
| 214 | + |
| 215 | +The example below shows how to perform animations during class changes: |
| 216 | + |
| 217 | +<example animations="true"> |
| 218 | + <file name="index.html"> |
| 219 | + <p> |
| 220 | + <input type="button" value="set" ng-click="myCssVar='css-class'"> |
| 221 | + <input type="button" value="clear" ng-click="myCssVar=''"> |
| 222 | + <br> |
| 223 | + <span ng-class="myCssVar">CSS-Animated Text</span> |
| 224 | + </p> |
| 225 | + </file> |
| 226 | + <file name="style.css"> |
| 227 | + .css-class-add, .css-class-remove { |
| 228 | + -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; |
| 229 | + -moz-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; |
| 230 | + -o-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; |
| 231 | + transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; |
| 232 | + } |
| 233 | + |
| 234 | + .css-class, |
| 235 | + .css-class-add.css-class-add-active { |
| 236 | + color: red; |
| 237 | + font-size:3em; |
| 238 | + } |
| 239 | + |
| 240 | + .css-class-remove.css-class-remove-active { |
| 241 | + font-size:1.0em; |
| 242 | + color:black; |
| 243 | + } |
| 244 | + </file> |
| 245 | +</example> |
| 246 | + |
| 247 | +Although the CSS is a little different then what we saw before, the idea is the same. |
| 248 | + |
| 249 | +## Which directives support animations? |
| 250 | + |
| 251 | +A handful of common AngularJS directives support and trigger animation hooks whenever any major event occurs during its life cycle. |
| 252 | +The table below explains in detail which animation events are triggered |
| 253 | + |
| 254 | +| Directive | Supported Animations | |
| 255 | +|-------------------------------------------------------------------------------------|------------------------------------------| |
| 256 | +| {@link api/ng.directive:ngRepeat#animations ngRepeat} | enter, leave, and move | |
| 257 | +| {@link api/ngRoute.directive:ngView#animations ngView} | enter and leave | |
| 258 | +| {@link api/ng.directive:ngInclude#animations ngInclude} | enter and leave | |
| 259 | +| {@link api/ng.directive:ngSwitch#animations ngSwitch} | enter and leave | |
| 260 | +| {@link api/ng.directive:ngIf#animations ngIf} | enter and leave | |
| 261 | +| {@link api/ng.directive:ngShow#animations ngClass or {{class}}} | add and remove | |
| 262 | +| {@link api/ng.directive:ngShow#animations ngShow & ngHide} | add and remove (the ng-hide class value) | |
| 263 | + |
| 264 | +For a full breakdown of the steps involved during each animation event, refer to the {@link api/ngAnimate.$animate API docs}. |
| 265 | + |
| 266 | +## How do I use animations in my own directives? |
| 267 | + |
| 268 | +You can also use animations within custom directives can also be established by injecting `$animate` directly into your directive and |
| 269 | +making calls to it when needed. |
| 270 | + |
| 271 | +<pre> |
| 272 | +myModule.directive('my-directive', ['$animate', function($animate) { |
| 273 | + return function(element, scope, attrs) { |
| 274 | + element.bind('click', function() { |
| 275 | + if(element.hasClass('clicked')) { |
| 276 | + $animate.removeClass(element, 'clicked'); |
| 277 | + } else { |
| 278 | + $animate.addClass(element, 'clicked'); |
| 279 | + } |
| 280 | + }); |
| 281 | + }; |
| 282 | +}]); |
| 283 | +</pre> |
| 284 | + |
| 285 | +## More about animations |
| 286 | + |
| 287 | +For a full breakdown of each method available on `$animate`, see the {@link api/ngAnimate.$animate API documentation}. |
| 288 | + |
| 289 | +To see a complete demo, see the {@link tutorial/step_12 animation step within the AngularJS phonecat tutorial}. |
0 commit comments