// 加载动画组件 - 大猫头 + 波浪猫爪 import { useEffect, useState } from 'react'; import { useI18n } from '../i18n'; // ============================================================================ // 类型 & 配置 // ============================================================================ type Stage = 'analyzing' | 'generating' | 'refining' | 'rendering' | 'still-rendering'; interface LoadingSpinnerProps { stage: Stage; jobId?: string; onCancel?: () => void; } const STAGE_CONFIG = { analyzing: { key: 'loading.analyzing', start: 0, target: 20 }, generating: { key: 'loading.generating', start: 20, target: 66 }, refining: { key: 'loading.refining', start: 66, target: 85 }, rendering: { key: 'loading.rendering', start: 85, target: 97 }, 'still-rendering': { key: 'loading.stillRendering', start: 85, target: 97 }, } as const; // ============================================================================ // 进度算法 - 阶段目标值 + 持续增长安慰机制 // ============================================================================ function usePerceivedProgress(stage: Stage): number { const [progress, setProgress] = useState(0); const [prevStage, setPrevStage] = useState(stage); const [stageStartProgress, setStageStartProgress] = useState(0); const [enteredAt, setEnteredAt] = useState(Date.now()); // 阶段变化时记录起点,确保进度单调递增不回退 useEffect(() => { if (stage !== prevStage) { setPrevStage(stage); setEnteredAt(Date.now()); setStageStartProgress((current) => Math.max(current, progress, STAGE_CONFIG[stage].start)); } }, [stage, prevStage, progress]); // 按阶段目标值推进:前快后慢 + 长耗时每4秒+1% useEffect(() => { const id = setInterval(() => { const elapsed = (Date.now() - enteredAt) / 1000; const { target } = STAGE_CONFIG[stage]; const start = Math.max(stageStartProgress, STAGE_CONFIG[stage].start); const range = Math.max(0, target - start); // 前段快速接近目标(不一次冲到顶) const quickGain = range * 0.72 * (1 - Math.exp(-elapsed / 5)); // 长耗时阶段:超过10秒后每4秒+1%,避免“卡死感” const comfortGain = elapsed > 10 ? Math.floor((elapsed - 10) / 4) : 0; const next = Math.min(target, start + quickGain + comfortGain); setProgress((current) => Math.max(current, next)); }, 120); return () => clearInterval(id); }, [stage, enteredAt, stageStartProgress]); // processing 阶段最高展示 97%,完成后由结果态切换 return Math.min(97, progress); } // ============================================================================ // 子组件 // ============================================================================ /** 大猫头 SVG */ function CatHead() { return ( ); } /** 浮动猫头 */ function FloatingCat() { const [y, setY] = useState(0); useEffect(() => { let t = 0; let id: number; const animate = () => { t += 0.02; setY(Math.sin(t) * 5); id = requestAnimationFrame(animate); }; id = requestAnimationFrame(animate); return () => cancelAnimationFrame(id); }, []); return (
{t(key)}
{Math.round(progress)}%