```javascript import React, { useState, useEffect, useRef, useMemo } from 'react'; const MingDynastyTimeline = () => { // --------------------------------------------------------------------------- // 1. 数据配置 (Data Configuration) // --------------------------------------------------------------------------- const EMPERORS = useMemo(() => [ { name: "朱元璋", temple: "太祖", era: "洪武", start: 1368, end: 1398, desc: "驱逐胡虏,恢复中华,立纲陈纪,救济斯民" }, { name: "朱允炆", temple: "惠宗", era: "建文", start: 1399, end: 1402, desc: "革除弊政,宽刑省狱,削藩失败,不知所终" }, { name: "朱棣", temple: "成祖", era: "永乐", start: 1403, end: 1424, desc: "迁都北京,天子守国门,郑和下西洋,修永乐大典" }, { name: "朱高炽", temple: "仁宗", era: "洪熙", start: 1425, end: 1425, desc: "停罢采买,平反冤狱,与民休息,仁宣之治始" }, { name: "朱瞻基", temple: "宣宗", era: "宣德", start: 1426, end: 1435, desc: "政治清明,国力鼎盛,绘事御笔,促织天子" }, { name: "朱祁镇", temple: "英宗", era: "正统", start: 1436, end: 1449, desc: "土木堡之变,北狩蒙尘,宠信王振" }, { name: "朱祁钰", temple: "代宗", era: "景泰", start: 1450, end: 1457, desc: "重用于谦,北京保卫战,挽狂澜于既倒" }, { name: "朱祁镇", temple: "英宗", era: "天顺", start: 1457, end: 1464, desc: "夺门之变复辟,冤杀于谦,废除殉葬" }, { name: "朱见深", temple: "宪宗", era: "成化", start: 1465, end: 1487, desc: "成化犁庭,设立西厂,专宠万贵妃,独爱方术" }, { name: "朱祐樘", temple: "孝宗", era: "弘治", start: 1488, end: 1505, desc: "弘治中兴,宽厚仁慈,一生只娶一妻" }, { name: "朱厚照", temple: "武宗", era: "正德", start: 1506, end: 1521, desc: "诛刘瑾,平宁王,自封大将军,嬉游无度" }, { name: "朱厚熜", temple: "世宗", era: "嘉靖", start: 1522, end: 1566, desc: "大礼议,二十年不上朝,崇道炼丹,海瑞罢官" }, { name: "朱载坖", temple: "穆宗", era: "隆庆", start: 1567, end: 1572, desc: "隆庆开关,解除海禁,俺答封贡,名为中兴" }, { name: "朱翊钧", temple: "神宗", era: "万历", start: 1573, end: 1620, desc: "张居正改革,万历三大征,怠政三十年,矿税之祸" }, { name: "朱常洛", temple: "光宗", era: "泰昌", start: 1620, end: 1620, desc: "一月天子,梃击案,红丸案,移宫案" }, { name: "朱由校", temple: "熹宗", era: "天启", start: 1621, end: 1627, desc: "木匠皇帝,魏忠贤专权,东林党争,辽事糜烂" }, { name: "朱由检", temple: "思宗", era: "崇祯", start: 1628, end: 1644, desc: "诛魏忠贤,勤政亡国,内忧外患,煤山自缢" }, ], []); // --------------------------------------------------------------------------- // 2. 状态与常量 (State & Constants) // --------------------------------------------------------------------------- const START_YEAR = 1360; const END_YEAR = 1650; const PIXELS_PER_YEAR = 40; // 增加间距以容纳更多信息 const VIEW_WIDTH = 1000; const VIEW_HEIGHT = 600; // 运动参数 const MOVE_SPEED = 0.8; // 正常移动速度 const PAUSE_DURATION = 2000; // 停留时间 (ms) // 核心动画状态 Ref (不触发重渲染) const stateRef = useRef({ currentYear: START_YEAR, isPlaying: true, isPaused: false, pauseStartTime: 0, lastPausedIndex: -1, // 记录上一次在哪位皇帝处停留过 }); // 渲染触发状态 const [renderYear, setRenderYear] = useState(START_YEAR); // --------------------------------------------------------------------------- // 3. 动画循环 (Animation Loop) // --------------------------------------------------------------------------- useEffect(() => { let requestID; const loop = (timestamp) => { const state = stateRef.current; if (state.isPlaying) { if (state.isPaused) { // --- 暂停逻辑 --- if (timestamp - state.pauseStartTime > PAUSE_DURATION) { // 暂停结束,恢复移动 state.isPaused = false; } } else { // --- 移动逻辑 --- state.currentYear += MOVE_SPEED; // 检查是否需要暂停 // 逻辑:遍历皇帝列表,如果当前年份经过了某位皇帝的"中间点",且该皇帝未被停留过 const activeEmperorIndex = EMPERORS.findIndex(emp => { const mid = (emp.start + emp.end) / 2; // 判定范围:当前年份在中间点附近 return Math.abs(state.currentYear - mid) < (MOVE_SPEED * 1.5); }); if (activeEmperorIndex !== -1 && activeEmperorIndex > state.lastPausedIndex) { // 触发暂停 state.isPaused = true; state.pauseStartTime = timestamp; state.lastPausedIndex = activeEmperorIndex; // 强制吸附到中间点,确保视觉居中 state.currentYear = (EMPERORS[activeEmperorIndex].start + EMPERORS[activeEmperorIndex].end) / 2; } // 循环播放检查 if (state.currentYear > END_YEAR + 10) { state.currentYear = START_YEAR; state.lastPausedIndex = -1; // 重置停留记录 } } // 更新 React 状态以渲染画面 setRenderYear(state.currentYear); } requestID = requestAnimationFrame(loop); }; requestID = requestAnimationFrame(loop); return () => { if (requestID) cancelAnimationFrame(requestID); }; }, [EMPERORS]); // 依赖项仅包含静态数据,确保 Loop 只启动一次 // --------------------------------------------------------------------------- // 4. 视觉计算 (Visual Calculations) // --------------------------------------------------------------------------- // 视口偏移:让 currentYear 位于屏幕中心偏左 (40% 处) const currentX = (renderYear - START_YEAR) * PIXELS_PER_YEAR; const translateX = (VIEW_WIDTH * 0.4) - currentX; // 背景装饰 (静态) const bgDecorations = useMemo(() => { return Array.from({ length: 20 }).map((_, i) => ({ id: i, x: i * 200, y: 100 + Math.random() * 400, size: 20 + Math.random() * 40, opacity: 0.05 + Math.random() * 0.05, type: Math.random() > 0.5 ? 'circle' : 'rect' })); }, []); // --------------------------------------------------------------------------- // 5. 渲染组件 (Render Component) // --------------------------------------------------------------------------- return (
{/* --- Definitions --- */} {/* --- Layer 1: Background --- */} {/* 装饰性背景元素 (视差移动) */} {bgDecorations.map(d => ( {d.type === 'circle' ? ( ) : ( )} ))} {/* --- Layer 2: Main Timeline (Moving) --- */} {/* 时间轴主线 */} {/* 皇帝节点渲染 */} {EMPERORS.map((emp, index) => { const empStartX = (emp.start - START_YEAR) * PIXELS_PER_YEAR; const empEndX = (emp.end - START_YEAR) * PIXELS_PER_YEAR; const empWidth = Math.max(empEndX - empStartX, 15); // 最小宽度保证可见性 const empCenterX = (empStartX + empEndX) / 2; // 状态计算 const isCurrent = renderYear >= emp.start && renderYear <= emp.end; const distance = Math.abs(renderYear - empCenterX); // 是否处于"聚焦"状态 (距离中心很近) const isFocus = distance < 50; // 样式变量 const color = isCurrent ? "#AA381E" : "#4b5563"; const scale = isCurrent ? 1.1 : (isFocus ? 1.05 : 1); const opacity = isCurrent ? 1 : (isFocus ? 0.8 : 0.4); const yPos = index % 2 === 0 ? -1 : 1; // 上下交错 return ( {/* 在位时间条 (轴上) */} {/* 详情卡片 */} {/* 连接线 */} {/* 信息容器 */} {/* 背景框 (仅激活时显示更明显的背景) */} {/* 庙号 & 名字 */} {emp.temple} · {emp.name} {/* 年号 */} {emp.era} {/* 在位时间数值 */} {emp.start} - {emp.end} {/* 详细描述 (仅聚焦时显示) */} {emp.desc} ); })} {/* 当前年份指示器 (Cursor) */} {/* 垂直贯穿线 */} {/* --- Layer 3: HUD / UI (Fixed) --- */} {/* 标题区域 */} 大明十六帝 Ming Dynasty Emperors Chronology (1368-1644) {/* 当前年份计数器 */} 公元 (Year) {Math.floor(renderYear)} {/* 底部进度条 */} Timeline Progress {/* 状态指示 */} {stateRef.current.isPaused ? "Reading..." : "Traveling..."}
); }; export default MingDynastyTimeline; ```