最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Angular2 Service實(shí)現(xiàn)簡(jiǎn)單音樂(lè)播放器服務(wù)

 更新時(shí)間:2017年02月24日 09:00:00   作者:Yitim  
本篇文章主要介紹了Angular2 Service實(shí)現(xiàn)簡(jiǎn)單音樂(lè)播放器服務(wù) ,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧

引言

如果說(shuō)組件系統(tǒng)(Component)是ng2應(yīng)用的軀體,那把服務(wù)(Service)認(rèn)為是流通于組件之間并為其帶來(lái)生機(jī)的血液再合適不過(guò)了。組件間通信的其中一種優(yōu)等選擇就是使用服務(wù),在ng1里就有了廣泛使用,而ng2保持了服務(wù)的全部特性,包括其全局單例與依賴(lài)注入。今天就來(lái)實(shí)踐一下ng2的服務(wù)(Service)這一利器,來(lái)實(shí)現(xiàn)一個(gè)簡(jiǎn)單的音樂(lè)播放器,重點(diǎn)在于使用服務(wù)來(lái)進(jìn)行音頻的播放控制與全局范圍的調(diào)用。

一、基本項(xiàng)目準(zhǔn)備:

考慮到音頻播放是個(gè)比較通用的服務(wù),決定將其創(chuàng)建為一個(gè)單獨(dú)的模塊AudioModule,并且在里面新增音頻服務(wù)主文件audio.service.ts,通用的音頻控制中心組件audio-studio.component.ts,作為輔助的TS接口文件play-data.model.ts與audio.model.ts。

最終項(xiàng)目音頻部分的目錄結(jié)構(gòu)如圖所示:

二、創(chuàng)建服務(wù):

ng2的服務(wù),照官網(wǎng)的說(shuō)法來(lái)解釋?zhuān)鋵?shí)只是個(gè)帶有Injectable裝飾器的類(lèi)而已,沒(méi)有其他任何特殊的定義,所以非常簡(jiǎn)單,不過(guò)定義如此簡(jiǎn)單的服務(wù)卻可以完成非常多酷炫的功能。

在TypeScript下定義變量有了public與private的訪問(wèn)級(jí)區(qū)分,所以定義服務(wù)通常套路就是,定義服務(wù)內(nèi)使用的私有變量,在constructor構(gòu)造函數(shù)中進(jìn)行初始化操作,定義共有方法給服務(wù)的消費(fèi)者使用。

專(zhuān)注于音頻播放服務(wù)的場(chǎng)景,我們需要的私有變量有:

1.音頻對(duì)象

①用于通過(guò)JS進(jìn)行H5音頻的播放控制

2.播放列表數(shù)據(jù)

①服務(wù)內(nèi)部使用的播放列表概念,實(shí)際播放音頻時(shí)都是從此列表中播放音頻,服務(wù)的消費(fèi)者可以調(diào)用接口來(lái)操作此列表

3.正在播放音頻的參數(shù)

①音頻時(shí)長(zhǎng),當(dāng)前進(jìn)度以及播放模式(隨機(jī)播放之類(lèi))等

4.播放時(shí)的輪詢(xún)監(jiān)聽(tīng)變量

①用于音頻播放過(guò)程中自動(dòng)啟動(dòng)輪詢(xún),定時(shí)(每秒)更新播放參數(shù),當(dāng)音頻暫?;蛲V箷r(shí)取消此監(jiān)聽(tīng)

服務(wù)初始化時(shí)需要做的事情有:

1.創(chuàng)建音頻對(duì)象

①可直接使用document.createElement('audio'),但不需要將其添加到DOM中。

②后續(xù)的播放控制均使用此對(duì)象來(lái)操作。

2.初始化私有變量

①私有變量中播放列表是一個(gè)數(shù)組,成員的參數(shù)使用audio.model.ts來(lái)規(guī)范化,

②必須包含一個(gè)Url參數(shù)存放播放源,以及其他可選參數(shù)

③相同的播放參數(shù)也用一個(gè)play-data.model.ts來(lái)規(guī)范化

3.給音頻添加onplay、onpause、onend等播放事件的監(jiān)聽(tīng)

此服務(wù)提供的公有接口包括:

1. Toggle(audio)

①判斷傳入的音頻是否已在列表中,已存在則播放或暫停,若不存在則添加進(jìn)來(lái)并播放

2. Add()

①僅添加音頻到列表中

3. Remove()

①移除音頻出播放列表,需要考慮好移除后對(duì)播放隊(duì)列的影響,比如是否是正在播放的音頻被移除等等

4. Next()

5. Prev()

上一曲與下一曲操作,需要考慮到播放模式

6. Skip()

進(jìn)行播放進(jìn)度的跳轉(zhuǎn)

7. PlayList()

8. PlayData()

①用于暴露服務(wù)所維護(hù)的兩個(gè)數(shù)據(jù)(播放列表與播放參數(shù)),在指令中都是通過(guò)這兩個(gè)接口來(lái)呈現(xiàn)數(shù)據(jù)的

服務(wù)的完整代碼如下:

import { Injectable } from '@angular/core';
import { Audio } from './audio.model';
import { PlayData } from './play-data.model';

/**
 * 音頻服務(wù),只關(guān)心播放列表控制與進(jìn)度控制
 * 不提供組件支持,只提供列表控制方法接口及進(jìn)度控制接口
 */
@Injectable()
export class AudioService {
 // 主音頻標(biāo)簽
 private _audio: HTMLAudioElement;
 // 當(dāng)前列表中的音頻
 private playList: Audio[];
 // 當(dāng)前播放的數(shù)據(jù)
 private playData: PlayData;
 private listenInterval;
 /**
  * 創(chuàng)建新的音頻標(biāo)簽
  */
 constructor() {
  this._audio = document.createElement('audio');
  this._audio.autoplay = false;
  this._audio.onplay = () => {
   let that = this;
   this.listenInterval = window.setInterval(() => {
    that.playData.Current = that._audio.currentTime;
    that.playData.Url = that._audio.src;
    that.playData.During = that._audio.duration;
    that.playData.Data = that._audio.buffered &&
     that._audio.buffered.length ?
     (that._audio.buffered.end(0) || 0) :
     0;
   }, 1000);
   this.playData.IsPlaying = true;
  };
  this._audio.onended = () => {
   window.clearInterval(this.listenInterval);
   this.FillPlayData();
   this.playData.IsPlaying = false;
  };
  this._audio.onabort = () => {
   window.clearInterval(this.listenInterval);
   this.playData.Current = this._audio.currentTime;
   this.playData.Url = this._audio.src;
   this.playData.During = this._audio.duration;
   this.playData.Data = this._audio.buffered &&
    this._audio.buffered.length ?
    (this._audio.buffered.end(0) || 0) :
    0;
   this.playData.IsPlaying = false;
  };
  this._audio.onpause = () => {
   window.clearInterval(this.listenInterval);
   this.playData.Current = this._audio.currentTime;
   this.playData.Url = this._audio.src;
   this.playData.During = this._audio.duration;
   this.playData.Data = this._audio.buffered &&
    this._audio.buffered.length ?
    (this._audio.buffered.end(0) || 0) :
    0;
   this.playData.IsPlaying = false;
  };
  this.playData = { Style: 0, Index: 0 };
  this.playList = [];
 }

 /**
  * 1.列表中無(wú)此音頻則添加并播放
  * 2.列表中存在此音頻但未播放則播放
  * 3.列表中存在此音頻且在播放則暫停
  * @param audio
  */
 public Toggle(audio?: Audio): void {
  let tryGet = audio ?
   this.playList.findIndex((p) => p.Url === audio.Url) :
   this.playData.Index;
  if (tryGet < 0) {
   this.playList.push(audio);
   this.PlayIndex(this.playList.length);
  } else {
   if (tryGet === this.playData.Index) {
    if (this._audio.paused) {
     this._audio.play();
     this.playData.IsPlaying = true;
    } else {
     this._audio.pause();
     this.playData.IsPlaying = false;
    }
   } else {
    this.PlayIndex(tryGet);
   }
  }
 }

 /**
  * 若列表中無(wú)此音頻則添加到列表的最后
  * 若列表中無(wú)音頻則添加后并播放
  * @param audio
  */
 public Add(audio: Audio): void {
  this.playList.push(audio);
  if (this.playList.length === 1) {
   this.PlayIndex(0);
  }
 }

 /**
  * 移除列表中指定索引的音頻
  * 若移除的就是正在播放的音頻則自動(dòng)播放新的同索引音頻,不存在此索引則遞減
  * 若只剩這一條音頻了則停止播放并移除
  * @param index
  */
 public Remove(index: number): void {
  this.playList.splice(index, 1);
  if (!this.playList.length) {
   this._audio.src = '';
  } else {
   this.PlayIndex(index);
  }
 }

 /**
  * 下一曲
  */
 public Next(): void {
  switch (this.playData.Style) {
   case 0:
    if (this.playData.Index < this.playList.length) {
     this.playData.Index++;
     this.PlayIndex(this.playData.Index);
    }
    break;
   case 1:
    this.playData.Index = (this.playData.Index + 1) % this.playList.length;
    this.PlayIndex(this.playData.Index);
    break;
   case 2:
    this.playData.Index = (this.playData.Index + 1) % this.playList.length;
    this.PlayIndex(this.playData.Index);
    console.log('暫不考慮隨機(jī)播放將視為列表循環(huán)播放');
    break;
   case 3:
    this._audio.currentTime = 0;
    break;
   default:
    if (this.playData.Index < this.playList.length) {
     this.playData.Index++;
     this.PlayIndex(this.playData.Index);
    }
    break;
  }
 }

 /**
  * 上一曲
  */
 public Prev(): void {
  switch (this.playData.Style) {
   case 0:
    if (this.playData.Index > 0) {
     this.playData.Index--;
     this.PlayIndex(this.playData.Index);
    }
    break;
   case 1:
    this.playData.Index = (this.playData.Index - 1) < 0 ?
     (this.playList.length - 1) :
     (this.playData.Index - 1);
    this.PlayIndex(this.playData.Index);
    break;
   case 2:
    this.playData.Index = (this.playData.Index - 1) < 0 ?
     (this.playList.length - 1) :
     (this.playData.Index - 1);
    this.PlayIndex(this.playData.Index);
    console.log('暫不考慮隨機(jī)播放將視為列表循環(huán)播放');
    break;
   case 3:
    this._audio.currentTime = 0;
    break;
   default:
    if (this.playData.Index > 0) {
     this.playData.Index--;
     this.PlayIndex(this.playData.Index);
    }
    break;
  }
 }

 /**
  * 將當(dāng)前音頻跳轉(zhuǎn)到指定百分比進(jìn)度處
  * @param percent
  */
 public Skip(percent: number): void {
  this._audio.currentTime = this._audio.duration * percent;
  this.playData.Current = this._audio.currentTime;
 }

 public PlayList(): Audio[] {
  return this.playList;
 }

 public PlayData(): PlayData {
  return this.playData;
 }

 /**
  * 用于播放最后強(qiáng)行填滿(mǎn)進(jìn)度條
  * 防止播放進(jìn)度偏差導(dǎo)致的用戶(hù)體驗(yàn)
  */
 private FillPlayData(): void {
  this.playData.Current = this._audio.duration;
  this.playData.Data = this._audio.duration;
 }

 /**
  * 嘗試播放指定索引的音頻
  * 索引不存在則嘗試遞增播放,又失敗則遞減播放,又失敗則失敗
  * @param index
  */
 private PlayIndex(index: number): void {
  index = this.playList[index] ? index :
   this.playList[index + 1] ? (index + 1) :
    this.playList[index - 1] ? (index - 1) : -1;
  if (index !== -1) {
   this._audio.src = this.playList[index].Url;
   if (this._audio.paused) {
    this._audio.play();
    this.playData.IsPlaying = true;
   }
   this.playData.Index = index;
  } else {
   console.log('nothing to be play');
  }
 }
}

三、使用服務(wù):

接下來(lái)要使用服務(wù)了,再ng2中服務(wù)也要依賴(lài)具體的模塊,我們得音頻服務(wù)依賴(lài)的就是自己的音頻模塊,在模塊的provider列表中配置它:

@NgModule({
 imports: [ CommonModule, SharedModule ],
 declarations: [ AudioStudioComponent ],
 exports: [ AudioStudioComponent ],
 providers: [ AudioService ]
})

接下來(lái)要實(shí)現(xiàn)服務(wù)的消費(fèi)者——AudioStudioComponent 了,步驟如下:

1.在構(gòu)造函數(shù)中注入服務(wù):

constructor(public audio: AudioService) { }

2.使用Add()方法添加音頻:

audio.Add({Url: '/assets/audio/唐人街.mp3', Title: '唐人街-林宥嘉',
  Cover: '/assets/img/2219A91D.jpg'});
  audio.Add({Url: '/assets/audio/自然醒.mp3', Title: '自然醒-林宥嘉',
  Cover: '/assets/img/336076CD.jpg'});

Add方法添加的音頻如果是列表中僅有的一條音頻則會(huì)直接播放,所以如此添加兩條音頻會(huì)直接播放第一條音頻。

再在組件內(nèi)實(shí)現(xiàn)一個(gè)Skip方法用于進(jìn)度控制:

public Skip(e) {
  this.audio.Skip(e.layerX /
  document.getElementById('audio-total').getBoundingClientRect().width);
 }

現(xiàn)在運(yùn)行項(xiàng)目:

音頻播放器的樣式是崩塌的...因?yàn)檫@個(gè)組件是筆者另一個(gè)項(xiàng)目中直接copy過(guò)來(lái)了,在此demo項(xiàng)目中還沒(méi)加上移動(dòng)端rem適配,尷尬,不過(guò)大概的效果是展現(xiàn)出來(lái)了。 

完整項(xiàng)目代碼下載:angular2-demo_jb51.rar

四、總結(jié):

總的來(lái)說(shuō)ng2的服務(wù)光使用來(lái)說(shuō)難度不高,關(guān)鍵在于如何來(lái)完美發(fā)揮服務(wù)的特性,來(lái)做數(shù)據(jù)共享傳遞,以及封裝網(wǎng)絡(luò)請(qǐng)求等都是很好的選擇。另外本文沒(méi)有專(zhuān)門(mén)去講服務(wù)的一些問(wèn)題點(diǎn),但使用服務(wù)還是有一些需要注意的地方的,比如只能在單個(gè)模塊中的provider中聲明,盡量保持全局單例,以及在懶加載模塊中會(huì)創(chuàng)建子注入器等,實(shí)際項(xiàng)目中還是要解決一些問(wèn)題的。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

最新評(píng)論

霍州市| 乌拉特前旗| 兴义市| 济宁市| 澎湖县| 株洲县| 上蔡县| 汪清县| 苍溪县| 杭州市| 宁远县| 德化县| 耿马| 岑溪市| 松溪县| 景谷| 安化县| 崇阳县| 固安县| 绿春县| 西宁市| 广德县| 华坪县| 金华市| 托里县| 小金县| 宁乡县| 名山县| 隆林| 杭锦后旗| 财经| 苏尼特右旗| 清涧县| 吕梁市| 乌兰察布市| 喀喇沁旗| 安塞县| 陈巴尔虎旗| 九江县| 杭州市| 梨树县|