/** * 音频播放 Pinia Store * 跨页面共享音频播放状态,音频实例存放在 globalData 中确保单例 */ import { defineStore } from 'pinia' const AUDIO_LIST = [ { name: '河流', posterimg: '/static/device/heliu.png', url: '/static/audio/audio1.mp3' }, { name: '下雨', posterimg: '/static/device/heliu.png', url: '/static/audio/audio2.mp3' } ] // 通过 globalData 确保 InnerAudioContext 跨页面单例 function _getAudioCtx() { const app = getApp() if (!app.globalData) app.globalData = {} return app.globalData._audioContext || null } function _setAudioCtx(ctx) { const app = getApp() if (!app.globalData) app.globalData = {} app.globalData._audioContext = ctx } export const useAudioStore = defineStore('audio', { state: () => ({ audiolist: AUDIO_LIST, curAudioInx: 0, totalTime: 0, currentTime: 0, ispause: true, // true=暂停中, false=播放中 isShowDrawer3: false }), actions: { /** 初始化或恢复音频上下文(页面进入时调用) */ initAudio() { let ctx = _getAudioCtx() if (ctx) { // 已有实例,重新绑定事件监听 this._bindEvents(ctx) // 同步当前播放状态 if (!ctx.paused && ctx.src) { this.ispause = false this.currentTime = ctx.currentTime || 0 this.totalTime = ctx.duration || 0 } return } // 首次创建 ctx = uni.createInnerAudioContext() ctx.src = this.audiolist[this.curAudioInx].url ctx.loop = true _setAudioCtx(ctx) this._bindEvents(ctx) }, /** 绑定事件监听 */ _bindEvents(ctx) { // 先清除旧监听,避免重复绑定(部分平台新建ctx调用off会报错,需保护) try { ctx.offPlay && ctx.offPlay() ctx.offCanplay && ctx.offCanplay() ctx.offError && ctx.offError() ctx.offTimeUpdate && ctx.offTimeUpdate() ctx.offPause && ctx.offPause() ctx.offStop && ctx.offStop() } catch (e) { // 忽略:新创建的ctx可能没有已注册的监听器 } ctx.onPlay(() => { this.totalTime = ctx.duration || 0 this.ispause = false }) ctx.onCanplay(() => { this.totalTime = ctx.duration || 0 }) ctx.onError((res) => { console.log('播放错误', res.errMsg) }) ctx.onTimeUpdate(() => { this.currentTime = ctx.currentTime || 0 }) ctx.onPause(() => { this.ispause = true }) ctx.onStop(() => { this.ispause = true this.currentTime = 0 }) }, /** 页面离开时解绑事件(不销毁音频) */ detachEvents() { const ctx = _getAudioCtx() if (ctx) { try { ctx.offPlay && ctx.offPlay() ctx.offCanplay && ctx.offCanplay() ctx.offError && ctx.offError() ctx.offTimeUpdate && ctx.offTimeUpdate() ctx.offPause && ctx.offPause() ctx.offStop && ctx.offStop() } catch (e) { // 忽略 } } }, /** 切换播放/暂停 */ playAudio() { const ctx = _getAudioCtx() if (!ctx) return if (this.ispause) { ctx.play() } else { ctx.pause() } }, /** 切换音源(切换后主动播放) */ changeAudio(url, inx) { this.curAudioInx = inx this.ispause = false const ctx = _getAudioCtx() if (ctx) { ctx.stop() setTimeout(() => { ctx.src = url ctx.play() }, 300) } else { // 音频上下文不存在时,先初始化再播放 this.initAudio() const newCtx = _getAudioCtx() if (newCtx) { newCtx.src = url newCtx.play() } } }, /** 停止播放(模式切换时调用) */ stopAudio() { const ctx = _getAudioCtx() if (ctx) { ctx.stop() } this.ispause = true this.currentTime = 0 }, /** 彻底销毁音频(APP退出等极端场景) */ destroyAudio() { const ctx = _getAudioCtx() if (ctx) { ctx.stop() ctx.destroy() _setAudioCtx(null) } this.ispause = true this.currentTime = 0 this.totalTime = 0 }, /** 格式化时间 */ formatTime(totalSeconds) { totalSeconds = Math.floor(totalSeconds || 0) const minutes = Math.floor(totalSeconds / 60) const seconds = totalSeconds % 60 return minutes.toString().padStart(2, '0') + ':' + seconds.toString().padStart(2, '0') } } })