在這最后一步中,我們將通過在我們之前創(chuàng)建的模板代碼的頂部添加CSS和JavaScript動畫豐富我們的手機分類網(wǎng)站應(yīng)用。
ngAnimate
模擬以啟用動畫,以貫穿這個應(yīng)用。ng
指令以自動觸發(fā)使動畫接入的鉤子。ngRepeat
上插入和移除節(jié)點,或在ngClass
上添加和移除類)。把工作空間重置到第十二步
git checkout -f step-12
刷新你的瀏覽器或在線檢查這一步:Step 12 Live Demo
下面列出了第十一步和第十二步之間最重要的區(qū)別。你可以在GitHub上看到完整的差異。
Angular在ngAnimate
模塊中提供動畫功能,它與核心Angular框架分開發(fā)布。另外,我們將在項目中使用jquery
以實現(xiàn)額外的JavaScript動畫。
我們正在使用Bower以安裝客戶側(cè)依賴性。這一步更新了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.4.x",
"angular-mocks": "1.4.x",
"jquery": "~2.1.1",
"bootstrap": "~3.1.1",
"angular-route": "1.4.x",
"angular-resource": "1.4.x",
"angular-animate": "1.4.x"
}
}
"angular-animate": "1.4.x"
告訴bower安裝一個angular-animate組件的版本,與v1.4x版兼容。"jquery": "~2.1.1"
告訴bower安裝jQuery的v2.1.1版。注意這不是一個Angular庫,它是標(biāo)準(zhǔn)jQuery庫。我們可以使用bower來安裝一個大作用域的第三方庫。我們必須要求bower以下載并安裝依賴性運行以下指令實現(xiàn)它:
npm install
ngAnimate
協(xié)作要想知道動畫如何與AngularJS協(xié)作,請先閱讀?AngularJS動畫指南。
在HTML模板代碼內(nèi)部需要修改,以鏈接asset文件,它定義了動畫以及angular-animate.js
文件。該動畫模塊,即ngAnimate
,被定義在angular-animate.js
內(nèi)部,并包含了必要的代碼,以使你的應(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/angularjs/jquery.js"></script>
...
<!-- required module to enable animation support in AngularJS -->
<script src="/attachments/image/wk/angularjs/angular-animate.js"></script>
<!-- for JavaScript Animations -->
<script src="/attachments/image/wk/angularjs/animations.js"></script>
...
可以在CSS代碼(animations.css
)內(nèi)中創(chuàng)建動畫,也可以在JavaScript代碼(animations.js
)內(nèi)部創(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)在,手機分類模塊已經(jīng)有動感了。讓我們制作更多更多動畫吧!
我們將從這一步開始,把CSS過渡動畫添加到出現(xiàn)在phone-list.html
網(wǎng)頁上的ngRepeat
指令。首先讓我們把一個額外的CSS類添加到我們的重復(fù)元素上,因此我們可以把它與我們的CSS動畫代碼連接。
app/partials/phone-list.html
.
<!--
讓我們改變重復(fù)器HTML,以包含一個新的CSS類,之后我們將用它實現(xiàn)動畫:
-->
<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)動畫鉤子。
ng-enter
類。ng-move
類。ng-leave
類。添加或刪除手機列表項目取決于傳遞給元素屬性ng-repeat
的數(shù)據(jù)。比如,如果過濾器數(shù)據(jù)改變了,項目動畫地加入或退出重復(fù)列表。
有些很重要的事情需要注意,當(dāng)動畫發(fā)生時,元素上添加了CSS類的兩個集合:
開始類的名稱是被激發(fā)的事件(就像enter
、move
或leave
)的名稱帶上前綴ng-
。所以一個enter
事件將導(dǎo)致一個稱為ng-enter
類。
激活類名與開始類名相同,但是帶了一個后綴-active
。這兩類CSS命名公約允許開發(fā)員精心制作一個動畫,自始至終。
在我們上面的示例中,當(dāng)元素添加到列表中時,該元素從0高度伸展到120像素高度;在從列表中移除之前,又收縮到0像素。同時還發(fā)生了一個漸現(xiàn)和漸消的效果。這里都是由CSS過渡動畫處理的,CSS過渡動畫聲明在上面示例代碼的頂部。
雖然大多數(shù)現(xiàn)代瀏覽器能很好地支持CSS過渡和CSS動畫。但是如果你想讓動畫與老舊的瀏覽器后向兼容,請考慮使用基于JavaScript的動畫,將在下面詳細(xì)講解它。
ngView
動起來接下來,讓我們?yōu)樵?a rel="nofollow" rel="external nofollow" target="_blank" target="_blank">ngView
內(nèi)部、路由之間的過渡添加一個動畫。
首先,讓我們給HTML添加一個新的CSS類,就像我們在上面的示例中所作的。這一次,不是使用ng-repeat
元素,而是把它添加到包含了ng-view
指令的元素上。為了做到這,我們需要對HTML代碼做一些小的改變,從而我們可以對我們的動畫,在視圖改變之間的動畫有更多的控制。
app/index.html
.
<div class="view-container">
<div ng-view class="view-frame"></div>
</div>
利用這個改變,ng-view
指令被嵌套在一個帶有view-container
CSS類的父元素內(nèi)部。這個類添加了一個position: relative
樣式,因此動畫過程中,ng-view
的定位相對于這個父元素。
在這里,讓我們?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; }
}
/* 別忘了供應(yīng)商的前綴! */
沒什么驚人的!僅僅是兩頁之間的一個簡單的漸顯和漸消效果。這里唯不尋常的東西是,在頁面之間實現(xiàn)軟切換動畫的時候,我們在前一頁(具有ng-leave
類的頁面)的上方使用絕對定位來定位下一頁(通過 ng-enter
來指定)。因此前一頁即將被刪除時,它是漸消淡出的,與此同時新頁漸顯現(xiàn)在它上面。
一旦離開動畫結(jié)束,元素會被移除;一旦進(jìn)入動畫結(jié)束 ,元素上的ng-enter
和ng-enter-active
CSS類會被移除,導(dǎo)致它用它的默認(rèn)CSS代碼重新呈現(xiàn)、重新定位(因此沒有一旦動畫結(jié)束就沒有絕對定位了)。這動作起來非常流暢,因此頁面在路由變化時流動自然,不會有任何跳動感。
應(yīng)用的CSS類(開始和結(jié)束類)與ng-repeat
很相像。每當(dāng)一個新頁面載入到ng-view
指令中時,將創(chuàng)建它自己的一個副本,下載模板并追加內(nèi)容。這確保了所有的視圖都包含在一個單獨的HTML元素中,該元素允許簡單的動畫控制。
要想了解更多關(guān)于CSS動畫的信息,請參閱Web 平臺文檔。
ngClass
動起來讓我們向應(yīng)用程序添加另一個動畫。切換到phone-detail.html
網(wǎng)頁,我們看到已經(jīng)有一個很棒的縮略圖交換器。通過點擊在網(wǎng)頁列列中的縮略圖,資料手機圖像就變了。但是我們可以如何在改變它的同時添加動畫呢?
讓我們先考慮一下?;旧?,當(dāng)你在一個資料圖上點擊時,你正在改變圖像的狀態(tài),以反映新選中的縮略圖。在HTML中指定狀態(tài)改變的最佳方法是使用樣式類。和以前很相像,我們使用的CSS樣式類以指定指定一個動畫,當(dāng)CSS類本身變化時動畫將發(fā)生。
當(dāng)選中了一個新的手機縮略圖時,狀態(tài)改變了,.active
CSS類添加到匹配的資料圖像上,并播放了動畫。
讓我們開始,在phone-detail.html
網(wǎng)賈上微調(diào)HTML代碼。注意我們已經(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>
就像縮略圖,我們使用迭代器來顯示所有的資料圖像作為一個列表,然而我們沒有變動任何迭代相關(guān)的動畫。而是,我們在ng-class
指令上保持關(guān)注,因為每當(dāng)active
類變成true,則它將應(yīng)用到元素上,將呈現(xiàn)為可見。否則,資料圖像將隱藏。在我們的案例中,總是有一個元素具有active
類,因此,任何時候總會有一款手機的資料圖像在屏幕上可見。
當(dāng)元素上添加了激活類的時候,先添加了active-add
類和adtive-add-active
類,以指示Angular引發(fā)一個動畫。當(dāng)元素上移除了激活類的時候,元素上應(yīng)用了active-remove
類和active-remove-active
,它們反過來又會觸發(fā)別的動畫。
要想確保手機圖像在頁面第一次加載時正確地顯示,我們還要微調(diào)詳情頁的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;
}
你可能認(rèn)為我們將創(chuàng)建另一個CSS可用的動畫。雖然我們可以那么做,但是還是讓我們抓住機會學(xué)習(xí)如何用abnimate
模塊方法創(chuàng)建JavaScript可用的動畫吧。
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)這個動畫。jQuery并不要求JavaScript動畫與AngularJS協(xié)作,但是我們將使用它,因為編寫你自己的JavaScript動畫庫超過了這個教程的范圍。想要了解更多關(guān)于jQuery.animate
的信息,請參閱jQuery文檔。
包含我們注冊過的類的元素,無論元素上添加了一個類還是移除了一個類,都會調(diào)用addClass
回調(diào)函數(shù)和removeClass
回調(diào)函數(shù);在本案例中,注冊過的類是.phone
。當(dāng)元素上添加了.active
類(通過ng-class
指令),將引發(fā)addClass
JavaScript回調(diào)函數(shù),該回調(diào)函數(shù)帶有一個參數(shù)element
。最后傳入的參數(shù)是done
回調(diào)函數(shù)。done
回調(diào)函數(shù)的目的是,通過調(diào)用該函數(shù),當(dāng)JavaScript動畫結(jié)束時,可以讓Angular知道。
removeClass
回調(diào)函數(shù)以同樣的方式起作用,但是是在一個類從元素上移除時觸發(fā)它。
在JavaScript回調(diào)函數(shù)中,你通過操縱DOM創(chuàng)建了該動畫。在上面的代碼中,這就是element.css()
和element.animate()
所做的事情。回調(diào)函數(shù)用500px
的偏移定位了下一個元素,把前一個項目和新的項目往上移500px
,使兩個項目一起動起來。這導(dǎo)致了一個仿傳送帶的動畫。當(dāng)animate
函數(shù)完成它的工作,它會調(diào)用 done
。
注意addClass
和removeClass
兩者都返回了一個函數(shù)。這是一個可選的函數(shù),當(dāng)動畫被取消時(同一個元素上發(fā)生了別的動畫)時或者動畫完成時,會調(diào)用這個函數(shù)。向這個函數(shù)傳遞一個布爾參數(shù),該參數(shù)讓開發(fā)者知道動畫是否被取消了。當(dāng)動畫完成時,這個函數(shù)可以用來做一些掃尾工作。
現(xiàn)在你學(xué)會了!我們在相對短的時間里創(chuàng)建了一個web應(yīng)用。在完結(jié)篇中我們將討論接下來何去何從。
更多建議: