Angular2利用組件與指令實(shí)現(xiàn)圖片輪播組件
前言
如果說模塊系統(tǒng)是Angular2的靈魂,那其組件體系就是其軀體,在模塊的支持下渲染出所有用戶直接看得見的東西,一個(gè)項(xiàng)目最表層的東西就是組件呈現(xiàn)的視圖。
而除了直接看的見的軀體之外,一個(gè)完整的“生物”還需要有感覺器官,用來感知外界與其的交互,這就是指令要做的事情。
本文將使用Angular2提供的強(qiáng)大的組件與指令等功能制作出一個(gè)簡單的圖片輪播控件,繼續(xù)上文打的比方的話這就像是一個(gè)“器官”,功能是呈現(xiàn)圖片,并感知用戶的點(diǎn)擊或手勢來切換圖片。
一、創(chuàng)建組件
結(jié)束上文打的尷尬的比方,著眼于一個(gè)待開發(fā)的ng2項(xiàng)目,它有一個(gè)空白的特性頁面,現(xiàn)在需要在上面呈現(xiàn)一個(gè)圖片輪播窗口。
圖片輪播是一個(gè)需要給用戶看見的東西,所以應(yīng)該使用ng2的組件(Component)來實(shí)現(xiàn)它,并且這個(gè)功能較為通用,可以將其獨(dú)立出來方便以后再次使用到。
所以在項(xiàng)目中的共享模塊(SharedModule)下創(chuàng)建這個(gè)組件名為 slide-img.component。
使用ng2提供的組建裝飾器來將這個(gè)TypeScript模塊正式變身成ng2的組件:
@Component({
selector: 'my-slide-img',
templateUrl: 'slide-img.component.html',
styleUrls: ['slide-img.component.css'],
animations: [
trigger('imgMove', [
/** 不顯示 */
state('off', style({'display': 'none', 'z-index': '0', 'transform': 'translateX(0)'})),
/** 上一張圖片 */
state('prev', style({'z-index': '1',
'transform': 'translateX(-100%)'})),
/** 下一張圖片 */
state('next', style({'z-index': '2', 'transform': 'translateX(100%)'})),
/** 當(dāng)前圖片 */
state('on', style({'z-index': '3', 'transform': 'translateX(0)'})),
transition('prev=>on', [
animate('0.3s ease-in')
]),
transition('next=>on', [
animate('0.3s ease-in')
]),
transition('on=>prev', [
animate('0.3s ease-in')
]),
transition('on=>next', [
animate('0.3s ease-in')
])
])
]
})
export class SlideImgComponent { }
其參數(shù)其實(shí)已經(jīng)不能再明確了:
selector就是其使用時(shí)的標(biāo)簽名,
templateUrl即組件關(guān)聯(lián)的界面的模板,
styleUrls即僅在此組件內(nèi)生效的樣式表,
animations定義的是一套ng2動(dòng)畫規(guī)則。
講講最后的這個(gè)動(dòng)畫規(guī)則:
ng2的動(dòng)畫其實(shí)非常簡單,步驟為1.定義觸發(fā)器名,2.定義狀態(tài),3.定義切換樣式,4.將此觸發(fā)器應(yīng)用到具體的標(biāo)簽中,狀態(tài)作為觸發(fā)器的值傳入。
當(dāng)ng2檢測到動(dòng)畫狀態(tài)的值更改了,就會(huì)套用定義的切換樣式,用法思路還算比較明確(當(dāng)然實(shí)際使用時(shí)會(huì)有一些尷尬的小問題)
二、實(shí)現(xiàn)組件
既然是輪播圖片組件,要做的事情首先就得是傳入輪播圖片然后顯示出來。
使用過ng1的朋友一定還記得其在定義指令(angular.directive)的時(shí)候是通過scope參數(shù)(或者link)來傳入數(shù)據(jù)的,而ng2中使用的是Input裝飾器,使用的方法如下:
@Input() public imgs: SlideImg[];
使用了此裝飾器的變量imgs將可以在運(yùn)行時(shí)接收其他組件傳入的圖片列表。使用方法如下:
<my-slide-img [imgs]="imgs"></my-slide-img>
關(guān)于這里的方括號“[]”,ng2其實(shí)提供了多種方式來進(jìn)行組件模板中值的傳入,其中這種變量名用方括號包起來的方法就是其中之一,代表是輸入的值,而后面會(huì)見到的圓括號來包圍的方式,是代表輸出的值。
傳入了數(shù)據(jù)后,下一步就是要如何來顯示圖片到界面上了,沒錯(cuò)就是ng1中ng-for指令的升級版*ngFor,除了寫法外表面上的差別不大。
關(guān)于輪播圖片邏輯的具體實(shí)現(xiàn)邏輯,筆者使用的方式就是利用ng2動(dòng)畫的狀態(tài)切換,設(shè)置一個(gè)當(dāng)前圖片索引值current,*ngFor渲染的圖片將其索引與當(dāng)前索引比較,如果是相鄰的圖片則設(shè)為'prev'狀態(tài)與'next'狀態(tài),ng2會(huì)為其加上位置屬性為-100%或者100%,如果是當(dāng)前圖片則設(shè)為'on'狀態(tài),ng2會(huì)將其的位置屬性設(shè)為0,其余均設(shè)為'off'狀態(tài),ng2會(huì)直接將其隱藏,實(shí)現(xiàn)的邏輯很簡單,考慮也不算周全,筆者就不繼續(xù)解釋獻(xiàn)丑了。
最終的輪播圖片組件及其模板文件代碼如下:
<div class="imgs">
<img src="{{img.Url}}" alt="" class="img"
*ngFor="let img of imgs;let i=index"
(mySwipe)="Change($event)"
[@imgMove]="ImgState(i)">
</div>
<div class="btn" (click)="Prev()">Prev</div>
<div class="btn" (click)="Next()">Next</div>
.imgs{
position: relative;width: 100%;height: 15em;
overflow: hidden;
}
.img{
position: absolute;
width: 100%;
height: 100%;
background: pink;
transition: 0.2s;
}
.btn{
display: inline-block;
padding: 1em 2em;font-size: 1em;border-radius: 0.5em;
border: 1px solid #ddd;cursor: pointer;
margin: 1em;background: #eee;box-shadow: 0.1em 0.1em 0.2em #aaa;
}
.btn:active{
background: #eee;
box-shadow: none;
}
import { Component, OnInit, Input,
animate,
style,
transition,
trigger,
state,
HostListener
} from '@angular/core';
import { SlideImg } from './slide-img.interface';
@Component({
selector: 'my-slide-img',
templateUrl: 'slide-img.component.html',
styleUrls: ['slide-img.component.css'],
animations: [
trigger('imgMove', [
/** 不顯示 */
state('off', style({'display': 'none', 'z-index': '0', 'transform': 'translateX(0)'})),
/** 上一張圖片 */
state('prev', style({'z-index': '1',
'transform': 'translateX(-100%)'})),
/** 下一張圖片 */
state('next', style({'z-index': '2', 'transform': 'translateX(100%)'})),
/** 當(dāng)前圖片 */
state('on', style({'z-index': '3', 'transform': 'translateX(0)'})),
transition('prev=>on', [
animate('0.3s ease-in')
]),
transition('next=>on', [
animate('0.3s ease-in')
]),
transition('on=>prev', [
animate('0.3s ease-in')
]),
transition('on=>next', [
animate('0.3s ease-in')
])
])
]
})
export class SlideImgComponent {
@Input() public imgs: SlideImg[];
public current;
constructor() {
this.current = 0;
}
public ImgState(index) {
if (this.imgs && this.imgs.length) {
if (this.current === 0) {
return index === 0 ? 'on' :
index === 1 ? 'next' :
index === this.imgs.length - 1 ? 'prev' :
'off';
} else if (this.current === this.imgs.length - 1) {
return index === this.imgs.length - 1 ? 'on' :
index === this.imgs.length - 2 ? 'prev' :
index === 0 ? 'next' :
'off';
}
switch (index - this.current) {
case 0:
return 'on';
case 1:
return 'next';
case -1:
return 'prev';
default:
return 'off';
}
} else {
return 'off';
}
}
public Next() {
this.current = (this.current + 1) % this.imgs.length;
}
public Prev() {
this.current = this.current - 1 < 0 ? this.imgs.length - 1 : this.current - 1;
}
public Change(e) {
if (e === 'left') {
this.Next();
} else if (e === 'right') {
this.Prev();
}
}
}
其中有兩個(gè)地方為講到,一個(gè)是組件代碼引入了一個(gè)slide-img.interface 模塊,這個(gè)僅僅用來規(guī)范一下輪播圖片的格式,二是在html中還有一個(gè)節(jié)點(diǎn)名為(mySwipe)這就是接下來要講的輸出屬性,目前知道的它的作用是,當(dāng)用戶滑動(dòng)圖片時(shí),將觸發(fā)此節(jié)點(diǎn)配置的回調(diào)方法,所做的事情就是判斷發(fā)生了左滑事件還是右滑事件,分別觸發(fā)上一張圖或下一張圖的切換。
三、給輪播圖片控件加上手勢效果
輪播圖片在移動(dòng)端很需要加上手勢滑動(dòng)的效果,所以接下來要給這個(gè)輪播組件加上一個(gè)指令,用于響應(yīng)用戶的滑動(dòng)手勢。代碼如下:
import { Directive, Input, HostListener, Output, EventEmitter } from '@angular/core';
@Directive({ selector: '[mySwipe]' })
export class SwipeDirective {
@Output() public mySwipe = new EventEmitter<string>();
private touchStartX;
private touchStartY;
@HostListener('touchstart', ['$event']) public onTouchStart(e) {
this.touchStartX = e.changedTouches[0].clientX;
this.touchStartY = e.changedTouches[0].clientY;
}
@HostListener('touchend', ['$event']) public onTouchEnd(e) {
let moveX = e.changedTouches[0].clientX - this.touchStartX;
let moveY = e.changedTouches[0].clientY - this.touchStartY;
if (Math.abs(moveY) < Math.abs(moveX)) {
/**
* Y軸移動(dòng)小于X軸 判定為橫向滑動(dòng)
*/
if (moveX > 50) {
this.mySwipe.emit('right');
} else if (moveX < -50) {
this.mySwipe.emit('left');
}
} else if (Math.abs(moveY) > Math.abs(moveX)) {
/**
* Y軸移動(dòng)大于X軸 判定為縱向滑動(dòng)
*/
if (moveY > 50) {
this.mySwipe.emit('down');
} else if (moveY < -50) {
this.mySwipe.emit('up');
}
}
this.touchStartX = this.touchStartY = -1;
}
}
指令的聲明甚至簡單過組建的聲明,因?yàn)橹噶畈恍枰蕾囉谀硞€(gè)視圖模板,只需要有個(gè)指令名稱就足夠了。
需要關(guān)心的是指令中定義的輸出屬性:
@Output() public mySwipe = new EventEmitter<string>();
此屬性接收了上文組件中的Change($event)回調(diào)方法,在此指令中,所做的事情就是監(jiān)聽組件的滑動(dòng),收到滑動(dòng)事件后就觸發(fā)這個(gè)回調(diào),監(jiān)聽使用的是ng2的HostListener裝飾器,用法顯而易見了。
現(xiàn)在運(yùn)行起項(xiàng)目來看看效果吧(比較懶就不截動(dòng)圖了):

總結(jié)以及題外話:
本文主要使用了ng2幾個(gè)比較基本的功能——輸入屬性(Input裝飾器)、輸出屬性(Outut裝飾器)、HostListener裝飾器、幾個(gè)系統(tǒng)指令(ngFor)、ng2動(dòng)畫實(shí)現(xiàn)了一個(gè)比較基本的圖片輪播控件。
使用好ng2的組件以及指令能完成很多的事情,其需要學(xué)習(xí)的東西絕不僅限與本文提到的,包括其底層的渲染,也很值得去研究。
最后提一個(gè)尷尬的問題點(diǎn):
其實(shí)最初寫這個(gè)輪播圖片的時(shí)候想過要加上拖動(dòng)的,也就是圖片會(huì)隨手勢的滑動(dòng)實(shí)時(shí)更新位置。
但后來發(fā)現(xiàn)ng2的動(dòng)畫有個(gè)尷尬的地方,那就是一定會(huì)從初始狀態(tài)按照定義好的轉(zhuǎn)換效果變化到目標(biāo)狀態(tài)。實(shí)時(shí)滑動(dòng)需要我在滑動(dòng)過程中就改變圖片的位置,這樣的話在滑動(dòng)結(jié)束需要切換圖片時(shí),圖片居然強(qiáng)行回到了初始位置然后才開始轉(zhuǎn)換動(dòng)畫,一時(shí)還想不到繼續(xù)使用ng2動(dòng)畫來實(shí)現(xiàn)這種實(shí)時(shí)滑動(dòng)的完美解決辦法,實(shí)在是尷尬。
好了,以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作能帶來一定的幫助,如果有疑問大家可以留言交流。
相關(guān)文章
Angular2管道Pipe及自定義管道格式數(shù)據(jù)用法實(shí)例分析
這篇文章主要介紹了Angular2管道Pipe及自定義管道格式數(shù)據(jù)用法,結(jié)合實(shí)例形式詳細(xì)分析了Angular2管道與純管道相關(guān)概念、語法及使用技巧,需要的朋友可以參考下2017-11-11
AngularJS獲取json數(shù)據(jù)的方法詳解
這篇文章主要介紹了AngularJS獲取json數(shù)據(jù)的方法,結(jié)合實(shí)例形式詳細(xì)分析了AngularJS獲取json數(shù)據(jù)的詳細(xì)步驟、操作技巧與相關(guān)注意事項(xiàng),需要的朋友可以參考下2017-05-05
詳解angular2如何手動(dòng)點(diǎn)擊特定元素上的點(diǎn)擊事件
這篇文章主要介紹了詳解angular2如何手動(dòng)點(diǎn)擊特定元素上的點(diǎn)擊事件,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-10-10
AngularJS ng-repeat數(shù)組有重復(fù)值的解決方法
不知道大家是否遇到過這個(gè)問題,在當(dāng)Angular.JS ng-repeat數(shù)組中有重復(fù)項(xiàng)時(shí),系統(tǒng)就會(huì)拋出異常,這是該怎么做?本文通過示例代碼介紹了詳細(xì)的解決方法,有需要的朋友們可以參考借鑒,下面來一起看看吧。2016-10-10
用Angular實(shí)時(shí)獲取本地Localstorage數(shù)據(jù),實(shí)現(xiàn)一個(gè)模擬后臺(tái)數(shù)據(jù)登入的效果
這篇文章主要介紹了用ANGULAR實(shí)時(shí)獲取本地LOCALSTORAGE數(shù)據(jù),實(shí)現(xiàn)一個(gè)模擬后臺(tái)數(shù)據(jù)登入的效果的相關(guān)資料,非常不錯(cuò)具有參考借鑒價(jià)值,需要的朋友可以參考下2016-11-11

