258 lines
7.0 KiB
JavaScript
258 lines
7.0 KiB
JavaScript
import {
|
||
defineStore
|
||
} from 'pinia';
|
||
import {
|
||
ref
|
||
} from 'vue';
|
||
|
||
export const useAudioStore = defineStore('audioStore', {
|
||
|
||
state: () => ({
|
||
// 音频实例(全局唯一)
|
||
audioInstance: null,
|
||
// 当前播放状态
|
||
isPlaying: false,
|
||
// 当前播放的音频地址
|
||
currentSrc: '',
|
||
// 加载状态
|
||
isLoading: false,
|
||
// 错误信息
|
||
errorMsg: '',
|
||
callbacks: {
|
||
onEnded: {}, // 播放完成回调
|
||
onError: {}, // 播放错误回调
|
||
onLoaded: {} // 音频加载完成回调(可获取时长)
|
||
}
|
||
}),
|
||
getters: {
|
||
// 获取当前音频实例(简化外部调用)
|
||
getAudioInstance(state) {
|
||
return state.audioInstance;
|
||
},
|
||
// 检查是否正在播放
|
||
isAudioPlaying(state) {
|
||
return state.isPlaying;
|
||
}
|
||
},
|
||
actions: {
|
||
// 初始化音频实例(确保全局唯一)
|
||
initAudio(audioId) {
|
||
if (!this.audioInstance) {
|
||
// 创建实例
|
||
this.audioInstance = uni.createInnerAudioContext();
|
||
// 绑定事件监听
|
||
this.bindAudioEvents(audioId);
|
||
}
|
||
return this.audioInstance;
|
||
},
|
||
|
||
// 绑定音频事件(统一管理生命周期)
|
||
bindAudioEvents(audioId) {
|
||
if (!this.audioInstance) return;
|
||
|
||
// 播放开始事件
|
||
this.audioInstance.onPlay(() => {
|
||
this.isPlaying = true;
|
||
this.isLoading = false;
|
||
this.errorMsg = '';
|
||
});
|
||
|
||
// 播放结束事件
|
||
this.audioInstance.onEnded(() => {
|
||
this.resetAudioState();
|
||
// 触发自定义回调
|
||
if (this.callbacks.onEnded[audioId] && typeof this.callbacks.onEnded[audioId] ===
|
||
'function') {
|
||
this.callbacks.onEnded[audioId]();
|
||
}
|
||
});
|
||
|
||
// 音频可以播放时触发(此时通常能获取到时长)
|
||
this.audioInstance.onCanplay(() => {
|
||
// 尝试获取时长(部分环境需要延迟一点)
|
||
// console.log('尝试获取时长(部分环境需要延迟一点)');
|
||
setTimeout(() => {
|
||
const duration = this.audioInstance.duration || 0;
|
||
console.log('尝试获duration', duration);
|
||
if (duration > 0) {
|
||
this.duration = duration;
|
||
this.isLoaded = true;
|
||
this.isLoading = false;
|
||
if (this.callbacks.onLoaded[audioId] && typeof this.callbacks.onLoaded[
|
||
audioId] === 'function') {
|
||
this.callbacks.onLoaded[audioId]({
|
||
duration: this.duration
|
||
});
|
||
}
|
||
}
|
||
}, 100); // 短暂延迟确保时长已加载
|
||
});
|
||
|
||
// 加载完成事件
|
||
this.audioInstance.onCanplay(() => {
|
||
this.isLoading = false;
|
||
});
|
||
|
||
// 错误事件
|
||
this.audioInstance.onError((err) => {
|
||
this.isPlaying = false;
|
||
this.isLoading = false;
|
||
this.errorMsg = `播放错误: ${err.errMsg || '未知错误'}`;
|
||
console.error('音频错误:', err);
|
||
if (this.callbacks.onError[audioId] && typeof this.callbacks.onError[audioId] === 'function') {
|
||
this.callbacks.onError[audioId](this.errorMsg); // 把错误信息传给外部
|
||
}
|
||
});
|
||
},
|
||
|
||
// 新增:预设置音频地址并加载(此时不播放,仅获取时长)
|
||
setAudioSrc(src, audioId) {
|
||
console.log('预设置音频地址并加载(此时不播放,仅获取时长)', src);
|
||
if (!src || src === this.currentSrc) return; // 地址不变则不重复加载
|
||
|
||
this.initAudio(audioId);
|
||
this.isLoading = true;
|
||
this.isLoaded = false; // 重置加载状态
|
||
this.currentSrc = src;
|
||
this.errorMsg = '';
|
||
try {
|
||
// 先停止当前播放并清空旧地址
|
||
// this.audioInstance.stop();
|
||
// this.audioInstance.src = '';
|
||
// 设置新地址并加载(不自动播放)
|
||
this.audioInstance.src = src;
|
||
// this.audioInstance.load(); // 手动触发加载
|
||
} catch (err) {
|
||
this.isLoading = false;
|
||
this.errorMsg = `设置音频失败: ${err.message}`;
|
||
}
|
||
},
|
||
|
||
// 修改:播放当前已设置的音频(需先调用setAudioSrc)
|
||
playAudio() {
|
||
if (!this.currentSrc) {
|
||
this.errorMsg = '请先设置音频地址';
|
||
return;
|
||
}
|
||
// 已在播放则暂停
|
||
console.log('已在播放则暂停', this.isPlaying);
|
||
if (this.isPlaying) {
|
||
this.pauseAudio();
|
||
return;
|
||
}
|
||
console.log('未加载完成则等待加载', this.isLoaded);
|
||
// 未加载完成则等待加载
|
||
if (!this.isLoaded) {
|
||
this.isLoading = true;
|
||
return;
|
||
}
|
||
|
||
// 执行播放
|
||
try {
|
||
this.audioInstance.play();
|
||
this.isPlaying = true;
|
||
this.isLoading = false;
|
||
} catch (err) {
|
||
this.isLoading = false;
|
||
this.errorMsg = `播放失败: ${err.message}`;
|
||
}
|
||
},
|
||
|
||
// 暂停播放
|
||
pauseAudio() {
|
||
if (this.audioInstance && this.isPlaying) {
|
||
this.audioInstance.pause();
|
||
this.isPlaying = false;
|
||
}
|
||
},
|
||
|
||
// 停止播放并重置状态
|
||
stopAudio() {
|
||
if (this.audioInstance) {
|
||
this.audioInstance.stop();
|
||
this.resetAudioState();
|
||
}
|
||
},
|
||
// 重置音频状态(保留实例,清空状态)
|
||
resetAudioState() {
|
||
this.isPlaying = false;
|
||
this.isLoading = false;
|
||
// this.currentSrc = '';
|
||
// 清空地址避免残留
|
||
if (this.audioInstance) {
|
||
this.audioInstance.src = '';
|
||
}
|
||
},
|
||
// 销毁音频实例(页面卸载时调用)
|
||
destroyAudio() {
|
||
if (this.audioInstance) {
|
||
// 移除所有事件监听
|
||
this.audioInstance.offPlay();
|
||
this.audioInstance.offEnded();
|
||
this.audioInstance.offCanplay();
|
||
this.audioInstance.offError();
|
||
// 停止并销毁实例
|
||
this.audioInstance.stop();
|
||
this.audioInstance.destroy();
|
||
// 重置所有状态
|
||
this.audioInstance = null;
|
||
this.resetAudioState();
|
||
this.errorMsg = '';
|
||
}
|
||
},
|
||
// 移除所有回调(组件卸载时调用)
|
||
removeCallbacks(audioId) {
|
||
this.callbacks.onEnded[audioId] = null;
|
||
this.callbacks.onError[audioId] = null;
|
||
this.callbacks.onLoaded[audioId] = null;
|
||
},
|
||
// 注册播放完成回调
|
||
onAudioEnded(callback, audioId) {
|
||
if (typeof callback === 'function') {
|
||
this.callbacks.onEnded[audioId] = callback;
|
||
}
|
||
},
|
||
|
||
// 注册播放错误回调
|
||
onAudioError(callback, audioId) {
|
||
if (typeof callback === 'function') {
|
||
this.callbacks.onError[audioId] = callback;
|
||
}
|
||
},
|
||
// 新增:注册音频加载完成回调(用于获取时长)
|
||
onAudioLoaded(callback, audioId) {
|
||
if (typeof callback === 'function') {
|
||
this.callbacks.onLoaded[audioId] = callback;
|
||
}
|
||
},
|
||
}
|
||
|
||
});
|
||
|
||
|
||
export const useAudioFun = (audioId) => {
|
||
const audioStore = useAudioStore();
|
||
|
||
return {
|
||
// 播放:忽略res参数,直接使用绑定的audioId
|
||
playAudio: (res) => audioStore.playAudio(audioId),
|
||
|
||
// 停止:无参数,直接调用
|
||
stopAudio: () => audioStore.stopAudio(),
|
||
|
||
// 移除回调:需要组件key,绑定audioId
|
||
removeCallbacks: () => audioStore.removeCallbacks(audioId),
|
||
|
||
// 设置音频源:第一个参数是src,自动绑定audioId
|
||
setAudioSrc: (src) => audioStore.setAudioSrc(src, audioId),
|
||
|
||
// 注册结束回调:第一个参数是回调,第二个是组件key,自动绑定audioId
|
||
onAudioEnded: (callback) => audioStore.onAudioEnded(callback, audioId),
|
||
|
||
// 注册错误回调:同上
|
||
onAudioError: (callback) => audioStore.onAudioError(callback, audioId),
|
||
|
||
// 注册加载完成回调:同上
|
||
onAudioLoaded: (callback) => audioStore.onAudioLoaded(callback, audioId)
|
||
};
|
||
}; |