feat: 更新音频和引导对话,优化用户体验
- 在多个场景中添加音频资源,增强学习互动性。 - 更新引导对话文本,确保信息准确性和一致性。 - 调整组件结构,清理不必要的代码,提升可维护性。 - 引入结束窗口,鼓励用户深入了解历史背景。
This commit is contained in:
1 parent
d7ff7d3a45
commit
6e106032fd
141 files changed
+1375
-479
No files matched your search
@@ -1,5 +1,6 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { useAudioManager } from "@/hooks/useAudioManager";
|
||||
|
||||
export interface NarrationProps {
|
||||
/** 旁白文本 */
|
||||
@@ -16,6 +17,10 @@ export interface NarrationProps {
|
||||
enableAnimation?: boolean;
|
||||
/** 文字动画速度(毫秒/字),默认 100ms */
|
||||
animationSpeed?: number;
|
||||
/** 覆盖层出现时是否自动播放音频(默认 true) */
|
||||
autoPlayAudio?: boolean;
|
||||
/** 音频分组 id,相同 channel 内互斥,不同 channel 可同时播放;不传则全局互斥 */
|
||||
audioChannelId?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -34,18 +39,33 @@ const Narration = ({
|
||||
className,
|
||||
enableAnimation = true,
|
||||
animationSpeed = 100,
|
||||
autoPlayAudio = true,
|
||||
audioChannelId,
|
||||
}: NarrationProps) => {
|
||||
const [displayedText, setDisplayedText] = useState("");
|
||||
const [isAnimating, setIsAnimating] = useState(true);
|
||||
const [audio, setAudio] = useState<HTMLAudioElement | null>(null);
|
||||
const [isSkipped, setIsSkipped] = useState(false);
|
||||
|
||||
// 旁白音频:默认自动播放,支持按 channel 分组互斥
|
||||
const { currentSrc, play, stop } = useAudioManager(
|
||||
audioChannelId
|
||||
? {
|
||||
mode: "channel",
|
||||
channelId: audioChannelId,
|
||||
}
|
||||
: { mode: "global" }
|
||||
);
|
||||
|
||||
// 文字动画效果
|
||||
useEffect(() => {
|
||||
if (!visible) {
|
||||
setDisplayedText("");
|
||||
setIsAnimating(true);
|
||||
setIsSkipped(false);
|
||||
// 旁白关闭时,如果当前正在播放该旁白音频,则停止
|
||||
if (audioSrc && currentSrc === audioSrc) {
|
||||
stop();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -76,7 +96,7 @@ const Narration = ({
|
||||
}, animationSpeed);
|
||||
|
||||
return () => clearInterval(timer);
|
||||
}, [text, visible, enableAnimation, animationSpeed, isSkipped]);
|
||||
}, [text, visible, enableAnimation, animationSpeed, isSkipped, audioSrc, currentSrc, stop]);
|
||||
|
||||
// 跳过动画
|
||||
const handleSkip = () => {
|
||||
@@ -85,27 +105,19 @@ const Narration = ({
|
||||
setIsSkipped(true);
|
||||
};
|
||||
|
||||
// 语音播报
|
||||
// 旁白出现时自动播放音频
|
||||
useEffect(() => {
|
||||
if (!visible || !audioSrc) return;
|
||||
if (!visible) return;
|
||||
if (!autoPlayAudio) return;
|
||||
if (!audioSrc) return;
|
||||
|
||||
const audioElement = new Audio(audioSrc);
|
||||
audioElement.play().catch((error) => {
|
||||
console.warn("Audio playback failed:", error);
|
||||
});
|
||||
setAudio(audioElement);
|
||||
|
||||
return () => {
|
||||
audioElement.pause();
|
||||
audioElement.currentTime = 0;
|
||||
};
|
||||
}, [visible, audioSrc]);
|
||||
play(audioSrc);
|
||||
}, [visible, autoPlayAudio, audioSrc, play]);
|
||||
|
||||
// 停止音频
|
||||
const handleClose = () => {
|
||||
if (audio) {
|
||||
audio.pause();
|
||||
audio.currentTime = 0;
|
||||
if (audioSrc && currentSrc === audioSrc) {
|
||||
stop();
|
||||
}
|
||||
onClose();
|
||||
};
|
||||
|
||||
Reference in new issue
Block a user