前言
之前写了一个音频播放器,但是没时间去优化,最近又有一个需求,希望能够支持背景播放,但是之前使用的是innerAudio,不能够实现背景播放,于是干脆就使用backgroundAudioManager来重新实现了一下音频播放器,并且优化了一下,使得第一次点击进度条时能够直接从点击位置开始播放音频,播放完成后进度条会归零并暂停播放;将js封装了一下便于调用
预览图
代码片段
微信官方所推荐的使用方式(点击后打开开发工具):代码片段
详细代码
以下为具体的页面代码,如果上方代码片段链接失效,请自行手动复制:
关键代码,audioManager.js
//audioManager.js
//2019年8月15日09:52:09
// 实现的功能
//1.进入后获取音频时长; 2.播放暂停 3.进度条自动滚动,播放时间自动增加 4.在播放时拖动进度条后可跳转播放 5.播放完成后进度条归零并暂停
//bug&tip
//1.由于微信API wx.getBackgroundAudioManager();创建的背景音频对象在设置src后不能立刻pause();
// 所以获取duration时使用了 wx.createInnerAudioContext();
// 在用户点击播放按钮时才创建背景音频
//2.使用时,请按照微信官方文档,在app.json中配置支持背景播放的以下字段
// "requiredBackgroundModes":["audio"]
//获取全局背景音乐管理器
const manager = wx.getBackgroundAudioManager();
//存储当前播放歌曲信息
let audioData = {
src: '',
title: '',
singer: '',
epname: '',
coverImgUrl: '',
webUrl: '',
duration: 0,
startTime: 0
};
let audioInterval;
//是否需要重置数据
let resetAudio = true;
const init = (e, t) => {//初始化播放器
if (!e.src || !e.title) {
return;
}
audioData.src = e.src || '';
audioData.title = e.title || '';
audioData.singer = e.singer || '';
audioData.epname = e.epname || '';
audioData.coverImgUrl = audioData.coverImgUrl || '';
audioData.webUrl = e.webUrl || '';
audioData.startTime = e.startTime || 0;
resetAudio = true;
}
const createAudio = (e, t) => {//创建音频播放
if (!e.src) {
alert('没有音频地址')
return;
}
setAudio(e, t);
clearInterval(audioInterval);
audioInterval = setInterval(() => {
let backgroundAudio = t.data.backgroundAudio;
backgroundAudio.isPlaying = !manager.paused;
t.setData({
backgroundAudio
})
if (!manager.paused) {
let e = {
currentTime: manager.currentTime
}
changeAudioProgressBar(e, t);
}
}, 1000);
manager.onError((err) => {
console.log('播放错误');
console.log(err);
})
manager.onEnded(() => {//播放完成
resetAudio = true;
changeAudioProgressBar({ progress: 0 }, t);
pause(t);
})
}
const getDuration = (e, t) => {//获取音频总时长
if (!e.src) {
return;
}
const innerAudioContext = wx.createInnerAudioContext();//用于获取duration
innerAudioContext.src = e.src;//设置src
innerAudioContext.play();//播放一次以获取音频信息
innerAudioContext.pause();//暂停
innerAudioContext.onCanplay(() => {
var durationInterval = setInterval(() => {
if (innerAudioContext.duration) {
audioData.duration = innerAudioContext.duration;
let backgroundAudio = t.data.backgroundAudio;
backgroundAudio.duration = innerAudioContext.duration;
var min = parseInt(innerAudioContext.duration / 60), sec = parseInt(innerAudioContext.duration % 60);
//小程序无法使用padstart,采用以下方式补全时间格式
if (min.toString().length == 1) {
min = `0${min}`;
} else {
min = `${min}`
}
if (sec.toString().length == 1) {
sec = `0${sec}`;
} else {
sec = `${sec}`
}
backgroundAudio.durationTime = `${min}:${sec}`;
t.setData({
backgroundAudio
})
clearInterval(durationInterval);
}
}, 500)
})
}
const seek = (e, t) => {//跳转到该进度播放
if (!(e.currentTime || e.progress || e.currentTime == 0 || e.progress == 0)) {
console.log('没有参数,无法跳转');
return;
}
if (resetAudio) {
if (e.currentTime || e.currentTime == 0) {
audioData.startTime = e.currentTime;
} else {
audioData.startTime = parseInt(e.progress * audioData.duration / 100)
}
} else {
if (e.currentTime || e.currentTime == 0) {
manager.seek(parseInt(e.currentTime));
} else {
manager.seek(parseInt(e.progress * manager.duration / 100));
}
if (!manager.paused) {
play(t);
} else {
pause(t);
}
}
}
const changeAudioProgressBar = (e, t) => {//修改进度条等显示用的信息
if (!(e.currentTime || e.progress || e.currentTime == 0 || e.progress == 0)) {
console.log('没有参数,无法设置进度条');
return;
}
let duration, durationTime, currentDuration, currentDurationTime, progress;
duration = manager.duration;
if (e.currentTime || e.currentTime == 0) {
currentDuration = e.currentTime;
progress = parseInt(100 * currentDuration / duration);
} else {
progress = e.progress;
currentDuration = parseInt(progress * duration / 100);
}
let timeArr = [parseInt(duration / 60), parseInt(duration % 60), parseInt(currentDuration / 60), parseInt(currentDuration % 60)];
for (let i in timeArr) {
//小程序无法使用padstart,采用以下方式补全时间格式
if (timeArr[i].toString().length == 1) {
timeArr[i] = `0${timeArr[i]}`
} else {
timeArr[i] = `${timeArr[i]}`
}
}
currentDurationTime = `${timeArr[2]}:${timeArr[3]}`;
let backgroundAudio = t.data.backgroundAudio;
backgroundAudio.currentDuration = currentDuration;
backgroundAudio.currentDurationTime = currentDurationTime;
backgroundAudio.progress = progress;
t.setData({ backgroundAudio })
return backgroundAudio;
}
const play = (t) => {//播放
if (resetAudio) {
let e = {
src: audioData.src,
title: audioData.title,
startTime: audioData.startTime
}
createAudio(e, t);
}
manager.play();
if (t) {
let backgroundAudio = t.data.backgroundAudio;
backgroundAudio.isPlaying = true;
t.setData({
backgroundAudio
})
}
}
const pause = (t) => {//暂停
manager.pause();
if (t) {
let backgroundAudio = t.data.backgroundAudio;
backgroundAudio.isPlaying = false;
t.setData({
backgroundAudio
})
}
}
const stop = (t) => {//结束
manager.stop();
if (t) {
let backgroundAudio = t.data.backgroundAudio;
backgroundAudio.isPlaying = false;
t.setData({
backgroundAudio
})
}
}
const uninstall = (t) => {
stop();//停止播放
clearInterval(audioInterval);//清除计时器
audioData = {//重置数据
src: '',
title: '',
singer: '',
epname: '',
coverImgUrl: '',
webUrl: ''
};
resetAudio = true;
}
const setAudio = (e, t) => {//设置播放器数据
if (!e.src) {
alert('没有音频地址')
return;
}
console.log('setAudio的信息');
console.log(e);
manager.src = e.src;
manager.title = e.title;
manager.startTime = e.startTime;
manager.epname = e.epname || '';
manager.singer = e.singer || '';
manager.coverImgUrl = e.coverImgUrl || '';
manager.webUrl = e.webUrl || '';
//将音频数据暂存
audioData.src = e.src;
audioData.title = e.title;
audioData.epname = e.epname || '';
audioData.singer = e.singer || '';
audioData.coverImgUrl = e.coverImgUrl || '';
audioData.webUrl = e.webUrl || '';
audioData.startTime = 0;
resetAudio = false;
pause(t);
}
const hideAudio = (t) => {//切换到背景播放,暂时没用到
}
const showAudio = (t) => {//切换到前台播放,暂时没用到
if (!manager.currentTime || !manager.duration) {
return;
}
}
const alert = (e) => {//提示
wx.showToast({
title: e,
icon: 'none',
mask: true
})
}
module.exports = {
init,
getDuration,
play,
pause,
stop,
seek,
hideAudio,
showAudio,
uninstall
}
index.js
//index.js
const app = getApp(), myAudio = require("../utils/audioManager.js");
Page({
data: {
audio:{//用来存储服务器传输过来的内容
src:'',
title:'',
coverImgUrl:''
},
backgroundAudio: {//实际正在播放的内容,必须配置在data中
image: "",
url: "",
name: "",
duration: "",
durationTime: "",
currentDuration: "",
currentDurationTime: "",
progress: "",
isPlaying: false
}
},
onLoad: function () {
//模拟从服务器获取数据
setTimeout(()=>{
let audio={
src:'http://rv01.sycdn.kuwo.cn/f79cf09a4426480dd5717af406275ffb/5d71f7df/resource/n3/2/9/591852463.mp3',
title:'比翼的羽根',
coverImgUrl:'https://goss.veer.com/creative/vcg/veer/800water/veer-146156021.jpg'
};
this.setData({
audio
})
this.initBackGroundAudio();
},2000);
},
//初始化音频
initBackGroundAudio() {
if (!this.data.audio.src) {
return
}
let audio = {//设置播放器属性,src与title为必填
src: this.data.audio.src,
title: this.data.audio.title,
coverImgUrl: this.data.audio.coverImgUrl
};
myAudio.init(audio, this);//初始化audio
myAudio.getDuration(audio, this);//获取视频长度
myAudio.pause();//暂停播放
},
dragAudioSlider(e) {//拖动进度条
myAudio.seek({
progress: e.detail.value
}, this)//跳转到该进度
myAudio.play(this);//播放
},
//自定义音频播放器
sliderChange(e) {
myAudio.seek({
progress: e.detail.value
}, this)//跳转到该进度
myAudio.play(this);//播放
},
//播放按钮
playAudio() {
if (this.data.backgroundAudio.isPlaying) {
myAudio.pause(this);
} else {
myAudio.play(this);
}
},
onUnload(){
myAudio.uninstall(this);//卸载播放器,重置播放器数据
}
})
index.wxml
<!-- index.wxml -->
<view class='audioPlayer'>
<view class='player'>
<image src='{{audio.coverImgUrl}}' class='audioBack' mode='aspectFill'></image>
<view class='audioControls'>
<view class='flex'>
<view class='bottom' catchtap='playAudio'>
<!-- 按钮 -->
<view wx:if="{{backgroundAudio.isPlaying}}">
<image src='../images/pause.png' />
</view>
<view wx:else>
<image src='../images/play.png' />
</view>
</view>
<view class='slider'>
<slider bindchange='dragAudioSlider' activeColor='red' block-size="12" value='{{backgroundAudio.progress}}' />
</view>
<view class='time'>
{{backgroundAudio.currentDurationTime||'00:00'}}/{{backgroundAudio.durationTime||'00:00'}}
</view>
</view>
</view>
</view>
</view>
index.wxss
/* index.wxss */
.flex{
display: flex;
}
.audioPlayer{
width: 100%;
height: 400rpx;
margin-bottom: 30rpx;
box-sizing: border-box;
padding: 20rpx 30rpx;
}
.player{
width: 100%;
height: 100%;
position: relative;
}
.audioBack{
width: 100%;
height: 100%;
}
.audioControls{
width: 100%;
height: 80rpx;
background: black;
opacity: .8;
position: absolute;
bottom: 0;
color: white;
font-size: 6pt;
line-height: 80rpx;
text-align: center;
}
.audioControls .bottom{
width: 60rpx;
height: 100%;
}
.audioControls .bottom image{
margin-top: 30%;
margin-left: 30%;
width: 40rpx;
height: 40rpx;
}
.audioControls .slider{
width: 520rpx;
height: 100%;
}
.slider slider{
width: 95%;
margin-left: 4%;
margin-right: 0;
}
.audioControls .time{
width: 120rpx;
height: 100%;
}
注意事项
1.使用时必须在使用的页面中引入audioManager.js,并且在data中设置好backgroundAudio的全部参数
2.必须要在页面onunload事件中卸载播放器,否则会导致interval一直存在占用内容,导致crash
3.需要在手机端预览,请填写appid
4.以下为播放暂停图片