最近公司在小程序的项目中应用到了该功能(官网地址),过程中也是出现一些问题,特此和前端小伙伴分享一波。
首先,创建一个pinia的js文件(官网描述-获取全局唯一的背景音频管理器)
状态部分
const audioList = ref([]) //播放列表
const currentIndex = ref(0) //播放索引
const isPlaying = ref(false) // 是否播放
const currentSong = ref(null) //当前播放歌曲
const currentTime = ref(0) //当前播放时间
初始化部分
const initAudioManager = () => {
backgroundAudioManager = uni.getBackgroundAudioManager()
backgroundAudioManager.onPlay(() => {
isPlaying.value = true;
});
backgroundAudioManager.onPause(() => {
// 暂停
console.log('pause')
isPlaying.value = false;
});
backgroundAudioManager.onStop(() => {
// 停止播放 (在实机的微信小程序里点击缩放的背景音频的 X 关闭)
isPlaying.value = false;
currentSong.value = null;
});
backgroundAudioManager.onEnded(() => {
// 当前歌曲结束
isPlaying.value = false;
playNext();
});
backgroundAudioManager.onPrev(() => {
// 在实机的微信小程序里的背景音频点开进入后点击上一首 (iOS only)
playPrev();
});
backgroundAudioManager.onNext(() => {
// 在实机的微信小程序里的背景音频点开进入后点击下一首 (iOS only)
playNext();
});
backgroundAudioManager.onTimeUpdate(function () {
//用于页面进度条显示
currentTime.value = Math.ceil(backgroundAudioManager.currentTime);
});
backgroundAudioManager.onError((res) => {
console.error("播放错误:", res);
uni.showToast({
title: "播放出错",
icon: "none",
});
});
};
功能部分
//播放
const playSong = (song, currentTime, rate=1) => {
//song audioList中的一条数据
//currentTime 当前播放的时间
//rate 当前播放的倍率
if (!song) return;
if (!song.src) {
getSongUrl(song, currentTime) //获取url 根据公司情况自行实现
return
}
currentSong.value = song;
backgroundAudioManager.playbackRate = rate
backgroundAudioManager.title = song.title;
backgroundAudioManager.singer = song.artist || "未知歌手";
backgroundAudioManager.epname = song.album || "未知专辑";
backgroundAudioManager.coverImgUrl = song.cover || `xxx.png`;
if(currentTime){
// startTime实现seek函数效果
backgroundAudioManager.startTime = currentTime
}
// 背景音频管理器实现切换的关键是给一个新的src
backgroundAudioManager.src = song.src; //当设置了新的 src 时,会自动开始播放
};
// 切换播放/暂停
const togglePlay = (playIndex,innerClickItemId,speed) => {
// 当前没播放-直接播放
if (!currentSong.value) {
playSong(audioList.value[currentIndex.value], 0, speed);
return;
}
// 当前存在播放 判断点击是否是当前播放的语音
if(不是 -直接播放){ //伪代码-根据公司实际业务情况实现
// backgroundAudioManager.playbackRate = speed
playSong(audioList.value[playIndex], 0, speed);
} else {
if ( 是 - 暂停或取消) {
backgroundAudioManager.pause();
} else {
backgroundAudioManager.play();
}
}
currentIndex.value = playIndex
clickItemId.value = innerClickItemId
};
// 上一首
const playPrev = () => {
if (currentIndex.value <= 0) {
currentIndex.value = audioList.value.length - 1;
} else {
currentIndex.value--;
}
playSong(audioList.value[currentIndex.value]);
};
// 播放下一首
const playNext = () => {
if (currentIndex.value >= audioList.value.length - 1) {
//列表播放完结束 可以设置currentIndex为0 循环播放
backgroundAudioManager.stop();
return
} else {
currentIndex.value++;
}
playSong(audioList.value[currentIndex.value]);
};
有坑的部分功能
// 跳转到指定时间
const seek = (time, innerClickItemId, pause) => {
const rate = backgroundAudioManager.playbackRate
if(!pause) {
// 停止后设置startTime再重新播放,直接用backgroundAudioManager的seek函数有问题会从头播放
backgroundAudioManager.stop();
setTimeout(()=>{
playSong(audioList.value[currentIndex.value], time, rate)
},100)
}
}
// 切换倍率
const changeSpeed = (rate, pause, innerClickItemId) => {
// 切换倍率同理,先停止后再初始化
const currentTime = backgroundAudioManager.currentTime
backgroundAudioManager.stop();
if(!pause) {
setTimeout(()=>{
playSong(audioList.value[currentIndex.value],currentTime, rate)
},100)
}
}
第一版实现中是通过直接将后端的get请求作为backgroundAudioManager的src来做的,然后通过每次给get请求的自定义query添加一个字符(如?date=1111,每次后面加1)这样可以不用stop只需暂停pause后再播放就能实现效果,但是这样写又会有问题(在一些机型上请求会报错:该内容因网络或其他原因暂时无法播放)查阅网上一些解决方案试了下不能解决该问题,后来改用这版