Files
tra-app/src/store/audioStore.js
T
2025-09-08 20:40:49 +08:00

258 lines
7.0 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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)
};
};