微信小程序-音樂播放器+背景播放

需求描述:

1.正常播放音頻
2.可以滑動進度條
3.可以切換上一條,下一條音頻
4.退出當前頁或關閉小程序之後仍然可以正常播放
5.試聽功能進入該播放頁不可以播放上一條,下一條
6.退出該頁面或小程序之後,再次回到該頁面,播放條自動到當前播放進度
1572519150391.jpg
1572519090620.jpg
1572519055358.jpg

圖二圖三是關閉小程序之後微信頁面的展示,可以通過懸浮關閉該音頻。

代碼
    <view class="vp-book-adPlayer">
        <view class="adp-wrapper">
            <view class="apd-progress">
                <!-- <audio class="apd-pro-audio" src="{{music.src}}" action="{{audioAction}}" bindplay="audioPlayed" bindtimeupdate="audioTimeUpdated" controls></audio> -->
                <!-- 之前用的是audio標籤,但是爲了能夠滿足退出當前頁面或者關閉小程序,音頻仍需播放的需求,改成了背景音頻-->
                <slider class="apd-pro-slider" value="{{slideLen}}" bindchanging="stopSlider" bindchange="timeSliderChanged" selected-color="#20a3ff" block-size="12" block-color="#20a3ff" step="0.01"/>
                <view class="apd-pro-timer">
                    <view class="apd-pro-start">{{music.start}}</view>
                    <view class="apd-pro-leave">{{music.leave}}</view>
                </view>
            </view>
            <view class="apd-btn-box">
                <!-- 列表圖標-->
                <image class="apd-btn-list" src="../../img/player/ico-list.png" bindtap="jumpAudioList"></image>
                <!-- 上一條圖標-->
                <image class="{{hasPre ? 'apd-btn-left' : 'apd-btn-left apd-btn-no'}}" bindtap="playPer" src="../../img/player/ico-left.png"></image>
                <!-- 暫停和播放圖標-->
                <image class="apd-btn-player" bindtap="ppAudio" src="{{isPlay ? '../../img/player/ico-pause.png' : '../../img/player/ico-player.png'}}"></image>
                <!-- 下一條圖標-->
                <image class="{{hasNxt ? 'apd-btn-right' : 'apd-btn-right apd-btn-no'}}" bindtap="playNxt" src="../../img/player/ico-right.png"></image>
            </view>
        </view>
    </view>
    
    
    
    // pages/audioPlayer/audioPlayer.js
const api = require('../../service/http.js');
const util = require('../../utils/util.js')
var App = getApp()
const bgMusic = App.bgMusic  //創建背景音樂
Page({
  /**
   * 頁面的初始數據
   */
  data: {
    isTry: null,  // 是否是試聽狀態
    idx: 0, // 當前音頻(第一個-上一條按鈕不能點擊,最後一條,下一條按鈕不能點擊)
    albumCode: '',  // 當前音頻標識
    opusName: '',  // 當前專輯名字
    musicSrc: '',  
    singler: '',  // 當前作者
    audioMsg: {},  // 音頻信息(作者,封面,名字--僅展示)
    opusSalt: '',  // 當前音頻id
    isEnd: false, // 最後一條音頻結束時控制
    endVideoTime: '', // 最後一條音頻時長
    isPlay: true,  // 是否暫停音樂
    isStop: false,  // 是否停止音樂
    slideLen: 0, // 進度條初始值
    music: {  // 音頻信息--用來處理數據
      start: '00:00',
      leave:'',
      long: '',
      length: ''
    },
    hasPre: true,  // 是否有上一條音頻
    hasNxt: true,  // 是否有下一條音頻
    musicList: [], // 用來存儲音頻列表,存儲到本地,點擊上一條、下一條音頻時,不調用接口
    perMusicMsg: {},  // 進入頁面之後,就將上一條音頻,下一條音頻信息提取出來,方便直接點擊按鈕
    nxtMusicMsg: {},  // 同上
    isStopSlider: false  // 是否停止滾動條隨着音頻播放改變長度  -- 防止拖動滾動條時發生回退現象!!!
  },

  /**
   * 生命週期函數--監聽頁面加載
   */
  onLoad: function (options) {
    let book
    try {
      let value = wx.getStorageSync('ai_cloud_book')
      if (value) {
        book = JSON.parse(value)
      }
    } catch (e) {
      // Do something when catch error
    }
    this.setData({
      albumCode: decodeURIComponent(options.albumCode),
      musicSrc: decodeURIComponent(options.playerUrl),
      opusName: decodeURIComponent(options.playerName),
      singler: decodeURIComponent(options.playerSinger),
      isTry: Boolean(Number(options.isTry)),
      audioMsg: book,
      opusSalt: options.opusSalt,
      idx: Number(options.idx),
      'music.long': util.formatM(options.playerLong),
      'music.length': options.playerLong,
    })
  },

  /**
   * 生命週期函數--監聽頁面初次渲染完成
   */
  onReady: function () {
    // 正在播放-進入(重新進入當前頁面時)
    // this.data.opusSalt === App.globalData.opusSalt  判斷從列表進入時,想要播放的和正在播放的是否爲同一條音頻
    if(bgMusic.src && this.data.opusSalt === App.globalData.opusSalt) {
      this.audioInitAgain()
    } else {
      // 進入的和之前播放的不是同一條音頻  存儲將要播放的音頻id,並獲取將要播放的音頻數據,然後播放
      App.globalData.opusSalt = this.data.opusSalt
      this.getAudioSrc()
    }
    // 試聽只能聽第一條,上一條,下一條按鈕不可點擊
    if(this.data.isTry) {
      this.setData({
        hasPre: false,
        hasNxt: false
      })
    } else {
      this.musicListHandle()
    }
  },

  // 跳轉專輯列表-- 返回上一頁面
  jumpAudioList: function() {
    wx.navigateBack()
  },
  // 獲取音頻信息
  getAudioSrc: function() {
    bgMusic.src = this.properties.musicSrc
    bgMusic.title = this.data.opusName
    bgMusic.epname = this.data.opusName
    bgMusic.singer = this.data.singer
    // 最後一條音樂存儲一下音樂時長--- 播放結束後,不自動跳轉下一條音頻,播放按鈕變爲暫停,滾動條置0,endVideoTime展示該音頻時長
    this.setData({
      endVideoTime: this.data.music.long
    })
    this.audioInitPlay()
  },
  // 音頻-暫停/播放
  // isPlay: true: 播放狀態  false:暫停狀態
  // isStop:true :當不在播放頁面時,點擊關閉懸浮框的關閉按鈕  false: 懸浮框未關閉  --- 實際監聽時,監聽不到懸浮框關閉,但依然保留了該字段
  ppAudio: function (e) {
    let _isPlay = this.data.isPlay
    let _isStop = this.data.isStop
    if(_isStop) {
      this.getAudioSrc()
      this.setData({
        isPlay: true,
        isStop: false
      })
      return
    }
    if(_isPlay) {
      this.pauseAudio()
    } else if(this.data.isEnd){
      // 最後一條音頻 - 再次播放需要重新初始化
      this.setData({
        isEnd: false
      })
      this.getAudioSrc()
    } else {
      this.playAudio()
    }
    this.setData({
      isPlay: !_isPlay
    })
  },
  // 音頻實時信息  -->  
  audioTimeUpdated: function (e) {
    const startTime = e.currentTime
    const leaveTime = e.duration - startTime
    this.setData({
      "music.start": util.formatM(startTime),
      "music.leave": util.formatM(leaveTime)
    })
    if(!this.data.isStopSlider) {
      const proLen = (e.currentTime / e.duration * 100).toFixed(2)
      this.setData({
        slideLen: proLen
      })
    }
  },
  /**
   * !!! 解決滑動播放條時的卡頓問題 !!! --- start
   */
  // 禁止播放條隨着音樂播放滾動
  stopSlider: function () {
    this.setData({
      isStopSlider: true
    })
  },
  // 音頻播放條改變 - 手動滑動滾動條停止
  timeSliderChanged: function (e) {
    this.setData({
      isStopSlider: false
    })
    if (!this.data.music.length)
      return;

    var time = this.data.music.length * e.detail.value / 100;

    // 音頻跳轉到指定位置
    bgMusic.seek(time)
  },
  /**
   * !!! 解決滑動播放條時的卡頓問題  --- end
   */
  // 開始播放-首次進入
  audioInitPlay: function () {
    App.globalData.opusSalt = this.data.opusSalt

    //監聽音樂自然播放結束
    bgMusic.onEnded(() => {  
      // 如果沒有下一個直接賦值並禁止播放
      if(!this.data.hasNxt) {
        let _endTime = this.data.endVideoTime
        let idx = 0
        let _timer = setInterval(()=>{
          if(idx > 1) {
            clearInterval(_timer)
          }
          this.setData({
            isPlay: false,
            isEnd: true,
            "music.start": "00:00",
            "music.leave": _endTime
          })
          console.log(this.data.music)
          idx ++
        }, 50)
      } else {
        this.playNxt()
      }
    })

    //監聽音樂播放
    bgMusic.onPlay(() => {
      console.log('onPlay')
      if(this.data.music.start == "00:00") {
        this.setData({
          "music.leave": util.formatM(bgMusic.duration),
          isPlay: true
        })
      }
      this.playAudio()
    })

    // 監聽背景音頻暫停事件
    bgMusic.onPause(() => {
      this.setData({
        isPlay: false
      })
      // App.globalData.opusSalt = 0
    })

    //監聽背景音頻停止事件  --- 實際監聽時,監聽不到懸浮框關閉,但依然保留了該字段
    bgMusic.onStop(() => {
      this.stopAudio()
      App.globalData.opusSalt = 0
    })
  },
  // 開始播放-重複進入
  audioInitAgain: function() {
    // true - 暫停中  false - 播放中
    this.setData({
      endVideoTime: util.formatM(bgMusic.duration)
    })
    console.log(this.data.endVideoTime)
    if(bgMusic.paused) {
      bgMusic.play()
      let timer = setTimeout(() => {
        clearTimeout(timer)
        //監聽音樂播放
        bgMusic.onPlay(() => {
          this.playAudio()
        })
      }, 30)
    } else {
      bgMusic.onTimeUpdate(() => {
        this.audioTimeUpdated(bgMusic)
      })
    }
  },
  //暫停
  pauseAudio: function () {
    bgMusic.pause(); 
  },
  // 繼續播放
  playAudio: function () {
    // 監聽音頻播放進度
    bgMusic.onTimeUpdate(() => {  
      this.audioTimeUpdated(bgMusic)
    })
    bgMusic.play() //播放音樂
  },
  // 背景音樂浮窗關閉,重置數據  -- 實際監聽不到懸浮框關閉事件
  stopAudio: function() {
    this.setData({
      isStop: true,
      isPlay: false,
      "music.start": "00:00",
      "music.leave": this.data.music.long,
      slideLen: 0
    })
  },
  // 上一首
  playPer() {
    if(!this.data.hasPre) return
    wx.redirectTo({
      url: `XX/audioPlayer?albumCode=${encodeURIComponent(this.data.albumCode)}&playerUrl=${encodeURIComponent(this.data.perMusicMsg.opusUrl)}&playerName=${encodeURIComponent(this.data.perMusicMsg.opusName)}&playerSinger=${encodeURIComponent(this.data.singer)}&playerLong=${this.data.perMusicMsg.opusLength}&opusSalt=${this.data.perMusicMsg.opusSalt}&idx=${this.data.idx - 1}&isTry=0`
    })
  },
  // 下一首
  playNxt() {
    if(!this.data.hasNxt) return
    wx.redirectTo({
      url: `XXX/audioPlayer?albumCode=${encodeURIComponent(this.data.albumCode)}&playerUrl=${encodeURIComponent(this.data.nxtMusicMsg.opusUrl)}&playerName=${encodeURIComponent(this.data.nxtMusicMsg.opusName)}&playerSinger=${encodeURIComponent(this.data.singer)}&playerLong=${this.data.nxtMusicMsg.opusLength}&opusSalt=${this.data.nxtMusicMsg.opusSalt}&idx=${this.data.idx + 1}&isTry=0`
    })
  },
  // 音樂數據處理
  musicListHandle() {
    try {
      let value = wx.getStorageSync('ai_cloud_book_album')
      if (value) {
        let _book = JSON.parse(value)
        let _hasPer = Boolean(this.data.idx)  
        let _hasNxt = this.data.idx === _book.length - 1 ? 0 : 1
        let _perMusicMsg = {}
        let _nxtMusicMsg = {}
        if(_hasPer) _perMusicMsg = _book[this.data.idx - 1]
        if(Boolean(_hasNxt)) _nxtMusicMsg = _book[this.data.idx + 1]
        this.setData({
          musicList: _book,
          hasPre: _hasPer,
          hasNxt: Boolean(_hasNxt),
          perMusicMsg: _perMusicMsg,
          nxtMusicMsg: _nxtMusicMsg
        })
      }
    } catch (e) {
      // Do something when catch error
    }
    // wx.setStorageSync("ai_cloud_book_album", JSON.stringify(this.data.listenList))
  }
})
    
    

音頻-暫停/播放(信息配置) ppAudio()
音頻實時信息 audioTimeUpdated()
音頻播放條改變 timeSliderChanged()
開始播放-首次進入 audioInitPlay()
開始播放-重複進入 audioInitAgain()
暫停 pauseAudio()
繼續播放 playAudio()

函數作用都已經在註釋裏標註了,有疑問的地方歡迎留言~~

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