最近在寫一個艾美食艾音樂的微信小程序,其中有用到音樂播放的功能,基本播放切換功能已經實現,但是在反復切換歌曲、重新進入歌曲以及單曲循環、列表循環的測試過程中還是發生了bug,特此寫一篇文章,捋一下思路.(功能寫到后面小程序官方文檔將音頻這一塊全都更新了...)
坑點:由於小程序主體部分沒有app.wxml,這就導致小程序不能寫一些公共的頁面結構,如果有相同的部分,只能封裝成組件再引入.
在我們點擊歌曲列表中的某首歌的時候,會進入音樂播放界面,當我們退出音樂播放界面,歌曲仍然繼續播放,但此時由於退出當前路由,頁面data中的數據也已被銷毀,當我們再次進入播放界面相當於重新進行頁面加載,所以需要我們進行分情況判斷,播放歌曲變的較為復雜.
一.存放公共數據
在我們進入音樂播放界面之前要存一下:
當前專輯信息:album(Object)
當前播放列表:playList(Array)
當前播放歌曲的index:playIndex(Number)
播放模式:mode(String)
封面旋轉的角度:imgRotate(Number)
// app.js
globalData: { album: null, // 當前專輯信息 playList: [], // 當前播放列表 playIndex: 0, // 當前播放歌曲index mode: 'multiple', // 循環模式 multiple: 循環播放, single: 單曲循環 imgRotate: 0, // 封面旋轉角度
}
二.專輯列表
在專輯列表中點擊某個專輯,進入該專輯的歌曲列表,同時將 該專輯的信息 存入globalData.
// music.js
gotoSongList(e) { let item = e.currentTarget.dataset.type app.globalData.album = item // 將專輯信息存入globalData wx.navigateTo({ url: '../songlist/songlist' }) }
三.歌曲列表
在歌曲列表中點擊播放某首歌時,進入歌曲播放界面,同時將 該專輯的歌曲列表、當前播放歌曲的index 存入globalData.
// songlist.js // 播放歌曲 play(e) { app.globalData.playList = this.data.songList let index = e.currentTarget.dataset.index app.globalData.playIndex = index let url = '../player/player' wx.navigateTo({ url: url }) }
四.歌曲播放頁面
需要在data中存放一下信息:
當前歌曲信息:item(Object)
歌曲解析后的歌詞:parseLyric(String)
當前播放進度點的歌詞:lineLyric(String)
播放模式:mode(String)
封面旋轉的角度:imgRotate(Number)
進度條的值:SliderValue(Number)
音頻的長度:duration(Number)
音頻的播放位置:currentPosition(Number)
播放狀態:playStatus(Boolean)
const app = getApp(); var Base64 = require('../../utils/base64.js').Base64 var Lyric = require('../../utils/lyric-parse.js') Page({ data: { item: {}, // 當前歌曲信息 parseLyric: '', // 歌曲解析后的歌詞 lineLyric: '', // 當前播放進度點的歌詞 mode: '', // 播放模式 imgRotate: 0, // 封面旋轉的角度 sliderValue: 0, // 進度條的值 duration: 0, // 音頻的長度(單位:s) currentPosition: 0, // 音頻的播放位置(單位:s) playStatus: 0, // 播放狀態 timee: 0, // 定時器 根據歌曲進度改變slider進度 coverImg: '', // 封面圖片 isDel: false, // 當前列表是否只有一首歌曲 }, onLoad() { let item = app.globalData.playList[app.globalData.playIndex] let album = app.globalData.album // 獲取背景音頻信息 const backgroundAudioManager = wx.getBackgroundAudioManager() console.log(backgroundAudioManager, 'backgroundAudioManager') if (backgroundAudioManager.src == item.src) { // 繼續聽歌 console.log('繼續聽歌') } else { // 播放新歌 app.globalData.imgRotate = 0 console.log('播放新歌') backgroundAudioManager.title = item.name backgroundAudioManager.epname = album.name backgroundAudioManager.singer = item.author backgroundAudioManager.coverImgUrl = album.poster // 設置了 src 之后會自動播放 backgroundAudioManager.src = item.src } let lyric = Base64.decode(item.lyric) this.setData({ item: item, coverImg: album.poster, playStatus: !backgroundAudioManager.paused, duration: this.stotime(backgroundAudioManager.duration), currentPosition: this.stotime(backgroundAudioManager.currentTime), parseLyric: new Lyric(lyric, this.handleLyric), mode: app.globalData.mode, imgRotate: app.globalData.imgRotate }) this.data.parseLyric.seek(backgroundAudioManager.currentTime*1000) console.log(this.data.playStatus, 'playStatus') if (backgroundAudioManager.paused) { this.data.parseLyric.togglePlay() } else if (!this.data.timee) { this.toRotate() } backgroundAudioManager.onPlay(this.onPlay) // 監聽背景音頻播放事件 backgroundAudioManager.onPause(this.onPause) // 監聽背景音頻暫停事件 backgroundAudioManager.onTimeUpdate(this.onTimeUpdate) // 監聽背景音頻播放進度更新事件 backgroundAudioManager.onEnded(this.onEnded) // 監聽背景音頻自然播放結束事件 wx.setNavigationBarTitle({ title: item.name }) console.log(app.globalData.playList.length, 'app.globalData.playList.length') if (app.globalData.playList.length == 1) { this.setData({ isDel: true }) } }, handleLyric({lineNum, txt}) { // 歌詞回調 console.log(lineNum, txt, 'txt') this.setData({ lineLyric: txt }) }, onPlay() { const backgroundAudioManager = wx.getBackgroundAudioManager() console.log('onPlay') console.log(backgroundAudioManager.duration, 'backgroundAudioManager.duration') this.setData({ playStatus: true }) this.data.parseLyric.seek(backgroundAudioManager.currentTime*1000) this.toRotate() }, onPause() { clearInterval(this.data.timee) this.data.parseLyric.stop() console.log('onPause') this.setData({ playStatus: false }) }, switch() { // 切換歌曲播放狀態 if (this.data.playStatus) { // 切換為暫停狀態 const backgroundAudioManager = wx.getBackgroundAudioManager() backgroundAudioManager.pause() } else { // 切換為播放狀態 const backgroundAudioManager = wx.getBackgroundAudioManager() backgroundAudioManager.play() } }, onTimeUpdate() { const backgroundAudioManager = wx.getBackgroundAudioManager() let sliderValue = backgroundAudioManager.currentTime / backgroundAudioManager.duration * 100 this.setData({ currentPosition: this.stotime(backgroundAudioManager.currentTime), sliderValue: sliderValue, duration: this.stotime(backgroundAudioManager.duration) }) // this.data.parseLyric.seek(backgroundAudioManager.currentTime*1000) }, toRotate() { this.data.timee && clearInterval(this.data.timee) this.data.timee = setInterval(() => { app.globalData.imgRotate = app.globalData.imgRotate + 0.8 this.setData({ imgRotate: app.globalData.imgRotate }) }, 35) }, onEnded() { console.log('onEnded') this.setData({ playStatus: false }) if (this.data.mode == 'multiple') { this.cutNext() } else { const backgroundAudioManager = wx.getBackgroundAudioManager() // 設置了 src 之后會自動播放 backgroundAudioManager.src = this.data.item.src } }, slideChange(e) { // 拖動滑塊 let value = e.detail.value this.setData({ sliderValue: value }) const backgroundAudioManager = wx.getBackgroundAudioManager() let currentTime = (value / 100) * backgroundAudioManager.duration backgroundAudioManager.seek(currentTime) this.data.parseLyric.seek(currentTime*1000) }, cutPrev() { // 上一首 this.delSongChange('prev') }, cutNext() { // 下一首 this.delSongChange('next') }, delSongChange(type) { // 切換歌曲 if (this.data.duration !== 0 && !this.data.isDel) { if (app.globalData.playList.length > 1) { clearInterval(this.data.timee) } this.data.duration = 0 this.data.parseLyric.stop() if (type == 'prev') { if (app.globalData.playIndex > 0) { app.globalData.playIndex -- } else { app.globalData.playIndex = app.globalData.playList.length - 1 } } else { if (app.globalData.playIndex < app.globalData.playList.length - 1) { app.globalData.playIndex ++ } else { app.globalData.playIndex = 0 } } let item = app.globalData.playList[app.globalData.playIndex] wx.setNavigationBarTitle({ title: item.name }) const backgroundAudioManager = wx.getBackgroundAudioManager() backgroundAudioManager.title = item.name backgroundAudioManager.singer = item.author // 設置了 src 之后會自動播放 backgroundAudioManager.src = item.src let lyric = Base64.decode(item.lyric) this.setData({ item: item, playStatus: !backgroundAudioManager.paused, parseLyric: new Lyric(lyric, this.handleLyric) }) this.data.parseLyric.seek(backgroundAudioManager.currentTime*1000) console.log(this.data.playStatus, 'playStatus') if (backgroundAudioManager.paused) { this.data.parseLyric.togglePlay() } } }, // 改變播放模式 changeMode() { if (this.data.mode == 'multiple') { this.setData({ mode: 'single' }) } else { this.setData({ mode: 'multiple' }) } app.globalData.mode = this.data.mode }, // 時間格式轉換 stotime(s) { let t = '' if (s > -1) { let min = Math.floor(s / 60) % 60; let sec = Math.floor(s) % 60 if (min < 10) { t += '0' } t += min + ':' if (sec < 10) { t += '0' } t += sec } return t }, onUnload() { // 頁面卸載 const backgroundAudioManager = wx.getBackgroundAudioManager() backgroundAudioManager.onTimeUpdate() backgroundAudioManager.onPlay() backgroundAudioManager.onPause() this.data.parseLyric.stop() clearInterval(this.data.timee) }, })