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向导”之“动画”一节。