AngularJS为部分常见的指令提供了动画钩子,比如 ngRepeat
, ngSwitch
和 ngView
, 而自定义指令则可以通过$animate
服务来实现。这些动画钩子在各种指令的生命周期内触发,触发时,它会尝试执行 CSS过渡动画(Transition), CSS关键帧动画(Keyframe Animation)或JavaScript回调动画(callback Animation)(取决于指令中的设定)。动画包括根据AngularJS内置的命名约定定义的CSS原生动画(包括过渡动画和关键帧动画),或者通过工厂(Factory)定义的JavaScript回调动画。
要想使用动画,必须单独引入ngAnimate
模块,angular.js中没有内置它。
下面便是一个通过 ngShow
和 ngHide
指令使用动画的一个简单例子:
<!doctype html> <html ng-app> <head> <script src="http://code.angularjs.org/1.2.25/angular.min.js"></script> <script src="http://code.angularjs.org/1.2.25/angular-animate.min.js"></script> </head> <body> <div ng-init="checked=true"> <label> <input type="checkbox" ng-model="checked" style="float:left; margin-right:10px;"> 可见... </label> <div class="check-element animate-show-hide" ng-show="checked" style="clear:both;"> 内容 </div> </div> </body> </html>
.animate-show-hide { padding:10px; border:1px solid black; background:white; } .animate-show-hide.ng-hide-add, .animate-show-hide.ng-hide-remove { -webkit-transition:all linear 0.5s; -moz-transition:all linear 0.5s; -o-transition:all linear 0.5s; transition:all linear 0.5s; display:block!important; } .animate-show-hide.ng-hide-add.ng-hide-add-active, .animate-show-hide.ng-hide-remove { opacity:0; } .animate-show-hide.ng-hide-add, .animate-show-hide.ng-hide-remove.ng-hide-remove-active { opacity:1; }
有关该模块的详细说明,请参见ngAnimate
API文档
你或许应该把这些CSS动画定义在一个单独的CSS文件中。
AngularJS的动画完全基于CSS class来工作。只要在你网页中的HTML元素加上了特定的class,你就得到了动画效果。让我们看一个在ng-repeat中使用动画的例子:
<div ng-repeat="item in items" class="repeated-item"> </div>
如上所示,ng-repeat所在的元素上有一个.repeated-item
类,这个类名将被CSS原生动画和/或JavaScript动画代码在定义动画时所引用。
当每次ngRepeat中新增一个条目(item)时, ngRepeat会在该条目上自动添加一个ng-enter
类,每次移除条目时会添加ng-leave
类,每次条目被移动时会添加ng-move
类。
下面的CSS代码中, 我们可以看到每一个ngRepeat触发的事件,设置transition以及animation动画代码:
/* 我们使用CSS过渡(transition)语句来为带有.repeated-item类的元素添加动画效果,他们将在各个条目新增和移动时触发。 */ .repeated-item.ng-enter, .repeated-item.ng-move { -webkit-transition:0.5s linear all; -moz-transition:0.5s linear all; -o-transition:0.5s linear all; transition:0.5s linear all; opacity:0; } /* 用ng-enter-active和ng-move-active类来定义过渡效果的最终属性值,以便动画指令知道它最终该达到什么状态。 */ .repeated-item.ng-enter.ng-enter-active, .repeated-item.ng-move.ng-move-active { opacity:1; } /* 我们使用CSS关键帧(keyframe animation)语句来为带有.repeated-item类的元素定义移除动画(ng-leave) */ .repeated-item.ng-leave { -webkit-animation:0.5s my_animation; -moz-animation:0.5s my_animation; -o-animation:0.5s my_animation; animation:0.5s my_animation; } @keyframes my_animation { from { opacity:1; } to { opacity:0; } } /* 不幸的是,每个浏览器都需要用它自己的“方言”来定义关键帧动画 */ @-webkit-keyframes my_animation { from { opacity:1; } to { opacity:0; } } @-moz-keyframes my_animation { from { opacity:1; } to { opacity:0; } } @-o-keyframes my_animation { from { opacity:1; } to { opacity:0; } }
同样的方法也可用于JavaScript动画 (用jQuery来执行动画):
myModule.animation('.repeated-item', function() { return { enter : function(element, done) { element.css('opacity',0); jQuery(element).animate({ opacity: 1 }, done); // 可选的onDone或onCancel回调函数可用于定义执行完动画后的各种清理操作。 return function(isCancelled) { if(isCancelled) { jQuery(element).stop(); } } }, leave : function(element, done) { element.css('opacity', 1); jQuery(element).animate({ opacity: 0 }, done); // 可选的onDone或onCancel回调函数可用于定义执行完动画后的各种清理操作。 return function(isCancelled) { if(isCancelled) { jQuery(element).stop(); } } }, move : function(element, done) { element.css('opacity', 0); jQuery(element).animate({ opacity: 1 }, done); // 可选的onDone或onCancel回调函数可用于定义执行完动画后的各种清理操作。 return function(isCancelled) { if(isCancelled) { jQuery(element).stop(); } } }, // 你还可以捕获下列动画事件 addClass : function(element, className, done) {}, removeClass : function(element, className, done) {} } });
一旦这些类名出现在元素上,AngularJS就知道该执行CSS或JavaScript动画了。 如果同时定义了CSS动画和JavaScript动画,并且匹配了这些类名,AngularJS就会同时执行它们。
AngularJS也通过设置add和remove钩子来关注类名的变化。这意味着如果从一个元素中增加或者删除一个CSS类,动画会在这个类名被添加或者删除完毕之前就开始执行。(记住:即使元素上使用了表达式或者ng-class指令,AngularJS也只能捕获到类名的变化。)
下面的例子演示了如何在类名变化时执行动画:
<!doctype html> <html ng-app> <head> <script src="http://code.angularjs.org/1.2.25/angular.min.js"></script> <script src="http://code.angularjs.org/1.2.25/angular-animate.min.js"></script> </head> <body> <p> <input type="button" value="设置" ng-click="myCssVar='css-class'"> <input type="button" value="清除" ng-click="myCssVar=''"> <br> <span ng-class="myCssVar">CSS-动画文本</span> </p> </body> </html>
.css-class-add, .css-class-remove { -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; -moz-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; -o-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; } .css-class, .css-class-add.css-class-add-active { color: red; font-size:3em; } .css-class-remove.css-class-remove-active { font-size:1.0em; color:black; }
虽然这里的CSS与我们之前看到的有点区别,但思想是一样的。
部分AngularJS指令支持在它的生命周期内发生重要事件时触发动画钩子,下面的表格详细说明了哪些动画事件会被触发。
Directive | 支持动画 |
---|---|
ngRepeat |
enter , leave , move |
ngView | enter , leave |
ngInclude |
enter , leave |
ngSwitch |
enter , leave |
ngIf |
enter , leave |
ngClass or {{class}} |
add , remove |
ngShow & ngHide |
add , remove (the ng-hide class value) |
对于各个动画事件的详细触发步骤,请参考API 文档。
在自定义指令中你可以通过依赖注入获得$animate
服务, 你可以在任何需要的时候调用它。
myModule.directive('my-directive', ['$animate', function($animate) { return function(element, scope, attrs) { element.bind('click', function() { if(element.hasClass('clicked')) { $animate.removeClass(element, 'clicked'); } else { $animate.addClass(element, 'clicked'); } }); }; }]);
关于$animate
各个方法的详情, 请参阅 API 文档。
查看完整demo,请参见“AngularJS phonecat向导”之“动画”一节。