vue 實現 循環播放音樂
本節講訴我項目中實現循環播放音樂的方法和遇到的問題。
實現 思路
播放音樂很簡單,使用多媒體標簽audio即可。而想要循環播放音樂也很簡單,使用js操作audio的src屬性即可。不過思路雖然簡單,但實際操作還是會遇到一些問題的。
獲取歌曲列表
使用axios,通過網易云后臺相應的接口獲得歌曲列表。
這是返回的數據:
vuex 保存 歌曲信息
項目中歌單頁面和播放器在不同路由中,所以使用狀態管理vuex 保存歌曲信息。
以下是歌曲相關信息:
store:{
//歌曲信息
play: {
idPlay: false;//是否播放
musicList: [],//播放列表
currentIndex: 0;//當前歌曲下標
currentSongId: 0;//當前歌曲ID
}
}
播放器基本代碼
html
<audio ref="audio" :src="currentSongUrl">audio>
audio屬性真時才會有控制面板,不過我仿照的網易云音樂,缺省的面板不符合要求,所以這里沒有面板,控件是另行編寫的,與本章的主題無關,這里就沒有寫出來。
data () {
return {
currentSongUrl: ''
}
使用data聲明響應式狀態,是歌曲地址單曲循環圖標怎么打出來,會在播放的時候由相應接口獲得。
watch: {
// 根據vuex狀態管理
async currentSongId (val) {
this.currentSong = this.musicList[this.currentIndex]
await this.getSongUrlData(val)
if (this.isPlay && this.$refs.audio.paused) {
this.$refs.audio.play()
}
}
},
methods: {
async getSongUrlData (id, level = 'standard') {

const result = await getSongUrl(id, level)
this.currentSongUrl = result.data[0].url
this.info.song = this.currentSong.name
this.info.singer = this.currentSong.ar[0].name
this.totalTime = this.currentSong.dt
}
},
使用偵聽器監聽,當歌曲id變化時獲取歌曲地址以及相關信息。
播放完畢自動播放下一首
audio播放結束的時候會觸發ended事件,監聽該事件,在該事件觸發的時候切換下一首歌曲。
添加事件
<audio ref="audio" :src="currentSongUrl" @ended="changeMusic(1)">audio>
事件函數
const changeMusic = (value) => {
store.commit('changeMusic', value)
}
vuex
changeMusic (state, value) {
//坐標加1
state.play.currentIndex += value
//超出邊界時循環
if (state.play.currentIndex < 0) {
state.play.currentIndex = state.play.musicList.length - 1
} else if (state.play.currentIndex >= state.play.musicList.length) {
state.play.currentIndex = 0
}
//更新歌曲ID
state.play.currentSongId = state.play.musicList[state.play.currentIndex].id
// vuex持久化
setLocalState(state)
}
歌曲跳躍單曲循環圖標怎么打出來,暫停和播放
<div class="footer__player__button">
//上一首歌
<span class="footer__player__button__previous iconfont"
@click="changeMusic(-1)">span>
//播放
<span class="footer__player__button__play iconfont" v-show="!isPlay"
@click="playMusic">span>
//暫停

<span class="footer__player__button__play iconfont" v-show="isPlay"
@click="pauseMusic">span>
//下一首歌
<span class="footer__player__button__next iconfont"
@click="changeMusic(1)">span>
div>
控件是由字體圖標實現的,代碼中看不出來,效果圖如下:
樣式表就不貼出來了,給按鈕添加點擊事件實現歌曲跳躍。暫停和播放按鈕會根據播放狀態顯示其中一個。
這是暫停和播放的事件函數:
const playMusic = () => {
store.commit('setIsPlay', true)
console.log(this.musicList)
}
const pauseMusic = () => {
store.commit('setIsPlay', false)
}
//vuex mutations
setIsPlay (state, value) {
state.play.isPlay = value
setLocalState(state)
},
然后還要監聽控制audio的播放和暫停
watch: {
// 根據vuex狀態管理
async currentSongId (val) {
this.currentSong = this.musicList[this.currentIndex]
await this.getSongUrlData(val)
if (this.isPlay && this.$refs.audio.paused) {
this.$refs.audio.play()
}
},
isPlay (val) {
// 無歌曲直接返回
console.log('isplay start')
if (this.currentSongUrl === '') return null
// 根據isPlay決定是否播放
if (val) {
this.$refs.audio.play()
} else {
this.$refs.audio.pause()

}
}
}
進度條實現
進度條的實現是由默認的播放面板得到的啟發,這里需要先說明接口,通過可以查看dom子樹,在谷歌瀏覽器可以按下Ctrl+Shift+c來查看網頁元素,如圖:
可以看到這里的進度條是input元素。
然后通過谷歌瀏覽器還可以看到完整的接口,下面是audio的DOM子樹:
這里我們可以看到audio到底是怎么實現的。
基本代碼
<div class="footer__player__progress">
//當前時間
<div class="footer__player__progress__current-time">
{{formatCurrentTime}}
div>
//進度條
<input type="range" min="0" :max="totalTime / 1000"
v-model="currentTime" class="footer__player__progress__item"
>
//總時間
<div class="footer__player__progress__total-time">
{{formatSongTime(totalTime)}}
div>
div>
data () {
return {
curretnSongUrl: '',
currentTime: 0,
//總時間,在歌曲信息中獲取
totalTime: 0,
}
}
computed:{
//計算屬性,返回00:00形式的當前時間
formatCurrentTime () {
const time = Math.trunc(this.currentTime) * 1000
function preToFixed (num, length = 2) {
return (Array(length).join(0) + num).slice(-length)

}
const length = Math.floor(time / 1000)
const minute = preToFixed(Math.floor(length / 60))
const second = preToFixed(length - minute * 60)
const result = `${minute}:${second}`
return result
}
}
進度條跟隨歌曲時間變化
首先要獲取歌曲的時間,可以通過audio的屬性獲得,但是要不斷獲取,audio事件由指定的事件更新,可以由該事件實現。
<audio ref="audio" :src="currentSongUrl"
@ended="nextMusic(1)"
@timeupdate="updateCurrentTime" preload="auto">audio>
//js
methods: {
updateCurrentTime () {
this.currentTime = this.$refs.audio.currentTime
}
}
audio. 變化 ,this. 會隨之變化,然后進度條value與this.綁定,進度條就動起來了。
這是效果,暫時只有圖片,等學會動圖再換上。
進度條拖動改變歌曲時間
事情做到這,就很簡單了,跟上面的思路一樣,在input 事件中將value 映射到audio.上就行。
<div class="footer__player__progress">
//當前時間
<div class="footer__player__progress__current-time">
{{formatCurrentTime}}
div>
//進度條
<input type="range" min="0" :max="totalTime / 1000"
v-model="currentTime" class="footer__player__progress__item"
@change="changeCurrentTime">//添加事件
//總時間
<div class="footer__player__progress__total-time">
{{formatSongTime(totalTime)}}
div>
div>
methods: {
async getSongUrlData (id, level = 'standard') {
const result = await getSongUrl(id, level)
console.log(result)
this.currentSongUrl = result.data[0].url
this.info.song = this.currentSong.name
this.info.singer = this.currentSong.ar[0].name
this.totalTime = this.currentSong.dt
},
updateCurrentTime () {
this.currentTime = this.$refs.audio.currentTime
},
//改變歌曲當前時間
changeCurrentTime (val) {
this.$refs.audio.currentTime = val.target.value
}
}
這里給動圖保留一個位置:
至此,循環播放的功能就基本實現了。
拖動進度條彈回問題
剛剛發現拖動進度條有問題,有時候會自動彈回去,可能是文件沒完全加載,拖到沒加載部分就會自動彈回去。但是我發現停止播放的時候基本不會回彈,所以可以先暫停再播放。
methods: {
async getSongUrlData (id, level = 'standard') {
const result = await getSongUrl(id, level)
console.log(result)
this.currentSongUrl = result.data[0].url
this.info.song = this.currentSong.name
this.info.singer = this.currentSong.ar[0].name
this.totalTime = this.currentSong.dt
},
updateCurrentTime () {
this.currentTime = this.$refs.audio.currentTime
},
changeCurrentTime (val) {
this.$refs.audio.pause()
this.$refs.audio.currentTime = val.target.value
this.$refs.audio.play()
}
}
其他
參考鏈接:
接口