1. 程式人生 > >輕量級音樂播放器搭建 7

輕量級音樂播放器搭建 7

rom 接受 update 如果 tar 折騰 es6 結果 port

輕量級音樂播放器搭建 7

今天是國慶節,4點多起床去看升國旗儀式。本身是一件好事,參加儀式自己很受感動。但是感覺自己身體真的是不行了,不再像十七八歲甚至是前兩年了。參加完觀禮儀式,感覺頭痛得要命,眼睛也發脹,趕勁回去補覺,從七點多一直又睡到十點多。還是不行,到現在還是頭疼。我確實睡得比較晚,但現在大環境都是這個樣,住在宿舍裏,早睡與晚睡只是五十步與一百步的區別。當然了,這多半是為自己開脫,我還是比較晚睡的。。好了,不說了,進入正題。

昨天晚上出現了兩個問題,首先那個報錯的,我不知道為什麽報錯顯示有新的請求:

  ncaught (in promise) DOMException: The play() request was interrupted by a new load request. https://goo.gl/LdLk22

我自己沒有請求這個地址,可能是src的鏈接有一些轉發什麽的。也定位不到報錯的位置,就是在快速切換歌曲的時候有幾率出現這種報錯,我看網上關於這個問題討論的也不多,解決方法更沒有找到有用的。折騰了半天,放棄了,現在不影響正常播放,那就先這樣吧。

另外一個問題就是播放記錄或者是播放列表的重復的問題。我想抽象出一個判斷是否重復的函數,再播放下一首歌的時候與添加播放記錄的時候進行調用,然後判斷之後再進行下一步的播放或者push進記錄。哦,補充一下如果播放下一首的歌曲是重復的那麽這種情況是在單曲循環的時候允許的,所以也就是說播放記錄的重復檢查也是有必要的。在common目錄中新建js目錄,用於儲存通用的非api請求的js模塊。其中新建isDuplicated.js文件,判斷歌曲時候重復可以判斷歌曲的id屬性是否相等:

  
export default function isDuplicated(firstSong, secondSong) {
return firstSong.id === secondSong.id
}

然後修改_playDefaultMusic()與其他相關代碼:

    ......
import isDuplicated from ‘js/isDuplicated‘
?
_playDefaultMusic () {
// 0代表單曲循環,1代表順序播放,2代表隨機播放
let lastMusicIndex = this.currentMusicIndex
if (this.$store.state.playMode === 2) {
// 生成隨機索引
this.currentMusicIndex = Math.floor(Math.random() * this.currentMusicList.length)
} else if (this.$store.state.playMode === 1) {
if (lastMusicIndex === this.currentMusicList.length - 1) {
this.currentMusicIndex = 0
} else {
this.currentMusicIndex = lastMusicIndex + 1
}
}
......
// 在隨機播放的時候判斷時候為重復歌曲
if(this.$store.state.playMode === 2) {
if (isDuplicated(this.currentMusicList[lastMusicIndex], this.currentMusicList[this.currentMusicIndex])) {
this._playDefaultMusic()
}
}
......

寫到這裏,插個內容。現在項目的控制臺中有很多的log,但是看起來比較亂,也不好快速尋找有用的信息,所以可以在服務端安裝一個插件colors或者chalk。如果是在瀏覽器的控制臺,可是使用格式化輸出。

然後就是對播放列表的去重,這個去重是不論什麽播放模式都需要去重的,單曲循環模式不能一直放好多的同一首音樂在已播放列表中。但是,蛋疼的是,嚴格模式下數組操作方法幾乎都不能用,於是進行以下修改,並且將更新已播放列表代碼抽象到一個新的函數中而不是在_playDefaultMusic函數中:

  
_updateHistory () {
// 判斷目前的歌曲是否與本地記錄的最後一條有重復,判斷播放記錄是否為空
if (this.history.length !== 0 &&
isDuplicated(this.currentMusicList[this.currentMusicIndex], this.history[this.history.length - 1])) {
console.log(‘%cDUPLICATED‘,"color: red; background: yellow; font-size: 24px;")
} else {
// 更新本地歷史紀錄
if (this.history.length === this.historyLength) {
for (let i = 1; i < this.historyLength; i++) {
this.history[i - 1] = this.history[i]
}
this.history[this.historyLength - 1] = this.currentMusicList[this.currentMusicIndex]
} else {
this.history.push(this.currentMusicList[this.currentMusicIndex])
}
}
console.log(this.history)
// 更新用戶歷史紀錄
},

弄完了以上的東西,就改繼續做上一首歌的切換了。寫到這裏就發現代碼可以優化,可以更加的模塊化,比如說之前的播放下一首歌是調用了_playDefaultMusic方法,方法中計算索引,再播放對應索引的音樂。那麽現在這個播放上一首歌可以利用播放對應索引的音樂,不過計算索引就不再是使用這個方法中一直使用的計算索引的代碼,而是通過其他的途徑獲取,所以現在的這個私有playDefaultMusic方法可以分成兩個函數。所以對此函數進行如下的修改:

  
_getNewIndex () {
// 0代表單曲循環,1代表順序播放,2代表隨機播放
let lastMusicIndex = this.currentMusicIndex
if (this.$store.state.playMode === 2) {
// 生成隨機索引
this.currentMusicIndex = Math.floor(Math.random() * this.currentMusicList.length)
} else if (this.$store.state.playMode === 1) {
if (lastMusicIndex === this.currentMusicList.length - 1) {
this.currentMusicIndex = 0
} else {
this.currentMusicIndex = lastMusicIndex + 1
}
}
// 在隨機播放的時候判斷時候為重復歌曲
if(this.$store.state.playMode === 2) {
if (isDuplicated(this.currentMusicList[lastMusicIndex], this.currentMusicList[this.currentMusicIndex])) {
console.log(‘the music is duplicated to the last one‘);
this._playDefaultMusic()
}
}
this._updateHistory()
},
_changeMusicSrc (this.currentMusicList[this.currentMusicIndex].id) {
getMusicUrl(id)
.then((res) => {
console.log(‘開始獲取歌曲 ‘ + this.currentMusicList[this.currentMusicIndex].name + ‘ 播放地址‘)
this.currentMusicUrl = res.data[0].url
console.log(‘獲取到歌曲播放地址為 ‘ + this.currentMusicUrl)
})
},
_playDefaultMusic () {
this._getNewIndex()
this._changeMusicSrc()
},

其實就是把代碼換了一下位置。然後稍微修改一下這個_changeMusicSrc函數,讓他可以接受一個歌曲id參數,如果有參數的話就使用id參數,沒有的話就使用默認參數。可以使用ES6的新特性——默認參數,當讓這只是一個語法糖,自己實現也可以。

然後這樣就可以對切換到上一首歌愉快的實現了,分兩步,第一步獲得上一首歌的id,第二部根據id進行歌曲url也就是src屬性的請求。不過又有一個新的問題,當沒有歷史紀錄或者是歷史紀錄被不斷地上一首切換到了盡頭的時候,這時候應當對上一首歌的切換進行限制。如下:

  
cutToPrevSong () {
console.log(‘cutToPrevSong‘)
let prevMusic = this._getPrevMusic()
let id = prevMusic ? prevMusic.id : null
this._changeMusicSrc(id)
},
_getPrevMusic () {
if (this.history.length === 0) {
return null
} else {
return this.history[this.history.length - 1]
}
},

不過這樣經過實驗是切不回去上一首歌的,下圖是對切換到上一首歌進行兩次請求的日誌:

技術分享

因為邏輯上取的是history這個棧的棧頂元素,然而再播放當前的music的時候就已經把當前的音樂信息push進了history棧。所以無論怎麽切換,結果都是當前的歌曲。所以把代碼中對數組索引的操作的數字0與1改為1與2。然後這樣就完了嗎?沒有!因為現在的做法是僅僅將上一首歌曲的url換了過來。但是其他的一切都沒變,包括currentMusicIndex,專輯圖片的地址等等。所以以上的方法不是一個好方法,想了想這裏還略微有些復雜。必須分為三種播放模式討論,因為牽涉到currentMusicIndex,所以不分類討論會很混亂。大前提是有一個播放列表與一個播放記錄,首先討論單曲循環,這個時候的上一首是切換到播放列表中的上一首再進行單曲循環;然後是順序播放(如果是推薦FM的這種歌單而不是本地播放列表,也應當算作並且應當使用順序播放模式),這個時候不應當走播放記錄,而是應當與單曲循環一樣,將currentMusicIndex移動到列表中的上一首歌;最後是隨機播放,這個時候應當是使用播放記錄,但是播放之前的歌曲要牽扯到播放記錄修改也就比較麻煩,嚴格模式還不支持數組的操作,所以這就不好實現。或者是我的隨機播放的方法一開始就想錯了,不應當是每次對索引進行隨機選取,而是應當對整個一個播放列表隨機打亂順序然後順序播放,這樣的話實現上一首歌會簡單一些。那現在先不考慮隨機播放的上一首歌的問題,以後有時間再改。實現如下:

  
cutToPrevSong () {
console.log(‘cutToPrevSong‘)
// let prevMusic = this._getPrevMusic()
// let id = prevMusic ? prevMusic.id : null
// this._changeMusicSrc(id)
let lastMusicIndex = this.currentMusicIndex
if (lastMusicIndex === 0) {
this.currentMusicIndex = this.currentMusicList.length - 1
} else {
this.currentMusicIndex = lastMusicIndex - 1
}
this._changeMusicSrc()
this._updateHistory()
},
_getPrevMusic () {
if (this.history.length === 1) {
return null
} else {
return this.history[this.history.length - 2]
}
},

說實話現在代碼已經開始有些淩亂了,是我沒有先打好框架的原因。之前寫天氣預報應用就很好,先寫好框架,再寫具體的實現。

參考鏈接:

  1. console 對象

  2. console 打印樣式

  3. ES6 默認參數

輕量級音樂播放器搭建 7