需求描述:
1.正常播放音频
2.可以滑动进度条
3.可以切换上一条,下一条音频
4.退出当前页或关闭小程序之后仍然可以正常播放
5.试听功能进入该播放页不可以播放上一条,下一条
6.退出该页面或小程序之后,再次回到该页面,播放条自动到当前播放进度
图二图三是关闭小程序之后微信页面的展示,可以通过悬浮关闭该音频。
参考文档
[小程序官方文档--背景音频]
⚠️ 使用小程序 BackgroundAudioManager,需要在 app.json配置相关参数
"requiredBackgroundModes": [
"audio"
]
代码
<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()
函数作用都已经在注释里标注了,有疑问的地方欢迎留言~~
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。