播放器進度條由如下部分組成:
1.左邊的時間:已經(jīng)播放的時間長度
2.右邊的時間:歌曲總長度
3.中間的進度條
中間的進度條由如下部分組成:
1.表示總長度的黑色進度條。
? ? ?(靜態(tài)的,只需要用css就能實現(xiàn);可以點擊進度條,歌曲跳到點擊處播放)
2.黃色的進度條。
? ? ?(表示已經(jīng)播放的歌曲長度,需要js動態(tài)設(shè)置其width)
3.進度條按鈕。
? ? ? (按鈕所在的位置表示歌曲已播放的長度,使用transform的translate3d可以動態(tài)調(diào)? ?整按鈕的位置;按鈕可以拖拽,改變歌曲的進度;)
根據(jù)以上的分析,首先我們需要知道歌曲播放的時長,以此來改變黃色進度條和進度條按鈕的位置。
1.計算已播放時長占總時長的百分比
歌曲播放使用的是audio控件,可以通過控件的timeupdate事件得到(或者設(shè)置)歌曲已播放的時長this.currentTime =?e.target.currentTime
將百分比設(shè)置為計算屬性,得到百分比: percent? = this.currentTime / this.currentSong.duration
在父組件(player.vue)上傳入percent屬性,子組件(progress-bar.vue)中使用props接收參數(shù),由此得到百分比
2.通過百分比設(shè)置進度條和按鈕的位置
進度條總長度:barWidth =?黑色進度條長度 -?按鈕寬度
黃色進度條長度和按鈕位置的偏移量相同,使用offset變量表示:offset = percent * barWidth
設(shè)置黃色進度條寬度:
this.$refs.progress.style.width = `${offset}px`
設(shè)置按鈕偏移量
this.$refs.progressBtn.style[transform] = `translate3d(${offset}px, 0, 0)`
由于有多處地方可能使用到改變進度條長度和按鈕偏移量,因此使用一個函數(shù)對其進行包裝:
_offset(offset) {
? ? ? ? ? ? this.$refs.progress.style.width = `${offset}px`
? ? ? ? ? ? this.$refs.progressBtn.style[transform] = `translate3d(${offset}px, 0, 0)`
}
3.按鈕的拖拽事件
在按鈕所在div上監(jiān)聽如下事件:
@touchstart.prevent = "progressTouchStart"
@touchmove.prevent = "progressTouchMove"
@touchend = "progressTouchEnd"
其中,progressTouchStart事件中,需要記錄開始的位置,設(shè)置一個標(biāo)識變量表示已經(jīng)開始拖拽事件,記錄當(dāng)前黃色進度條的寬度,由于記錄的這些值可能會在move或者end函數(shù)中使用到,因此組件應(yīng)該維護一個變量對象來記錄這些值,這里使用 this.touch = {}
this.touch.initialed = true? //標(biāo)識變量,標(biāo)識已經(jīng)開始拖拽事件
this.touch.startX = e.touches[0].pageX? //開始位置
this.touch.left = this.$refs.progress.clientWidth? //黃色進度條寬度
progressTouchMove事件中,應(yīng)該根據(jù)拖拽的位置重新計算偏移量offset?
progressTouchMove(e) {
? ? ?if (!this.touch.initiated) {
? ? ? ? ? ?return
? ? ? }
? ? ?const deltaX = e.touches[0].pageX - this.touch.startX
? ? ?const offsetWidth = Math.min(this.$refs.progressBar.clientWidth - progressBtnWidth,? ? ? ? ? ?Math.max(0, this.touch.left + deltaX))
? ? ? this._offset(offsetWidth)
}
在progressEnd事件中,應(yīng)該將標(biāo)識標(biāo)量設(shè)置為false,并且通過拖拽結(jié)果重新計算歌曲已播放百分比
progressTouchEnd() {
? ? ? ?this.touch.initiated = false
? ? ? this._triggerPercent()? ? //?觸發(fā)重新計算百分比事件
}
其中_triggerPercent所做工作如下:
_triggerPercent() {
? ? const barWidth = this.$refs.progressBar.clientWidth - progressBtnWidth
? ? const percent = this.$refs.progress.clientWidth / barWidth
? ? ?this.$emit('percentChange', percent)
}
由此,父組件(player.vue)中可以監(jiān)聽percentChange事件,并且根據(jù)參數(shù)percent計算已播放事件,并且設(shè)置audio控件的currentTime
onProgressBarChange(percent) {
? ? ?const currentTime = this.currentSong.duration * percent
? ? ? this.$refs.audio.currentTime = currentTime
? ? ?//?這個if是為了解決:在暫停狀態(tài)拖動進度條,拖動之后還是暫時的問題(正常情況下 應(yīng)該是在拖動結(jié)束處繼續(xù)播放)
? ? ?if (!this.playing) {
? ? ? ? ? this.togglePlaying()
? ? ?}
}