angular学习(十七)——-Animations


Angular为一些常用的指令(ngRepeat,ngSwitch,ngView)提供了动画的hooks,自动的指令也可以通过$animate服务来实现。

简介

Angular为一些常用的指令(ngRepeat,ngSwitch,ngView)提供了动画的hooks,自动的指令也可以通过$animate服务来实现。这些指令上的动画hook被设置来代替一些繁琐的代码,比如通过条件触发来达到各种效果,比如css的过渡,css形式的动画或者一个js的回调。

一定要在app中注入ngAnimate模块,动画才能起作用。下面是一个可以用于ngShow和ngHide的例子。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<!DOCTYPE html>
<html lang="en" ng-app="myanimate">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="bower_components/angular/angular.min.js"></script>
<script src="bower_components/angular-animate/angular-animate.min.js"></script>
<style>
.content-area {
border: 1px solid black;
margin-top: 10px;
padding: 10px;
}
.sample-show-hide {
transition: all linear 0.5s;
}
.sample-show-hide.ng-hide {
opacity: 0;
}
</style>
</head>
<body ng-controller="ainmationController">
<div ng-init="checked = true">
<label>
<input type="checkbox" ng-model="checked"/>
Is visible
</label>
<div class="content-area sample-show-hide" ng-show="checked">
Content...
</div>
</div>
</body>
<script>
var myanimation = angular.module("myanimate", ['ngAnimate']);
myanimation.controller("ainmationController", function () {
});
</script>
</html>

工作原理

angularjs的动画是完全基于css的,只要你将相应的css应用到html元素中,你就可以使用动画。下面,我们来看一个repeater的例子。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
<!DOCTYPE html>
<html lang="en" ng-app="myanimate">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="bower_components/angular/angular.min.js"></script>
<script src="bower_components/angular-animate/angular-animate.min.js"></script>
<style>
/*
为元素的新增和顺序改变用css的transition方式增加动画效果
*/
.repeated-item.ng-enter, .repeated-item.ng-move {
transition: all 0.5s linear;
opacity: 0;
}
/*
上面transition方式动画的目标效果
*/
.repeated-item.ng-enter.ng-enter-active,
.repeated-item.ng-move.ng-move-active {
opacity: 1;
}
/*
为元素的移除使用css3的keyframe方式增加动画效果
*/
.repeated-item.ng-leave {
animation: 0.5s my_animation;
}
@keyframes my_animation {
from {
opacity: 1;
}
to {
opacity: 0;
}
}
</style>
</head>
<body ng-controller="ainmationController as ctrl">
<div ng-repeat="item in items" class="repeated-item">
{{ item.id }}
</div>
<button ng-click="ctrl.add()">add</button>
<button ng-click="ctrl.remove()">remove</button>
<button ng-click="ctrl.reverse()">reverse</button>
</body>
<script>
var myanimation = angular.module("myanimate", ['ngAnimate']);
myanimation.controller("ainmationController", ['$scope', function ($scope) {
$scope.items = [];
$scope.items.push({"id": "1001"});
$scope.items.push({"id": "1002"});
$scope.items.push({"id": "1003"});
this.add = function () {
$scope.items.push({"id": "1004"});
}
this.remove = function () {
$scope.items.pop();
}
this.reverse = function () {
$scope.items.reverse();
}
}]);
</script>
</html>

每个ng-repeat重复的元素节点都会带有repeated-item的样式,它将告诉每个元素如何演示动画效果。在这里要说明一下:每增加一个元素,这个元素就增加一个ng-enter的css;如果元素顺序发生改变,就会增加一个ng-remove的css;移除一个元素,就会增加一个ng-leave的CSS。这些css会在动画完成后消失。

为了更好的理解,来看一下用jquey实现同样的效果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
myModule.animation('.repeated-item', function() {
return {
enter: function(element, done) {
// 初始化元素的opacity
element.css('opacity', 0);
// 用opacity方式实现动画
element.animate({opacity: 1}, done);
// 当取消事件产生后,停止动画
return function(isCancelled) {
if (isCancelled) {
element.stop();
}
};
},
leave: function(element, done) {
element.css('opacity', 1);
element.animate({opacity: 0}, done);
return function(isCancelled) {
if (isCancelled) {
element.stop();
}
};
},
// 其他事件的动画也是一样的实现方式
move: function(element, done) {},
addClass: function(element, className, done) {},
removeClass: function(element, className, done) {}
}
});

元素上的css决定了angularjs什么时候执行动画,同一个css上如果想css动画和javascript动画一起使用的话需要使用$animateCss.

Class和ngClass的动画hook

angularjs也为css的class更改附加了动画hook,也就是说当css的class的值添加或者移除时就会相应的产生动画效果,但要注意的一点是仅仅ng-class的相应的插值发生改变时才会触发这个动画。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<!DOCTYPE html>
<html lang="en" ng-app="myanimate">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="bower_components/angular/angular.min.js"></script>
<script src="bower_components/angular-animate/angular-animate.min.js"></script>
<style>
.css-class-add, .css-class-remove {
transition: all 0.5s cubic-bezier(0.250, 0.460, 0.450, 0.940);
}
.css-class {
color: red;
font-size: 3em;
}
</style>
</head>
<body ng-controller="ainmationController as ctrl">
<p>
<button ng-click="myCssVar='css-class'">Set</button>
<button ng-click="myCssVar=''">Clear</button>
<br>
<span ng-class="myCssVar">CSS-Animated Text</span>
</p>
</body>
<script>
var myanimation = angular.module("myanimate", ['ngAnimate']);
myanimation.controller("ainmationController", ['$scope', function ($scope) {
}]);
</script>
</html>

支持动画的指令

一些常用的指令在他们生命周期的几个主要事件上可以触发动画,下面列表详细的给出了各个指令可以支持的动画:

指令 动画
ngRepeat enter, leave, and move
ngIf enter and leave
ngSwitch enter and leave
ngInclude enter and leave
ngView enter and leave
ngMessage / ngMessageExp enter and leave
ngClass / { {class} } add and remove
ngClassEven / ngClassOdd add and remove
ngHide add and remove (the ng-hide class)
ngShow add and remove (the ng-hide class)
ngModel add and remove (various classes)
form / ngForm add and remove (various classes)
ngMessages add and remove (the ng-active/ng-inactive classes)

自定义指令中使用动画

自定义的指令可以通过注入$animate间接地使用动画。

1
2
3
4
5
6
7
8
9
10
11
myModule.directive('my-directive', ['$animate', function($animate) {
return function(scope, element) {
element.on('click', function() {
if (element.hasClass('clicked')) {
$animate.removeClass(element, 'clicked');
} else {
$animate.addClass(element, 'clicked');
}
});
};
}]);

app引导启动和页面加载时的动画

你可能注意到了,我们之前ng-repeat的例子中,前三个数据是一下就出现的并没有动画,这是因为默认的app启动时的动画是禁止的,这是为了防止在页面有大量的动画时,可能会引起视觉上的混乱,对加载速度也有一定的影响。

在angularjs的内部,ngAnimate要等待app启动的引导完成,再等待模板的下载,还要等当前$digest的完成。一定要确保整个app编译之前,动画是不会运行的。

如果你一定要在app引导启动时产生动画效果,那么你需要在module的run方法中激活anmitate即可,但要注意这个激活是全局的。

1
2
3
myModule.run(function($animate) {
$animate.enabled(true);
});

怎么样选择性的启动,禁用或者跳过动画

有多种不同的方法来全局禁用动画或者禁用特定的动画。禁用特定的动画可以提升渲染的性能,比如一些很大的ngRepeat列表实际上是没有动画的。即使元素没有一个动画,ngAnimate都在运行时会去检查有没有动画要演示,这是会影响性能的。

在config中,使用$animateProvider.classNameFilter()

这个函数可以在app的配置阶段被调用,它仅仅接受一个正则表达式参数,用于匹配需要展示动画的元素class的名称。正则表达式有极大的灵活性,通过这个可以很方便的指定或者排除相应的动画。

1
2
3
myanimation.config(function ($animateProvider) {
$animateProvider.classNameFilter(/animate-/);
})

classNameFilter方法相较于其他策略来讲极大的提高了app的速率,因为匹配是在其他禁用启用策略之前就发生的,但是有个缺点就是无法使用两个classNameFilter策略,只能通过修改class名称的方式来达到动画的启用和禁用。

在run中,使用$animate.enabled()

这个函数启用/禁用动画的方式有两种:

只传一个boolean参数,将会全局的启用/禁用动画,$animate.enabled(false)将会禁用app的所有动画。

当第一个参数是一个原生dom元素或者是jqLite/jQuery的元素时,这个函数可以启用/禁用该元素及其所有子节点的动画:$animate.enabled(myElement, false)。当然你也可用在子节点上重新定义动画的启用/禁用。和classNameFilter不同的是,这个函数实在app的运行阶段作用的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<body ng-controller="ainmationController as ctrl">
<p>
<button ng-click="myCssVar='css-class'">Set</button>
<button ng-click="myCssVar=''">Clear</button>
<br>
<span ng-class="myCssVar">CSS-Animated Text</span>
</p>
</body>
<script>
var myanimation = angular.module("myanimate", ['ngAnimate']);
myanimation.run(function ($animate) {
$animate.enabled(true);
$animate.enabled(angular.element(document).find('p'), false);
$animate.enabled(angular.element(document).find('span'), true);
}).controller("ainmationController", ['$scope', function ($scope) {
}]);
</script>

有一点需要再重申一次:$animateProvider.classNameFilter()
策略的优先级要高于$animate.enabled()

通过css class:用ng-animate重写css class

当动画启动后,ngAnimate会在附加ng-animate的class在对应的元素上,持续整个动画周期,通过附加transition/animation的样式,可以成功跳过该动画。

修改一下我们之前的例子,附加上这两段,动画就不会再出现了:

1
2
3
4
5
6
7
.css-class-add.ng-animate {
transition: 0s;
}
.css-class-remove.ng-animate{
transition: 0s;
}

通过把transition设为0s,可以让ngAnimate忽略掉已经存在的动画的样式,当然javascript的动画仍会执行。

在动画开始前防止闪烁

当在一个结构和的嵌套元素的动画中,比如ngIf动画嵌套在一个基于ngClass的DOM结构中时,在动画的启动过程中,有时会有一个短暂的闪烁产生。

为了不至于这样,你可以增加ng-[event]-prepare的class,这个class会在动画初始化的时候附加在元素上,当动画开始之前就会消失掉。这个class只在结构性的动画时产生(enter, move, and leave)。

以下是个防止闪烁的例子,你可以去掉注释标明的地方试下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
<!DOCTYPE html>
<html lang="en" ng-app="myanimate">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="bower_components/angular/angular.min.js"></script>
<script src="bower_components/angular-animate/angular-animate.min.js"></script>
<style>
.red {
background: red;
border: 1px solid black;
padding: 10px;
}
.blue {
background: blue;
border: 1px solid black;
padding: 10px;
}
.message {
background: white;
border: 1px solid black;
padding: 10px;
}
.message.ng-enter, .message.ng-leave {
transition: all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1s;
}
.message.ng-enter,
.message.ng-enter-prepare,/*不加这行就会闪烁*/
.message.ng-leave.ng-leave-active {
opacity: 0;
}
.message.ng-leave,
.message.ng-enter.ng-enter-active {
opacity: 1;
}
</style>
</head>
<body ng-controller="ainmationController as ctrl">
<div ng-class="{red: myProp}">
<div ng-class="{blue: myProp}">
<div class="message" ng-if="myProp">message</div>
</div>
</div>
</body>
<script>
var myanimation = angular.module("myanimate", ['ngAnimate']);
myanimation.run(function ($animate) {
$animate.enabled(true);
}).controller("ainmationController", ['$scope', function ($scope) {
$scope.myProp = true;
}]);
</script>
</html>
文章目录
  1. 1. 简介
  2. 2. 工作原理
  3. 3. Class和ngClass的动画hook
  4. 4. 支持动画的指令
  5. 5. 自定义指令中使用动画
  6. 6. app引导启动和页面加载时的动画
  7. 7. 怎么样选择性的启动,禁用或者跳过动画
    1. 7.1. 在config中,使用$animateProvider.classNameFilter()
    2. 7.2. 在run中,使用$animate.enabled()
    3. 7.3. 通过css class:用ng-animate重写css class
    4. 7.4. 在动画开始前防止闪烁
|