在这一步中,我们将会通过在我们先前创建的模板代码中添加CSS和JavaScript动画效果来扩展我们的web应用。

  ·我们现在使用ngAnimate模块来允许动画效果贯穿整个应用。

  ·我们也依赖于自带的指令来自动触发动画来进行开发。

  ·当一个动画效果被发现时,在给定的时间内,它将会和置于元素中的实际DOM操作一同运行(比如:在ngRepeat中插入/删除节点或在ngClass中添加/删除类)。

最大的不同列举如下,您可以点击这里在GitHub上查看全部的不同。

CSS过渡动画:使ngRepeat有生机

我们将会在位于phoneList组件模板中的ngRepeat指令添加CSS过渡动画来开始我们的故事。我们需要在迭代元素中添加一个额外的CSS类,以使其与我们的CSS动画代码挂钩。

app/phone-list/phone-list.template.html:

...
<ul class="phones">
<li ng-repeat="phone in $ctrl.phones | filter:$ctrl.query | orderBy:$ctrl.orderProp"
class="thumbnail phone-list-item">
<a href="#!/phones/{{phone.id}}" class="thumb">
<img ng-src="{{phone.imageUrl}}" alt="{{phone.name}}" />
</a>
<a href="#!/phones/{{phone.id}}">{{phone.name}}</a>
<p>{{phone.snippet}}</p>
</li>
</ul>
...

您注意到新添加的phone-list-item CSS类了吗?这是是我们的HTML代码产生动画效果的全部。

现在来看看实际的CSS过渡动画代码:

app/app.animations.css:

.phone-list-item.ng-enter,
.phone-list-item.ng-leave,
.phone-list-item.ng-move {
transition: 0.5s linear all;
} .phone-list-item.ng-enter,
.phone-list-item.ng-move {
height: 0;
opacity: 0;
overflow: hidden;
} .phone-list-item.ng-enter.ng-enter-active,
.phone-list-item.ng-move.ng-move-active {
height: 120px;
opacity: 1;
} .phone-list-item.ng-leave {
opacity: 1;
overflow: hidden;
} .phone-list-item.ng-leave.ng-leave-active {
height: 0;
opacity: 0;
padding-bottom: 0;
padding-top: 0;
}

正如您所看到的那样,我们的phone-list-item CSS类在列表中的条款插入和删除时与动画效果挂钩:

  ·ng-enter类在一部新电话在列表中被添加且传递给页面时被处触发。

  ·ng-move类在一部电话在页面中的相对位置改变时被触发。

  ·ng-leave类在列表中的一部电话被移除时被触发。

电话列表中条款的添加和删除基于传递给ngRepeat指令的数据。比如,如果过滤器数据改变了,迭代列表中条款会展示进进出出的效果。

值得一提的是,当一个动画效果发绅士,两个CSS类集合会被添加到元素中:

  ·一个代表动画开始效果的"starting"类。

  ·一个代表动画结束效果的"active"类。

starting类的名字就是带有ng-前缀的事件(比如enter,move或leave)的名字。所以一个enter事件会导致添加ng-enter类。

active类的名字源于starting类,通过添加一个-active后缀。这两个类的命名惯例使得开发者可以创建制作一个动画,从开始到结束。

在上面的例子中,加入动画效果的元素在它们被加入列表时,高度从0px扩展到120px,在它们从列表中被移除前,高度会被折叠回0px。同时也会产生一个淡入/淡出效果。所有这些是被声明于最顶层的CSS文件所处理的。

CSS关键框架动画:使ngView有生机

接下来,让我们为ngView中路由的过滤添加动画。

同样的,我们需要在HTML模板中添加一个新的CSS类,这次轮到了ng-view元素,为了为我们的动画效果获取更多“生动的能力”,我们将会通过一个容器元素将[ng-view]元素包起来。

app/index.html:

<div class="view-container">
<div ng-view class="view-frame"></div>
</div>

我们将一个position: relative风格应用于.view-container容器。所以在动画效果的过程中管理.view-frame元素的位置是很简单的。

一旦我们的预备代码准备好了,让我们来看看这个过渡效果的实际CSS风格。

app/app.animations.css:

...

.view-container {
position: relative;
} .view-frame.ng-enter,
.view-frame.ng-leave {
background: white;
left: 0;
position: absolute;
right: 0;
top: 0;
} .view-frame.ng-enter {
animation: 1s fade-in;
z-index: 100;
} .view-frame.ng-leave {
animation: 1s fade-out;
z-index: 99;
} @keyframes fade-in {
from { opacity: 0; }
to { opacity: 1; }
} @keyframes fade-out {
from { opacity: 1; }
to { opacity: 0; }
} /* Older browsers might need vendor-prefixes for keyframes and animation! */

没什么大不了了!仅仅是一个页面间的淡入/淡出效果。唯一与平常不同的就是在离开页面的顶部(由ng-leave类识别),我们使用了绝对布局来确定整个页面的位置(由ng-enter类识别)。同时产生了一个平滑过渡效果。所以,随着原先页面的移除,将会产生淡出效果,这时新页面将在上面有淡入效果。

一旦leave动画结束了,元素会从DOM中移除。同样的,一旦enter动画效果完成了,ng-enter和ng-enter-active CSS类也会在元素中被移除,导致其传递和重定位至默认的CSS风格(所以一旦动画结束之手,绝对布局就不存在了)。随着路由的改变,页面间的转换非常自然,而不是跳来跳去。

应用到的CSS类和ngRepeat非常相似。每次一个新页面加载至ngView指令,都会创建一份备份。下载模板并且添加内容。这确保了所有的视图包含在一个单一的HTML元素中,这允许更简单的动画控制。

关于更多CSS动画,请查看这里

用JavaScript使ngClass有生机

让我们在应用中添加另一个动画效果,在我们的phone-detail.template.html视图中,我们有一个很棒的略图容器。通过点击页面中列出的略图,电话的介绍图片也会改变。但我们怎样加入动画呢?

让我们首先给它一点思想。一般说来,当用户点击一张略图,介绍图片将会转换成新近选中的略图。使用类在HTML中指定状态的改变是最好的方法。和先前很像--当我们使用一个CSS类来驱动动画--这次当CSS类自身改变时动画将会发生。

每次一张电话略图被选中时,状态会改变,且.selected CSS类将会添加到介绍图片,这样就触发了动画效果。

我们将会从调整phone-detail.template.html代码开始,注意到我们改变了我们展示我们大图片的方式:

app/phone-detail/phone-detail.template.html:

<div class="phone-images">
<img ng-src="{{img}}" class="phone"
ng-class="{selected: img === $ctrl.mainImageUrl}"
ng-repeat="img in $ctrl.phone.images" />
</div> ...

和略图一样,我们使用迭代器将所有的介绍图展示为一个列表,然而我们并没有任何与迭代器过渡相关的的动画。相反,我们将关注每一个元素类,尤其是selected类,因为它的存在与否会决定元素的可见与否。selected类的添加/删除是由ngClass指令所管理的,基于指定的条件 (img === $ctrl.mainImageUrl).在我们条件下,始终存在一个元素拥有selected类,因此总是有一张电话介绍图片在屏幕中是可见的。

当selected类添加为一个元素时,selected-add和selected-add-active 类被添加至AngularJS来设置一个动画效果。当selected类被从元素中移除时,selected-remove和selected-remove-active类会在元素中被应用,触发另外的动画。

最后,为了确保当页面第一次加载时,电话图片被正确展示,我们也稍微修改了电话细节的CSS风格:

app/app.css:

...

.phone {
background-color: white;
display: none;
float: left;
height: 400px;
margin-bottom: 2em;
margin-right: 3em;
padding: 2em;
width: 400px;
} .phone:first-child {
display: block;
} .phone-images {
background-color: white;
float: left;
height: 450px;
overflow: hidden;
position: relative;
width: 450px;
} ...

您可能会认为我们打算创建另一个基于CSS的动画效果。虽然我们可以这么做,但让我们在这里学习一下如何用基于JavaScript的.animation() 模块方法来创建一个动画吧。

app/app.animations.js:

angular.
module('phonecatApp').
animation('.phone', function phoneAnimationFactory() {
return {
addClass: animateIn,
removeClass: animateOut
}; function animateIn(element, className, done) {
if (className !== 'selected') return; element.
css({
display: 'block',
position: 'absolute',
top: 500,
left: 0
}).
animate({
top: 0
}, done); return function animateInEnd(wasCanceled) {
if (wasCanceled) element.stop();
};
} function animateOut(element, className, done) {
if (className !== 'selected') return; element.
css({
position: 'absolute',
top: 0,
left: 0
}).
animate({
top: -500
}, done); return function animateOutEnd(wasCanceled) {
if (wasCanceled) element.stop();
};
}
});

我们通过指定一个CSS类选择器(这里是.phone)和一个动画工厂函数(这里是phoneAnimationFactory())来创建一个自定义的动画效果。工厂函数返回一个从时间(对象键)指向动画回调(对象值)的对象。事件相当于ngAnimate识别和能连接的DOM行为,比如addClass/removeClass/setClass,enter/move/leave和

animate 。相关的回调会被ngAnimate调用适当的次数。

更多关于动画工厂,请查看API Reference。

在这种情况下,我们感兴趣的是在一个类中.phone元素的添加/删除。因此我们为addClass和removeClass事件指定回调函数。当selected类被添加为一个元素时(经由ngClass指令),addClass JavaScript回调函数将被执行,伴随着element作为一个传递的参数。最后一个传递的参数是done回调函数。我们调用done()来告诉Angular我们自定义的JavaScript已经结束了。removeClass用相同的方式工作,不同的是这在类被移除时执行。

注意到我们使用了jQuery的animate()来提高动画效果。jQuery中JavaScript动画的实现不需要Angular,但无论如何我们在这里使用了,以此来作为一个范例。更多jQuery.animate()的信息请看 jQuery documentation.

随着事件的回调,我们通过操作DOM来创建动画效果。在上面的代码中,这由element.css()和element.animate()来实现。这样做的结果是一个新元素有一个500px的位置移动,并且所有的元素--无论是先前的还是最新的--都有了一个500px的位置移动。结果就产生了一个传送带一样的动画。在animate()函数完成其动画效果之后,它调用done来提醒Angular。

您可能注意到了每一个动画回调都返回一个函数。这是一个可选的函数,将在动画效果结束时被调用,要么被完全执行,要么被取消(比如另一个动画效果发生在相同的元素上)。一个布尔参数(wasCanceled)被传递给这个函数,使得开发者知道其被取消与否。我们使用这个函数来执行任何必要的清除工作。

实验

  ·反转动画效果,实现动画效果向下传递。

  ·想要动画效果运行得更快或更慢,可以传递一个duration参数给.animate():

element.css({...}).animate({...}, 1000 /* 1 second */, done);

  ·使得动画“不对称”。比如,在新元素放大时将原来的元素淡出:

// animateIn()
element.css({
display: 'block',
opacity: 1,
position: 'absolute',
width: 0,
height: 0,
top: 200,
left: 200
}).animate({
width: 400,
height: 400,
top: 0,
left: 0
}, done); // animateOut()
element.animate({
opacity: 0
}, done);

  ·设计您自己的酷炫动画吧。

总结

我们的应用现在已经易于使用了,多亏了页面和UI状态间平滑的过渡。

您做到了!我们在相对较短的时间内创建了一个web应用。我们会在下一节中给出下一步指导。

[Angular Tutorial] 14 -Animations的更多相关文章

  1. [Angular Tutorial]PhoneCat Tutorial App

    (注:曾经在<不敢止步>一书中看到学到一个观点,作者认为学习一门技术最好的方法就是翻译某部领域书籍.这里我决定做一次尝试,接下来花1个月左右时间,将Angular Tutorial Pho ...

  2. [Angular Tutorial] 3-Components

    在先前的步骤中,我们看到了一个控制器和一个模板如何一起工作来将一个静态的HTML文件转化为动态页面(view).一般说来,这在单页应用中一种非常常见的模式(在Angular应用中尤其是这样): ·客户 ...

  3. [Angular Tutorial] 7-XHRs & Dependency Injection

    我们受够了在应用中用硬编码的方法嵌入三部电话!现在让我们用Angular内建的叫做$http的服务来从我们的服务器获取更大的数据集吧.我们将会使用Angular的依赖注入来为PhoneListCtrl ...

  4. [Angular Tutorial] 0-Bootstraping

    在这一节的tutorial中,您将会逐渐熟悉AngularJS phonecat app的最重要的源代码文件.您也将学到如何将开发服务器与angular-seed绑定到一起,并且在浏览器中运行应用. ...

  5. [Angular Tutorial] 13 -REST and Custom Services

    在这一步中,我们将会改变我们获取数据的方式. ·我们定义一个代表RESTful客户端的自定义服务.使用这个客户端,我们可以用一种更简单的方法向服务端请求数据,而不用处理更底层的$httpAPI,HTT ...

  6. [Angular Tutorial] 12 -Event Handlers

    在这一步中,您将会在电话细节页面添加一个可点击的电话图片转换器. ·电话细节页面展示了当前电话的一张大图片和几张相对较小的略图.如果我们能仅仅通过点击略图就能把大图片换成略图就好了.让我们看看用Ang ...

  7. [Angular Tutorial] 11 -Custom Filters

    在这一步中您将学到如何创建您自己的展示过滤器. ·在先前的步骤中,细节页面展示“true”或“false”来显示某部电话是否有某项功能.在这一步中,我们将使用自定义的过滤器来将这些个字符串转化成符号: ...

  8. [Angular Tutorial] 10 -More Templating

    在这一步中,我们会实现电话细节的视图,这在用户点击列表中的一部电话时被展示. ·当您点击列表中的一部电话时,带有电话特定信息的电话细节页面将被展示. 我们打算使用$http来获取我们的数据,以此来实现 ...

  9. [Angular Tutorial] 9 -Routing & Multiple Views

    在这一步中,您将学到如何创建一个布局模板,并且学习怎样使用一个叫做ngRoute的Angular模块来构建一个具有多重视图的应用. ·当您现在访问/index.html,您将被重定向到/index.h ...

随机推荐

  1. js date相关学习!

    var myDate = new Date(); myDate.getYear(); //获取当前年份(2位) myDate.getFullYear(); //获取完整的年份(4位,1970-???? ...

  2. R语言——基本绘图函数

    通过一个综合的例子测试绘图函数 学习的内容是tigerfish老师的教程. 第一节:基本知识 用seq函数产生100位学生的学号. > num = seq(,) > num [] [] [ ...

  3. elasticsearch 管理工具

    ------------------sense------------------- google chrome 浏览器插件,数据交互使用   -------------------------hea ...

  4. 剑指offer反转链表

    way1: 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 3 ...

  5. JSON数据传递

    Servlet端代码 try { while (rs.next()) { for(int i=0;i<60;i++){ Day[i]+=rs.getInt("Day"+(i+ ...

  6. jquery判断节点是否存在

    if($('.onloadMore').length>0){ return '节点存在'; }else{ return '节点不存在'; }

  7. Spring Quartz定时器 配置文件详解

    在JavaEE系统中,我们会经常用到定时任务,比如每天凌晨生成前天报表,每一小时生成汇总数据等等.我们可以使用java.util.Timer结合java.util.TimerTask来完成这项工作,但 ...

  8. list转换为map

    Java代码如下: package Test01; import java.util.ArrayList; import java.util.HashMap; import java.util.Lis ...

  9. 2016大连网络赛 Football Games

    Football Games Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) P ...

  10. WINCE下进程间通信(二)

    WINCE下进程间通信(二) 接着前面的文章<WINCE下进程间通信(一)>,现在介绍进程间通信的另一种方法. 三.管道(消息队列) WINCE并不支持类似于PC机上匿名管道.命名管道的通 ...