import { reactive, ref } from 'vue' import { ElMessage } from 'element-plus' export default function useAudio(url, playEnd = () => {}) { return new Promise((resolve, reject) => { const state: any = reactive({ source: null, analyser: null, dataArray: null, animationId: null, pauseTime: 0, }) const volume = ref(0) const audioContext = new window.AudioContext() let audioDom: any = document.createElement('audio') audioDom.setAttribute('src', url) audioDom.setAttribute('crossorigin', 'anonymous') document.body.appendChild(audioDom) // 创建分析器节点 state.analyser = audioContext.createAnalyser() state.analyser.fftSize = 256 state.dataArray = new Uint8Array(state.analyser.frequencyBinCount) // 创建媒体元素源节点 state.source = audioContext.createMediaElementSource(audioDom) // 连接节点:源 -> 分析器 -> 目的地(扬声器) state.source.connect(state.analyser) state.analyser.connect(audioContext.destination) const pause = () => { cancelAnimationFrame(state.animationId) audioDom.pause() volume.value = 0 } const stop = () => { pause() document.body.removeChild(audioDom) audioDom = null playEnd() } audioDom.oncanplay = () => { resolve({ volume, play: () => { audioDom?.play() updateVolume() }, pause, stop, }) } audioDom.onended = () => { stop() } const updateVolume = () => { if (!state.analyser) return state.analyser.getByteFrequencyData(state.dataArray) // 计算平均音量 let sum = 0 for (let i = 0; i < state.dataArray.length; i++) { sum += state.dataArray[i] } const average = sum / state.dataArray.length // 将音量映射到0-100范围 volume.value = Math.min(100, Math.max(0, Math.round(average / 2.55))) // 继续动画 state.animationId = requestAnimationFrame(updateVolume) } }) }