最近在写一个艾美食艾音乐的微信小程序,其中有用到音乐播放的功能,基本播放切换功能已经实现,但是在反复切换歌曲、重新进入歌曲以及单曲循环、列表循环的测试过程中还是发生了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)
},
})