新建組件文件(略)
引用組件(略)
組件界面設計
在組件內定義顯示內容
開始播放時間、總時間、滑動模塊
時間部分
使用數據從腳本中定義引用
滑動模塊效果方式
(1)官方自帶滑動組件
(2)自定義滑動元素
使用內置的滑動區域組件,構造一個可以移動的區域
屬性的說明在截圖註釋上。
這樣在加上樣式就可以構成一個顯示的進度條樣式了
樣式代碼請參考在github上的文件。
JS腳本邏輯設計
首先我們要獲取到進度條的長度
使用了很多內置函數,最後的exec
會按照輸入順序來一點一點輸出這個問題
//獲取進度條的寬度
_getMovableDis(){
const query=this.createSelectorQuery();//返回一個實例化對象,通過該對象獲取到寬度
query.select('.movable-area').boundingClientRect()//獲取到了movable-area的寬度
query.select('.movable-view').boundingClientRect()//獲取到了movable-view的寬度
query.exec((rect)=>{
//獲取執行所有的請求,按照上邊請求的順序返回一個數組,返回數組按順序輸出
movableAreaWidth=rect[0].width
movableViewWidth=rect[1].width
})
},
在我們初次加載時的有一個生命週期函數,我們肯定是需要在頁面加載時就呈現出來的()。
lifetimes:{
ready(){
this._getMovableDis();
this._backgroundBGM();
}
}
在獲取到寬度之後,我們還需要獲取到的肯定就是時間了,歌曲的總長度時間。
//獲取背景音樂
_backgroundBGM(){
backgroundAudioManager.onPlay(()=>{
})
backgroundAudioManager.onStop(()=>{//停止
})
backgroundAudioManager.onPause(()=>{//暫停
})
backgroundAudioManager.onWaiting(()=>{//監聽當前音頻,正在加載當中
})
backgroundAudioManager.onCanplay(()=>{//監聽背景音樂進入一個可以播放的狀態
//獲取到歌曲信息時出現了很多undefined的情況,所以爲了處理爲空的狀態,在獲取到的時候直接判斷一下當前獲取到的是否爲空,如果爲空就進行處理,設置一個定時器進行一秒的延時進行處理獲取時間
console.log(backgroundAudioManager.duration)
if (typeof backgroundAudioManager.duration!='undefined') {
this._setTime()
}else{
setTimeout(()=>{
this._setTime()
},1000)
}
})
backgroundAudioManager.onTimeUpdate(()=>{//監聽當前音樂的播放進度,只在前臺播放時運行
})
backgroundAudioManager.onEnded(()=>{//監聽結束時的狀態
})
backgroundAudioManager.onError(()=>{//監聽當前音樂播放錯誤時的轉態
wx.showToast({
title: '錯誤'+res.errCode,
})
})
},
在背景音頻的onCanplay
即監測歌曲可以播放狀態,我們在監測到歌曲可以播放時獲取歌曲最終時間。
但是在獲取歌曲時間使用了關鍵字backgroundAudioManager.duration
,但是這個關鍵字卻在獲取時會市場返回空值
解決辦法就是用定時器,多加載一秒的延時,可以有效解決問題,代碼如上圖onCanplay
中。
在不爲undefined的時候,我們獲取當前歌曲的時長
_setTime(){
const duration=backgroundAudioManager.duration//獲取時間
const durationFmt=this._dateFormat(duration);
this.setData({
['showTime.totalTime']:`${durationFmt.min}:${durationFmt.sec}`//爲數組中的某一個元素賦值的方法
})
},
綁定數據的方式爲對數組中的某一個元素賦值的解決辦法。
這樣獲取到的時間爲以秒爲單位,所以我們還應該格式化處理一下,可以讓他符合常用規範
_dateFormat(sec){
const min=Math.floor(sec/60)
sec=Math.floor(sec%60)
return{
'min':this._parse0(min),
'sec':this._parse0(sec)
}
},
這樣我們就可以獲取到正常顯示的值了,但是我們平時所用的歌單時間,不足十補0,所以我們應該讓我們的時間正常顯示補0
_parse0(sec){
return sec<10?'0'+sec:sec
}
實現結果如圖
進度條與播放時間關聯
首先我們通過控制檯輸出,知道了歌曲播放的時間觸發流程
知道onTimeUpdate
是會隨着歌曲的播放進度而不停改變的,所以我們在這裏寫我們的方法
先獲取到我們的已播放時間和總的時間const currentTime = backgroundAudioManager.currentTime; const duration = backgroundAudioManager.duration;
之後進行計算,設置一個全局變量,代表當前秒數let currentSec = -1
同時爲了減少事件的回調次數,將當前時間轉化爲字符串併成爲點風格currentTime.toString().split('.')[0]
並取到下標第一個元素,所以使用【0】
來獲取第一個,判斷他和當前秒數的關係
backgroundAudioManager.onTimeUpdate(() => { //監聽當前音樂的播放進度,只在前臺播放時運行
const currentTime = backgroundAudioManager.currentTime;
const duration = backgroundAudioManager.duration;
const sec=currentTime.toString().split('.')[0]
if ( sec!= currentSec) {
const currentTimeFmt = this._dateFormat(currentTime) //格式轉化
this.setData({
movableDis: (movableAreaWidth - movableViewWidth) * currentTime / duration,
progress: currentTime / duration * 100,
['showTime.currentTime']: `${currentTimeFmt.min}:${currentTimeFmt.sec}`
})
}
currentSec=sec
})
如果不相等,就做運算求出當前時間,如果相等就將時間賦給當前時間。movableDis
是有計算寬度得出來的當前移動距離。progress
爲進度移動距離
進度條拖拽
在movable-view
中觸發了一個事件,即在改變時觸發的事件bindchange="onChange"
另外在用戶拖動時結束我們需要獲取到最後結束的事件,完成過程,此時觸發一個原生事件bindtouchend="onTouchEnd"
爲了不頻繁的調用setData綁定數據,造成程序壓力過大,所以使用兩個函數,一個來獲取移動,一個判斷最後的綁定位置
先獲取改變的事件
onChange(event){
//判斷是否是拖動產生的移動效果
if (event.detail.source=='touch') {
this.data.progress=event.detail.x/(movableAreaWidth-movableViewWidth)*100//當前移動進度
this.data.movableDis=event.detail.x
}
},
將我們獲取的值,先放在獲取到的值中
爲什麼判斷是touch
呢,看下圖比較,都是在獲取改變進度條事件的event的時候,自然播放時,source是沒有值得,在我們拖動時會獲取到觸摸事件touch
所以我們通過判斷當前是否爲觸摸就可以求出detail.x
的值,X的值爲歌曲已播放秒數。所以餓哦們可以求出了對應的移動距離和時間。
在移動結束時進行數據綁定,所以有了onTouchEnd
事件
onTouchEnd(){
//對應時間獲取
const currentTimeFmt=this._dateFormat(Math.floor(backgroundAudioManager.currentTime))
this.setData({
progress:this.data.progress,
movableDis:this.data.movableDis,
['showTime.currentTime']:`${currentTimeFmt.min}:${currentTimeFmt.sec}`
})
backgroundAudioManager.seek(duration*this.data.progress/100)
},
除了綁定距離,肯定也要綁定時間seek
即爲當前移動的時間的定位,將我們獲取到的移動時間放置進去(以秒爲單位)。所以在我們將時間和距離都綁定沒有問題的,那麼就OK了currentTime爲全局變量,放置以秒爲單位的已播放時間
自動播放下一首
在歌曲播放結束後,自動播放下一首肯定是需要的,總不能一直重複播放
在播放事件中,歌曲播放結束時會最後觸發onEnded
事件,所以功能在該事件中實現。
自動播放下一首類似於點擊播放下一首,所以我們讓組件調用頁面的時間進行完成。
在onEnded
中this.triggerEvent('musicEnd')
通過內置函數觸發一個自定義事件。
在頁面自定義組件響應中,響應該事件並讓他發生下一首歌曲的事件bind:musicEnd="onXia"
將tab換成了我們自定義的事件,並讓他去觸發下一首的事件即可。
優化問題
在歌曲播放時,拖動進度條,可能會造成點擊事件中的代碼和監聽播放過程的代碼的衝突,即在進度條拖動的時候和onTimeUpdate事件衝突
解決代碼的衝突可以通過設置一個標誌位,讓他們這兩個有關聯的事件,或者說,監聽的內容了類似的事件同時只有一個運行。
設置一個全部的isMoving=false
來做判斷
當開始觸發移動時isMoving=true
,當事件結束,在onTouchEnd中將isMoving=false
而在onTimeUpdate事件中,判斷isMoving=false是否爲false,當是的時候才成立執行裏邊的事件。
但是在小程序中,每次觸發過onTouchEnd中將isMoving=false
的時候,還會觸發onPlay
事件,同時使得isMoving=true,所以我們還需要在isplay中進行設置,讓他在任何情況也不會無故觸發等於true的情況。
在進行優化之前,我的拖動是有一些小問題的,比如很難拖拽到最後,會自動回跳,但是優化過之後,我是可以流暢移動到最後一秒的
代碼在下邊
源碼地址