AngularJS 动画操作
动画操作
最后一步,我们通过在之前创建的模板代码上方附加CSS和JavaScript动画来加强phonecat web应用。
- 现在我们使用
ngAnimate
模块来保证动画可以贯穿这个应用。 - 另外我们使用常用的
ng
指令来自动触发使动画接入的挂钩。 - 当发现一个动画之后,这个动画就会在标准的DOM操作之间运行,该操作在给定的时间中发布在元素上(如在
ngRepeat
上插入或删除节点或者在ngClass
上添加或删除类)。
工作区复位说明:
git checkout -f step-11
下面列出几种最重要的变化。你可以在 GitHub 上浏览全部差异。
依赖关系
动画功能由ngAniamte
模块中的Angular提供,它是从核心的Angular框架中分离出来的。另外,在这个项目中我们使用jQuery
模块来完成其余的JavaScript
动画。我们使用的是Bower来安装客户端依赖关系。这一步更新bower.json
配置文件来包含新的依赖关系:
{
"name": "angular-seed",
"description": "A starter project for AngularJS",
"version": "0.0.0",
"homepage": "https://github.com/angular/angular-seed",
"license": "MIT",
"private": true,
"dependencies": {
"angular": "~1.3.0",
"angular-mocks": "~1.3.0",
"bootstrap": "~3.1.1",
"angular-route": "~1.3.0",
"angular-resource": "~1.3.0",
"jquery": "~2.1.1",
"angular-animate": "~1.3.0"
}
}
“angular-animate”:“~1.3.0”
意味着bower要安装与版本1.3.x兼容的angular-animate组件版本。“jquery”:“2.1.1”
意味着bower要安装jQuery2.1.1版本。注意这不是Angular库,而是标准的jQuery库。我们可以使用bower来安装一个大范围的第三方库。
我们必须要求bower来下载和安装这个依赖关系。通过运行下述程序来实现这一要求:
npm install
警告:如果你上次运行npm install
后,一个新版本的Angular已经发布,那么由于需要安装的angular.js版本之间的冲突,你的bower install
可能会遇到问题。如果你有这个问题,那么只需要在运行npm install
之前,简单的删除你的app/bower_components
文件夹。
注意:如果你在全局范围内安装bower,那么就可以运行 bower instal
,但是对于这个项目来说,我们已经预配置了npm install
来运行bower。
在 ngAnimate 下动画是如何工作的
想要了解在AngularJS下动画是如何工作的,请先阅读 AngularJS Animation Guide。
模板
我们需要的带有HTML动画代码的更改是用来链接asset文件夹的,这些文件夹定义了动画和angular-animate.js
文件。动画模块,即ngAnimate
,由angualr-animate.js
文件定义,包含了使您的应用程序成为动画意识的必要的代码。以下是在索引文件中需要更改的:
app/index.html.
...
<!-- for CSS Transitions and/or Keyframe Animations -->
<link rel="stylesheet" href="css/animations.css">
...
<!-- jQuery is used for JavaScript animations (include this before angular.js) -->
<script src="/attachments/image/wk/angularjstutorial/jquery.js"></script>
...
<!-- required module to enable animation support in AngularJS -->
<script src="/attachments/image/wk/angularjstutorial/angular-animate.js"></script>
<!-- for JavaScript Animations -->
<script src="/attachments/image/wk/angularjstutorial/animations.js"></script>
...
重要:当使用Angular 1.3时,请确保使用jQuery版本2.1或更新的版本;jQuery 1.x官网不在支持。要确保加载 jQuery在所有AngularJS 脚本之前,否则AngularJS不会检测jQuery,并且动画也不会像预期的那样起作用。现在可以在CSS代码(animations.css
)和JavaScript代码(animations.js
)中创建动画。但是在开始之前,让我们创建一个新的模块,该模块使用ngAnimate模块作为依赖,这就像我们之前使用ngResource
一样。
模块与动画
app/js/animations.js
.
angular.module('phonecatAnimations', ['ngAnimate']);
// ...
// this module will later be used to define animations
// ...
现在让我们将这个模块附加到应用模块中…
app/js/app.js
.
// ...
angular.module('phonecatApp', [
'ngRoute',
'phonecatAnimations',
'phonecatControllers',
'phonecatFilters',
'phonecatServices',
]);
// ...
现在,phonecat模块是动画意识的。让我们做一些动画吧!
用 CSS 过渡动画实现动画的 ngRepeat
我们通过把CSS过渡动画添加到ngRepeat
指令呈现在phone-list.html
页面上开始。首先,在重复的元素中添加一个额外的CSS类,这样我们可以用CSS动画代码来与它连接。
app/partials/phone-list.html
.
<!--
Let's change the repeater HTML to include a new CSS class
which we will later use for animations:
-->
<ul class="phones">
<li ng-repeat="phone in phones | filter:query | orderBy:orderProp"
class="thumbnail phone-listing">
<a href="#/phones/{{phone.id}}" class="thumb"><img ng-src="{{phone.imageUrl}}"></a>
<a href="#/phones/{{phone.id}}">{{phone.name}}</a>
<p>{{phone.snippet}}</p>
</li>
</ul>
注意到怎样添加phone-listing
CSS类了吗?这就是我们需要的在HTML代码中使动画起作用的全部东西。
以下是实际的CSS 过渡动画代码:app/css/animations.css
.phone-listing.ng-enter,
.phone-listing.ng-leave,
.phone-listing.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;
}
.phone-listing.ng-enter,
.phone-listing.ng-move {
opacity: 0;
height: 0;
overflow: hidden;
}
.phone-listing.ng-move.ng-move-active,
.phone-listing.ng-enter.ng-enter-active {
opacity: 1;
height: 120px;
}
.phone-listing.ng-leave {
opacity: 1;
overflow: hidden;
}
.phone-listing.ng-leave.ng-leave-active {
opacity: 0;
height: 0;
padding-top: 0;
padding-bottom: 0;
}
正如你所见到的,phone-listing
CSS类与动画钩相结合,当列表中有项目被插入或者移出的时候,动画钩就会出现。
- ng-enter类应用于元素中,当一个新的phone添加到列表中并呈现在页面上。
- ng-move类应用于列表中项目的移动。
- ng-leave类应用于列表中项目删除。
phone listing项目的添加和删除取决于传给ng-repeat
属性的数据。例如,若过滤数据改变了,项目就会动画地加入到repeat列表中或从repeat列表中动画地去除。一些重要的声明,当动画出现时,两组CSS类会被添加到元素中:
“starting”类在动画开始时表明风格
- “active”类在动画结束时表明风格
starting类的名称是被激发事件的名称(如enter
,move
或leave
),带有ng -前缀。所以一个enter
事件将产生一个称为ng-enter
的类。active类名与starting类名相似,但带有一个-active
的后缀。这两类CSS命名约定允许开发人员制作动画,自始至终。
在上面的例子中,当添加或删除项目时,元素从0到120像素的高度扩展,在从列表中删除项目之前,元素使项目崩溃。还有一个漂亮的淡入和淡出效果也在同时发生。所有的这些都是由上述示例代码顶部的CSS过渡声明操作的。
尽管大多数现代的浏览器为CSS过渡和CSS动画CSS动画提供了很好的支持,但是IE9和更早的浏览器却没有。如果你想使动画与更早的浏览器兼容,考虑使用基于JavaScript的动画,下面会具体介绍。
用 CSS 关键帧动画实现动画的 ngView
接下来,让我们在ngView
中的路径更改之间为过渡添加动画。
首先,像上述例子一样,在HTML中添加一个新的CSS类。这一次,不是ng-repeat
元素,而是添加包含ng-view
指令的元素。为了实现这个,我们必须在HTML代码中做一些小小的改变,以使得在view改变中我们对动画有更多的控制。
app/index.html.
<div class="view-container">
<div ng-view class="view-frame"></div>
</div>
通过这个改变,ng-view
指令是嵌套在一个带有view-container
CSS类的父元素中。这个类添加了一个position:relative
样式,所以定位ng-view
是相对于父元素的,因为它模拟转换。
这里,让我们为这动画过渡将这个CSS类添加到animations.css
文件:
app/css/animations.css
.
.view-container {
position: relative;
}
.view-frame.ng-enter, .view-frame.ng-leave {
background: white;
position: absolute;
top: 0;
left: 0;
right: 0;
}
.view-frame.ng-enter {
-webkit-animation: 0.5s fade-in;
-moz-animation: 0.5s fade-in;
-o-animation: 0.5s fade-in;
animation: 0.5s fade-in;
z-index: 100;
}
.view-frame.ng-leave {
-webkit-animation: 0.5s fade-out;
-moz-animation: 0.5s fade-out;
-o-animation: 0.5s fade-out;
animation: 0.5s fade-out;
z-index:99;
}
@keyframes fade-in {
from { opacity: 0; }
to { opacity: 1; }
}
@-moz-keyframes fade-in {
from { opacity: 0; }
to { opacity: 1; }
}
@-webkit-keyframes fade-in {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes fade-out {
from { opacity: 1; }
to { opacity: 0; }
}
@-moz-keyframes fade-out {
from { opacity: 1; }
to { opacity: 0; }
}
@-webkit-keyframes fade-out {
from { opacity: 1; }
to { opacity: 0; }
}
/* don't forget about the vendor-prefixes! */
这里没有什么出乎意料的!只是一个简单的页面之间的淡入和淡出效果。这里的唯一不寻常的是,在页面之间实现软切换动画时,我们在前一页(有ng-leave
类的页面)的上方使用绝对定位来定位下一个页面(通过ng-enter
识别)。这样前一页即将被删除时,它逐渐消失而新的页面逐渐出现在它上面。
一旦离开动画结束元素被移除,一旦进入动画完成时,ng-enter
和ng-enter-active
CSS类从元素中被移除,使其rerender和重新定位其默认CSS代码(所以一旦动画结束,没有绝对定位)。这运作起来非常流畅,使得页面在路径变化中自然流动,不会有任何东西跳动。
应用的CSS类(开始和结束类)与ng-repeat
大体相同。每次加载一个新页面,ng-view
指令将创建自身的一个副本,下载模板并且附加内容。这将确保所有的视图都包含在一个单独的HTML元素中,该元素允许简单的动画控制。
更多关于 CSS 动画,请看 Web Platform documentation。
用JavaScript 实现动画的 ngClass
让我们在应用程序中添加另一个动画。切换到phone-detail.html
页面,我们看到,我们有一个不错的缩略图交换程序。通过点击页面上列出的缩略图,这个手机图片改变了。但我们怎样才能改变这些添加动画?
让我们先考虑一下。基本上,当你点击缩略图,你正在改变图像的状态来反映新选中的缩略图。HTML内来指定状态改变的最好办法是使用类。像之前一样,我们如何使用CSS类指定一个动画,这一次每当CSS类自身变化时,动画就会出现。
每当一个新的手机缩略图被选中时,状态改变,.active
CSS类添加到匹配的图像中,动画出现。
首先让我们对phone-detail.html
页面上的 HTML代码稍作调整。请注意,我们已经改变了显示大图的方式:
app/partials/phone-detail.html
.
<!-- We're only changing the top of the file -->
<div class="phone-images">
<img ng-src="{{img}}"
class="phone"
ng-repeat="img in phone.images"
ng-class="{active:mainImageUrl==img}">
</div>
<h1>{{phone.name}}</h1>
<p>{{phone.description}}</p>
<ul class="phone-thumbs">
<li ng-repeat="img in phone.images">
<img ng-src="{{img}}" ng-mouseenter="setImage(img)">
</li>
</ul>
就像缩略图,我们使用一个中继器来显示所有的图片,这些图片作为一个列表。然而我们不是使任何repeat-related动画来产生动作。相反,我们关注ng-class指令因为若active
类是正确的,那么它将被应用到元素中并且是可见的。否则,图像是隐藏的。在我们的例子中,总有一个元素有active类,,因此,总会有一个手机图片在屏幕上可见。
当活跃类添加到元素中, 在给AngularJS信号使它发射一个动画之前,active-add
类和active-add-active
类被添加。当活跃类被删除时,active-remove
类和active-remove-active
类应用于元素,该元素会反过来触发另一个动画。
确保手机图片在第一次加载页面时正确显示,我们也调整细节页面的CSS样式:
app/css/app.css
.phone-images {
background-color: white;
width: 450px;
height: 450px;
overflow: hidden;
position: relative;
float: left;
}
...
img.phone {
float: left;
margin-right: 3em;
margin-bottom: 2em;
background-color: white;
padding: 2em;
height: 400px;
width: 400px;
display: none;
}
img.phone:first-child {
display: block;
}
你可能会想,我们只是要创建另一个CSS-enabled动画。虽然我们可以那样做,但是让我们抓住机会来学习如何使用animation()
模块来创建javascript-enabled动画的方法。
app/js/animations.js
.
var phonecatAnimations = angular.module('phonecatAnimations', ['ngAnimate']);
phonecatAnimations.animation('.phone', function() {
var animateUp = function(element, className, done) {
if(className != 'active') {
return;
}
element.css({
position: 'absolute',
top: 500,
left: 0,
display: 'block'
});
jQuery(element).animate({
top: 0
}, done);
return function(cancel) {
if(cancel) {
element.stop();
}
};
}
var animateDown = function(element, className, done) {
if(className != 'active') {
return;
}
element.css({
position: 'absolute',
left: 0,
top: 0
});
jQuery(element).animate({
top: -500
}, done);
return function(cancel) {
if(cancel) {
element.stop();
}
};
}
return {
addClass: animateUp,
removeClass: animateDown
};
});
注意,我们使用 jQuery 来实现动画。有了AngularJS,jQuery不要求做JavaScript动画,但是我们要用它,因为编写自己的JavaScript动画库已经超出了本教程的范围。更多关于jQuery.animate,看 jQuery documentation。当在元素中添加或删除一个类时,使用addClass
和removeClass
回调函数,该元素包含我们注册的类,这是在这种情况下.phone
。当。活跃的类添加到元素(通过ng-class
指令)theaddClass JavaScript回调将发射元素作为参数传递给回调。传入的最后一个参数是done
回调函数。done回调函数的作用是,通过调用该函数,可以使Angular知道JavaScript动画结束。
removeClass
回调以同样的方式工作,当从元素中国移除一个类时得到触发。
在JavaScript回调中,通过操纵DOM创建动画。在上面的代码中,这是element.css()
和element.animate()
所做的。回调用一个500像素的偏移量定位下一个元素的位置,并通过把每个项目上移500像素
来定位之前的动画以及新的项目。这产生一个像动画一样的传送带。当animate
函数完成后,调用done
。
注意addClass
和removeClass
每个返回一个函数。这是一个可选的函数,当动画消失时(当另一个动画在相同的元素中替代) 以及动画完成后,可以被调用。一个布尔参数传递到函数中,可以让开发人员知道动画是否被取消了。当动画结束时,这个函数可以用来做任何必要的清理工作。
总结
现在你学会了!我们已经在一个相对短的时间内创建了一个web应用程序。在 完结篇 中,我们将讨论这里的东西给我们的指引。
更多建议: