import { useEffect, useState } from 'react' import { getStaticImage } from '@/lib/utils' import { useAudioManager } from '@/hooks/useAudioManager' export interface EndingOverlayProps { /** 是否可见 */ visible: boolean /** 中间显示的文本内容 */ text: string /** 点击继续回调 */ onContinue: () => void /** 文字动画速度(毫秒/字),默认 60ms */ animationSpeed?: number /** 结语对应的音频地址(可选) */ audioSrc?: string /** 覆盖层出现时是否自动播放音频(默认 true) */ autoPlayAudio?: boolean /** 音频分组 id,相同 channel 内互斥,不同 channel 可同时播放;不传则全局互斥 */ audioChannelId?: string } /** * 反思模块统一的结语覆盖层 * 使用 components/overlay/bg 作为背景 */ export const EndingOverlay = ({ visible, text, onContinue, animationSpeed = 60, audioSrc, autoPlayAudio = true, audioChannelId, }: EndingOverlayProps) => { const [displayedText, setDisplayedText] = useState('') const [isAnimating, setIsAnimating] = useState(false) // 结语音频:默认自动播放,支持按 channel 分组互斥 const { currentSrc, isPlaying, play, stop } = useAudioManager( audioChannelId ? { mode: 'channel', channelId: audioChannelId, } : { mode: 'global' } ) // 文字逐字动画 useEffect(() => { if (!visible) { setDisplayedText('') setIsAnimating(false) // 结语关闭时,如果当前正在播放该结语音频,则停止 if (audioSrc && currentSrc === audioSrc) { stop() } return } let currentIndex = 0 setDisplayedText('') setIsAnimating(true) const timer = setInterval(() => { if (currentIndex < text.length) { setDisplayedText(text.slice(0, currentIndex + 1)) currentIndex++ } else { setIsAnimating(false) clearInterval(timer) } }, animationSpeed) return () => clearInterval(timer) }, [visible, text, animationSpeed, audioSrc, currentSrc, stop]) // 覆盖层显示时自动播放音频 useEffect(() => { if (!visible) return if (!autoPlayAudio) return if (!audioSrc) return play(audioSrc) }, [visible, autoPlayAudio, audioSrc, play]) // 全部文字出现后自动关闭(如果有音频,需等待音频播放完成) useEffect(() => { if (!visible) return if (isAnimating) return if (!text || displayedText !== text) return // 如果有音频,需要等待音频播放完成 if (audioSrc) { // 检查当前播放的音频是否是我们期望的音频,且已播放完成 if (currentSrc === audioSrc && !isPlaying) { // 音频播放完成后,等待 800ms 再关闭 const timer = setTimeout(() => { onContinue() }, 800) return () => clearTimeout(timer) } // 音频还在播放,等待播放完成 return } // 没有音频,文字动画完成后等待 800ms 关闭 const timer = setTimeout(() => { onContinue() }, 800) return () => clearTimeout(timer) }, [visible, isAnimating, displayedText, text, audioSrc, currentSrc, isPlaying, onContinue]) if (!visible) return null return (