最后一步,我們通過在之前創(chuàng)建的模板代碼上方附加CSS和JavaScript動畫來加強phonecat web應(yīng)用。
ngAnimate
模塊來保證動畫可以貫穿這個應(yīng)用。ng
指令來自動觸發(fā)使動畫接入的掛鉤。ngRepeat
上插入或刪除節(jié)點或者在ngClass
上添加或刪除類)。工作區(qū)復(fù)位說明:
git checkout -f step-11
下面列出幾種最重要的變化。你可以在 GitHub 上瀏覽全部差異。
動畫功能由ngAniamte
模塊中的Angular提供,它是從核心的Angular框架中分離出來的。另外,在這個項目中我們使用jQuery
模塊來完成其余的JavaScript
動畫。我們使用的是Bower來安裝客戶端依賴關(guān)系。這一步更新bower.json
配置文件來包含新的依賴關(guān)系:
{
"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庫,而是標(biāo)準(zhǔn)的jQuery庫。我們可以使用bower來安裝一個大范圍的第三方庫。我們必須要求bower來下載和安裝這個依賴關(guān)系。通過運行下述程序來實現(xiàn)這一要求:
npm install
警告:如果你上次運行npm install
后,一個新版本的Angular已經(jīng)發(fā)布,那么由于需要安裝的angular.js版本之間的沖突,你的bower install
可能會遇到問題。如果你有這個問題,那么只需要在運行npm install
之前,簡單的刪除你的app/bower_components
文件夾。
注意:如果你在全局范圍內(nèi)安裝bower,那么就可以運行 bower instal
,但是對于這個項目來說,我們已經(jīng)預(yù)配置了npm install
來運行bower。
想要了解在AngularJS下動畫是如何工作的,請先閱讀 AngularJS Animation Guide。
我們需要的帶有HTML動畫代碼的更改是用來鏈接asset文件夾的,這些文件夾定義了動畫和angular-animate.js
文件。動畫模塊,即ngAnimate
,由angualr-animate.js
文件定義,包含了使您的應(yīng)用程序成為動畫意識的必要的代碼。以下是在索引文件中需要更改的:
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>
...
重要:當(dāng)使用Angular 1.3時,請確保使用jQuery版本2.1或更新的版本;jQuery 1.x官網(wǎng)不在支持。要確保加載 jQuery在所有AngularJS 腳本之前,否則AngularJS不會檢測jQuery,并且動畫也不會像預(yù)期的那樣起作用?,F(xiàn)在可以在CSS代碼(animations.css
)和JavaScript代碼(animations.js
)中創(chuàng)建動畫。但是在開始之前,讓我們創(chuàng)建一個新的模塊,該模塊使用ngAnimate模塊作為依賴,這就像我們之前使用ngResource
一樣。
app/js/animations.js
.
angular.module('phonecatAnimations', ['ngAnimate']);
// ...
// this module will later be used to define animations
// ...
現(xiàn)在讓我們將這個模塊附加到應(yīng)用模塊中…
app/js/app.js
.
// ...
angular.module('phonecatApp', [
'ngRoute',
'phonecatAnimations',
'phonecatControllers',
'phonecatFilters',
'phonecatServices',
]);
// ...
現(xiàn)在,phonecat模塊是動畫意識的。讓我們做一些動畫吧!
我們通過把CSS過渡動畫添加到ngRepeat
指令呈現(xiàn)在phone-list.html
頁面上開始。首先,在重復(fù)的元素中添加一個額外的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類與動畫鉤相結(jié)合,當(dāng)列表中有項目被插入或者移出的時候,動畫鉤就會出現(xiàn)。
phone listing項目的添加和刪除取決于傳給ng-repeat
屬性的數(shù)據(jù)。例如,若過濾數(shù)據(jù)改變了,項目就會動畫地加入到repeat列表中或從repeat列表中動畫地去除。一些重要的聲明,當(dāng)動畫出現(xiàn)時,兩組CSS類會被添加到元素中:
“starting”類在動畫開始時表明風(fēng)格
starting類的名稱是被激發(fā)事件的名稱(如enter
,move
或leave
),帶有ng -前綴。所以一個enter
事件將產(chǎn)生一個稱為ng-enter
的類。active類名與starting類名相似,但帶有一個-active
的后綴。這兩類CSS命名約定允許開發(fā)人員制作動畫,自始至終。
在上面的例子中,當(dāng)添加或刪除項目時,元素從0到120像素的高度擴展,在從列表中刪除項目之前,元素使項目崩潰。還有一個漂亮的淡入和淡出效果也在同時發(fā)生。所有的這些都是由上述示例代碼頂部的CSS過渡聲明操作的。
盡管大多數(shù)現(xiàn)代的瀏覽器為CSS過渡和CSS動畫CSS動畫提供了很好的支持,但是IE9和更早的瀏覽器卻沒有。如果你想使動畫與更早的瀏覽器兼容,考慮使用基于JavaScript的動畫,下面會具體介紹。
接下來,讓我們在ngView
中的路徑更改之間為過渡添加動畫。
首先,像上述例子一樣,在HTML中添加一個新的CSS類。這一次,不是ng-repeat
元素,而是添加包含ng-view
指令的元素。為了實現(xiàn)這個,我們必須在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
是相對于父元素的,因為它模擬轉(zhuǎn)換。
這里,讓我們?yōu)檫@動畫過渡將這個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! */
這里沒有什么出乎意料的!只是一個簡單的頁面之間的淡入和淡出效果。這里的唯一不尋常的是,在頁面之間實現(xiàn)軟切換動畫時,我們在前一頁(有ng-leave
類的頁面)的上方使用絕對定位來定位下一個頁面(通過ng-enter
識別)。這樣前一頁即將被刪除時,它逐漸消失而新的頁面逐漸出現(xiàn)在它上面。
一旦離開動畫結(jié)束元素被移除,一旦進(jìn)入動畫完成時,ng-enter
和ng-enter-active
CSS類從元素中被移除,使其rerender和重新定位其默認(rèn)CSS代碼(所以一旦動畫結(jié)束,沒有絕對定位)。這運作起來非常流暢,使得頁面在路徑變化中自然流動,不會有任何東西跳動。
應(yīng)用的CSS類(開始和結(jié)束類)與ng-repeat
大體相同。每次加載一個新頁面,ng-view
指令將創(chuàng)建自身的一個副本,下載模板并且附加內(nèi)容。這將確保所有的視圖都包含在一個單獨的HTML元素中,該元素允許簡單的動畫控制。
更多關(guān)于 CSS 動畫,請看 Web Platform documentation。
ngClass
讓我們在應(yīng)用程序中添加另一個動畫。切換到phone-detail.html
頁面,我們看到,我們有一個不錯的縮略圖交換程序。通過點擊頁面上列出的縮略圖,這個手機圖片改變了。但我們怎樣才能改變這些添加動畫?
讓我們先考慮一下?;旧?當(dāng)你點擊縮略圖,你正在改變圖像的狀態(tài)來反映新選中的縮略圖。HTML內(nèi)來指定狀態(tài)改變的最好辦法是使用類。像之前一樣,我們?nèi)绾问褂肅SS類指定一個動畫,這一次每當(dāng)CSS類自身變化時,動畫就會出現(xiàn)。
每當(dāng)一個新的手機縮略圖被選中時,狀態(tài)改變,.active
CSS類添加到匹配的圖像中,動畫出現(xiàn)。
首先讓我們對phone-detail.html
頁面上的 HTML代碼稍作調(diào)整。請注意,我們已經(jīng)改變了顯示大圖的方式:
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動畫來產(chǎn)生動作。相反,我們關(guān)注ng-class指令因為若active
類是正確的,那么它將被應(yīng)用到元素中并且是可見的。否則,圖像是隱藏的。在我們的例子中,總有一個元素有active類,,因此,總會有一個手機圖片在屏幕上可見。
當(dāng)活躍類添加到元素中, 在給AngularJS信號使它發(fā)射一個動畫之前,active-add
類和active-add-active
類被添加。當(dāng)活躍類被刪除時,active-remove
類和active-remove-active
類應(yīng)用于元素,該元素會反過來觸發(fā)另一個動畫。
確保手機圖片在第一次加載頁面時正確顯示,我們也調(diào)整細(xì)節(jié)頁面的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;
}
你可能會想,我們只是要創(chuàng)建另一個CSS-enabled動畫。雖然我們可以那樣做,但是讓我們抓住機會來學(xué)習(xí)如何使用animation()
模塊來創(chuàng)建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 來實現(xiàn)動畫。有了AngularJS,jQuery不要求做JavaScript動畫,但是我們要用它,因為編寫自己的JavaScript動畫庫已經(jīng)超出了本教程的范圍。更多關(guān)于jQuery.animate,看 jQuery documentation。當(dāng)在元素中添加或刪除一個類時,使用addClass
和removeClass
回調(diào)函數(shù),該元素包含我們注冊的類,這是在這種情況下.phone
。當(dāng)?;钴S的類添加到元素(通過ng-class
指令)theaddClass JavaScript回調(diào)將發(fā)射元素作為參數(shù)傳遞給回調(diào)。傳入的最后一個參數(shù)是done
回調(diào)函數(shù)。done回調(diào)函數(shù)的作用是,通過調(diào)用該函數(shù),可以使Angular知道JavaScript動畫結(jié)束。
removeClass
回調(diào)以同樣的方式工作,當(dāng)從元素中國移除一個類時得到觸發(fā)。
在JavaScript回調(diào)中,通過操縱DOM創(chuàng)建動畫。在上面的代碼中,這是element.css()
和element.animate()
所做的?;卣{(diào)用一個500像素的偏移量定位下一個元素的位置,并通過把每個項目上移500像素
來定位之前的動畫以及新的項目。這產(chǎn)生一個像動畫一樣的傳送帶。當(dāng)animate
函數(shù)完成后,調(diào)用done
。
注意addClass
和removeClass
每個返回一個函數(shù)。這是一個可選的函數(shù),當(dāng)動畫消失時(當(dāng)另一個動畫在相同的元素中替代) 以及動畫完成后,可以被調(diào)用。一個布爾參數(shù)傳遞到函數(shù)中,可以讓開發(fā)人員知道動畫是否被取消了。當(dāng)動畫結(jié)束時,這個函數(shù)可以用來做任何必要的清理工作。
現(xiàn)在你學(xué)會了!我們已經(jīng)在一個相對短的時間內(nèi)創(chuàng)建了一個web應(yīng)用程序。在 完結(jié)篇 中,我們將討論這里的東西給我們的指引。
更多建議: