crush-level-web/src/hooks/useAudioPlayer.ts

78 lines
1.8 KiB
TypeScript

import { useState, useRef, useCallback, useEffect } from 'react'
export interface UseAudioPlayerReturn {
currentlyPlaying: string | null
isPlaying: (id: string) => boolean
play: (id: string, audioUrl: string) => Promise<void>
stop: () => void
}
export function useAudioPlayer(): UseAudioPlayerReturn {
const [currentlyPlaying, setCurrentlyPlaying] = useState<string | null>(null)
const audioRef = useRef<HTMLAudioElement | null>(null)
const stop = useCallback(() => {
if (audioRef.current) {
audioRef.current.pause()
audioRef.current.currentTime = 0
}
setCurrentlyPlaying(null)
}, [])
const play = useCallback(async (id: string, audioUrl: string) => {
// 如果已经在播放相同的音频,则停止
if (currentlyPlaying === id) {
stop()
return
}
// 停止当前播放的音频
if (audioRef.current) {
audioRef.current.pause()
}
try {
// 创建新的音频实例
const audio = new Audio(audioUrl)
audioRef.current = audio
// 设置音频事件监听
audio.addEventListener('ended', () => {
setCurrentlyPlaying(null)
})
audio.addEventListener('error', (e) => {
console.error('Audio playback error:', e)
setCurrentlyPlaying(null)
})
// 开始播放
setCurrentlyPlaying(id)
await audio.play()
} catch (error) {
console.error('Failed to play audio:', error)
setCurrentlyPlaying(null)
}
}, [currentlyPlaying, stop])
const isPlaying = useCallback((id: string) => {
return currentlyPlaying === id
}, [currentlyPlaying])
// 清理函数
useEffect(() => {
return () => {
if (audioRef.current) {
audioRef.current.pause()
audioRef.current = null
}
}
}, [])
return {
currentlyPlaying,
isPlaying,
play,
stop,
}
}