Files
hunli/vite-tailwindcss/src/composables/useAudio.js
2026-07-31 09:05:15 +08:00

103 lines
2.9 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 { ref, computed, onUnmounted } from 'vue'
/**
* 背景音乐控制 composable支持多曲目
* - tracks: [{ url, name }] 播放列表
* - activeUrl: 当前选中的曲目地址
* - 处理浏览器自动播放限制:首次用户交互后再尝试播放
*/
export function useAudio() {
const isPlaying = ref(false)
const isReady = ref(false)
const tracks = ref([]) // [{ url, name }]
const activeUrl = ref('') // 当前选中曲目 url
let audio = null
let unlockHandler = null
const currentName = computed(() => {
const t = tracks.value.find((t) => t.url === activeUrl.value)
return t?.name || '背景音乐'
})
const ensureAudio = () => {
if (audio) return audio
audio = new Audio()
audio.loop = true
audio.volume = 0.5
audio.preload = 'auto'
audio.addEventListener('canplay', () => (isReady.value = true))
audio.addEventListener('play', () => (isPlaying.value = true))
audio.addEventListener('pause', () => (isPlaying.value = false))
return audio
}
const playUrl = async (url) => {
if (!url) return
const a = ensureAudio()
if (a.src !== url) a.src = url
activeUrl.value = url
try {
await a.play()
isPlaying.value = true
} catch (e) {
// 被浏览器拦截,等待用户手势
isPlaying.value = false
}
}
// 设置播放列表与默认选中项
const setTracks = (list, active) => {
tracks.value = Array.isArray(list) ? list.slice().filter((t) => t && t.url) : []
const first = tracks.value[0]?.url || ''
const chosen = active && tracks.value.some((t) => t.url === active) ? active : first
activeUrl.value = chosen
if (audio && isPlaying.value && chosen) playUrl(chosen)
}
// 切换当前播放曲目(若正在播放则立即切换)
const setActive = (url) => {
if (!tracks.value.some((t) => t.url === url)) return
if (isPlaying.value) playUrl(url)
else activeUrl.value = url
}
const play = () => playUrl(activeUrl.value)
const pause = () => {
if (audio) audio.pause()
isPlaying.value = false
}
const toggle = () => {
if (isPlaying.value) pause()
else play()
}
// 首次交互解锁自动播放
const armAutoplay = () => {
const handler = () => {
play()
removeUnlock()
}
unlockHandler = handler
window.addEventListener('pointerdown', handler, { once: true })
window.addEventListener('keydown', handler, { once: true })
}
const removeUnlock = () => {
if (!unlockHandler) return
window.removeEventListener('pointerdown', unlockHandler)
window.removeEventListener('keydown', unlockHandler)
unlockHandler = null
}
onUnmounted(() => {
pause()
removeUnlock()
if (audio) {
audio.src = ''
audio = null
}
})
return { isPlaying, isReady, tracks, activeUrl, currentName, setTracks, setActive, play, pause, toggle, armAutoplay }
}