import React, { useState, useEffect, useRef, useMemo } from 'react'; const VIEW_WIDTH = 1000; const VIEW_HEIGHT = 600; const CENTER_X = 400; // 将主图稍微偏左,右侧留给文字 const CENTER_Y = 300; const RADIUS = 180; // 五行配置 const ELEMENTS = [ { id: 'wood', name: '木', label: 'Wood', color: '#10B981', desc: '生发、条达', organ: '肝 (Liver)' }, { id: 'fire', name: '火', label: 'Fire', color: '#EF4444', desc: '温热、升腾', organ: '心 (Heart)' }, { id: 'earth', name: '土', label: 'Earth', color: '#F59E0B', desc: '生化、承载', organ: '脾 (Spleen)' }, { id: 'metal', name: '金', label: 'Metal', color: '#6B7280', desc: '清洁、收敛', organ: '肺 (Lung)' }, { id: 'water', name: '水', label: 'Water', color: '#3B82F6', desc: '寒凉、滋润', organ: '肾 (Kidney)' }, ]; // 辅助函数:计算多边形顶点 const getPolygonVertex = (index, total, radius, offsetAngle = -Math.PI / 2) => { const angle = (Math.PI * 2 * index) / total + offsetAngle; return { x: CENTER_X + Math.cos(angle) * radius, y: CENTER_Y + Math.sin(angle) * radius, angle }; }; export default function TCMPincipleAnimation() { const [frame, setFrame] = useState(0); const requestRef = useRef(); const startTimeRef = useRef(Date.now()); // 动画状态 const [activeIdx, setActiveIdx] = useState(0); // 粒子系统 Ref,避免 React 渲染负担 const particlesRef = useRef( Array.from({ length: 20 }, (_, i) => ({ id: i, progress: Math.random(), speed: 0.005 + Math.random() * 0.005, pathIndex: Math.floor(Math.random() * 5) })) ); // 启动主循环 useEffect(() => { const animate = () => { const now = Date.now(); const elapsed = now - startTimeRef.current; // 更新帧计数用于驱动视觉变化 setFrame(elapsed); // 更新激活元素逻辑 (每3秒切换一个元素) const cycleDuration = 3000; const newActiveIdx = Math.floor((elapsed / cycleDuration) % 5); setActiveIdx(prev => (prev !== newActiveIdx ? newActiveIdx : prev)); // 更新粒子逻辑 particlesRef.current.forEach(p => { p.progress += p.speed; if (p.progress >= 1) { p.progress = 0; p.pathIndex = (p.pathIndex + 1) % 5; // 简单的循环流动 } }); requestRef.current = requestAnimationFrame(animate); }; requestRef.current = requestAnimationFrame(animate); return () => { if (requestRef.current) cancelAnimationFrame(requestRef.current); }; }, []); // 计算五行节点位置 const nodes = useMemo(() => { return ELEMENTS.map((el, i) => { const pos = getPolygonVertex(i, 5, RADIUS, -Math.PI / 2); // 从顶部开始 return { ...el, ...pos, index: i }; }); }, []); // 渲染太极图 (SVG Path) const renderTaiji = () => { const size = 80; // 随时间旋转 const rotation = frame * 0.05; return ( {/* 阴影/光晕 */} {/* 阳鱼 (白) */} {/* 阴鱼 (黑) - 这里用深灰色代表阴,白色背景上需要反差 */} {/* 鱼眼 */} ); }; // 渲染相生连线 (Pentagon borders) const renderConnections = () => { return nodes.map((node, i) => { const nextNode = nodes[(i + 1) % 5]; // 高亮当前流动的路径 const isActivePath = i === activeIdx; return ( {/* 基础连线 */} {/* 动态高亮连线 */} {isActivePath && } ); }); }; // 渲染相克连线 (Star shape inside) const renderControlLines = () => { return nodes.map((node, i) => { // 相克关系:木克土,土克水... (i + 2) const targetIndex = (i + 2) % 5; const targetNode = nodes[targetIndex]; return ( ); }); } // 渲染粒子 (Qi Flow) const renderParticles = () => { return particlesRef.current.map((p, i) => { // 只渲染当前激活路径相关的粒子,或者全部渲染但淡化 // 这里为了视觉效果,让粒子在所有相生路径上流动,但激活路径更亮 const startNode = nodes[p.pathIndex]; const endNode = nodes[(p.pathIndex + 1) % 5]; const px = startNode.x + (endNode.x - startNode.x) * p.progress; const py = startNode.y + (endNode.y - startNode.y) * p.progress; const isPathActive = p.pathIndex === activeIdx; return ( ); }); }; return (
{/* 背景网格装饰 */} {Array.from({ length: 11 }).map((_, i) => ( ))} {Array.from({ length: 11 }).map((_, i) => ( ))} {/* 标题区域 */} 中医五行原理 Five Elements Theory & Energy Flow {/* 主图层 */} {renderControlLines()} {renderConnections()} {renderTaiji()} {renderParticles()} {/* 节点渲染 */} {nodes.map((node, i) => { const isActive = i === activeIdx; const size = isActive ? 40 : 30; return ( {/* 脉冲光环 */} {isActive && ( )} {/* 节点主体 */} {node.name} {/* 标签 */} {node.label} ); })} {/* 右侧信息面板 */} {/* 面板标题 */} 系统诊断 (System Analysis) {/* 动态内容 */} 当前行位 (Active Element) {ELEMENTS[activeIdx].name} / {ELEMENTS[activeIdx].label} 脏腑对应 (Organ) {ELEMENTS[activeIdx].organ} 特性 (Characteristics) {ELEMENTS[activeIdx].desc} {/* 能量条模拟 */} 气机流量 (Qi Level) {/* 底部生克关系说明 */} 实线:相生 (Generation Cycle) 虚线:相克 (Control Cycle)
); }