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 (
{/* 文本内容层 */}
{displayedText}
{/* 底部渐隐提示条,仅做视觉平衡 */} {/*
*/}
); };