音樂進度條組件progress-bar的設計與實現

新建組件文件(略)
引用組件(略)

組件界面設計

在組件內定義顯示內容
開始播放時間、總時間、滑動模塊

時間部分
在這裏插入圖片描述
使用數據從腳本中定義引用
在這裏插入圖片描述
滑動模塊效果方式
(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事件,所以功能在該事件中實現。
自動播放下一首類似於點擊播放下一首,所以我們讓組件調用頁面的時間進行完成。
onEndedthis.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的情況。

在進行優化之前,我的拖動是有一些小問題的,比如很難拖拽到最後,會自動回跳,但是優化過之後,我是可以流暢移動到最後一秒的

代碼在下邊
源碼地址

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章