```javascript 'use client'; import React, { useState, useEffect, useMemo, useRef } from 'react'; // ---------------------------------------------------------------------- // 1. 配置与数据生成 (Configuration & Data) // ---------------------------------------------------------------------- const VIEW_WIDTH = 1400; const VIEW_HEIGHT = 800; const CELL_SIZE = 44; // 稍微增大一点 const GAP = 6; // 计算表格总宽度: 18列 * (44+6) - 6 = 894 // 居中计算: (1400 - 894) / 2 = 253 const START_X = 253; const START_Y = 140; // 元素分类颜色配置 const CATEGORIES = { ALKALI: { color: '#ef4444', label: '碱金属' }, ALKALINE: { color: '#f97316', label: '碱土金属' }, TRANSITION: { color: '#3b82f6', label: '过渡金属' }, BASIC_METAL: { color: '#06b6d4', label: '贫金属' }, METALLOID: { color: '#10b981', label: '类金属' }, NON_METAL: { color: '#a855f7', label: '非金属' }, HALOGEN: { color: '#d946ef', label: '卤素' }, NOBLE: { color: '#f43f5e', label: '稀有气体' }, LANTHANIDE: { color: '#eab308', label: '镧系元素' }, ACTINIDE: { color: '#84cc16', label: '锕系元素' }, UNKNOWN: { color: '#64748b', label: '未知区域' } }; // 辅助函数:根据原子序数获取位置 const getPeriodicPosition = (z) => { if (z === 1) return { x: 1, y: 1, c: 'NON_METAL' }; if (z === 2) return { x: 18, y: 1, c: 'NOBLE' }; if (z >= 3 && z <= 4) return { x: (z - 2), y: 2, c: z===3?'ALKALI':'ALKALINE' }; if (z >= 5 && z <= 10) return { x: (z + 8), y: 2, c: z===10?'NOBLE':(z===9?'HALOGEN':'NON_METAL') }; if (z >= 11 && z <= 12) return { x: (z - 10), y: 3, c: z===11?'ALKALI':'ALKALINE' }; if (z >= 13 && z <= 18) return { x: (z), y: 3, c: z===18?'NOBLE':(z===17?'HALOGEN':'BASIC_METAL') }; if (z >= 19 && z <= 36) return { x: (z - 18), y: 4, c: z<=20?(z===19?'ALKALI':'ALKALINE'):(z>=30?'BASIC_METAL':'TRANSITION') }; if (z >= 37 && z <= 54) return { x: (z - 36), y: 5, c: z<=38?(z===37?'ALKALI':'ALKALINE'):'TRANSITION' }; // 镧系 (57-71) if (z >= 57 && z <= 71) return { x: (z - 53), y: 9, c: 'LANTHANIDE' }; // 锕系 (89-103) if (z >= 89 && z <= 103) return { x: (z - 85), y: 10, c: 'ACTINIDE' }; // 第6周期主表 if (z === 55 || z === 56) return { x: (z - 54), y: 6, c: z===55?'ALKALI':'ALKALINE' }; if (z >= 72 && z <= 86) return { x: (z - 68), y: 6, c: 'TRANSITION' }; // 第7周期主表 if (z === 87 || z === 88) return { x: (z - 86), y: 7, c: z===87?'ALKALI':'ALKALINE' }; if (z >= 104 && z <= 118) return { x: (z - 100), y: 7, c: 'TRANSITION' }; return { x: 1, y: 1, c: 'UNKNOWN' }; }; // 元素名称映射 (部分示例) const ELEMENT_NAMES = { 1: {s:'H', n:'氢', m:'1.008'}, 2: {s:'He', n:'氦', m:'4.002'}, 3: {s:'Li', n:'锂', m:'6.94'}, 4: {s:'Be', n:'铍', m:'9.012'}, 5: {s:'B', n:'硼', m:'10.81'}, 6: {s:'C', n:'碳', m:'12.011'}, 7: {s:'N', n:'氮', m:'14.007'}, 8: {s:'O', n:'氧', m:'15.999'}, 9: {s:'F', n:'氟', m:'18.998'}, 10: {s:'Ne', n:'氖', m:'20.180'}, 11: {s:'Na', n:'钠', m:'22.990'}, 12: {s:'Mg', n:'镁', m:'24.305'}, 13: {s:'Al', n:'铝', m:'26.982'}, 14: {s:'Si', n:'硅', m:'28.085'}, 15: {s:'P', n:'磷', m:'30.974'}, 16: {s:'S', n:'硫', m:'32.06'}, 17: {s:'Cl', n:'氯', m:'35.45'}, 18: {s:'Ar', n:'氩', m:'39.95'}, 19: {s:'K', n:'钾', m:'39.098'}, 20: {s:'Ca', n:'钙', m:'40.078'}, 26: {s:'Fe', n:'铁', m:'55.845'}, 29: {s:'Cu', n:'铜', m:'63.546'}, 47: {s:'Ag', n:'银', m:'107.87'}, 79: {s:'Au', n:'金', m:'196.97'}, 80: {s:'Hg', n:'汞', m:'200.59'}, 92: {s:'U', n:'铀', m:'238.03'}, 32: {s:'Ge', n:'锗', m:'72.63'} }; // 获取通用符号 const getSymbol = (z) => { const symbols = "H He Li Be B C N O F Ne Na Mg Al Si P S Cl Ar K Ca Sc Ti V Cr Mn Fe Co Ni Cu Zn Ga Ge As Se Br Kr Rb Sr Y Zr Nb Mo Tc Ru Rh Pd Ag Cd In Sn Sb Te I Xe Cs Ba La Ce Pr Nd Pm Sm Eu Gd Tb Dy Ho Er Tm Yb Lu Hf Ta W Re Os Ir Pt Au Hg Tl Pb Bi Po At Rn Fr Ra Ac Th Pa U Np Pu Am Cm Bk Cf Es Fm Md No Lr Rf Db Sg Bh Hs Mt Ds Rg Cn Nh Fl Mc Lv Ts Og".split(" "); return symbols[z-1] || "Uue"; }; // 生成所有元素数据 const generateElements = () => { const elements = []; for (let z = 1; z <= 118; z++) { const pos = getPeriodicPosition(z); const info = ELEMENT_NAMES[z] || { s: 'El', n: z.toString(), m: (z*2.1).toFixed(2) }; // 修正分类逻辑 let cat = pos.c; if (z === 1) cat = 'NON_METAL'; if ([2,10,18,36,54,86,118].includes(z)) cat = 'NOBLE'; if ([9,17,35,53,85,117].includes(z)) cat = 'HALOGEN'; if ([5,14,32,33,51,52].includes(z)) cat = 'METALLOID'; if ([13,31,49,50,81,82,83,84].includes(z)) cat = 'BASIC_METAL'; if (z >= 104) cat = 'TRANSITION'; // 计算网格位置 const gx = START_X + (pos.x - 1) * (CELL_SIZE + GAP); const gy = START_Y + (pos.y - 1) * (CELL_SIZE + GAP) + (pos.y > 7 ? 40 : 0); elements.push({ z, s: info.s === 'El' ? getSymbol(z) : info.s, n: info.n, category: cat, mass: info.m, gx, gy, // 随机散落位置 sx: Math.random() * VIEW_WIDTH, sy: Math.random() * VIEW_HEIGHT * 2 - VIEW_HEIGHT, }); } return elements; }; // ---------------------------------------------------------------------- // 2. 组件实现 (Component Implementation) // ---------------------------------------------------------------------- export default function PeriodicTableTech() { const [elements] = useState(generateElements()); const [phase, setPhase] = useState('INIT'); const [activeZ, setActiveZ] = useState(null); const [currentTime, setCurrentTime] = useState(0); const [loadingText, setLoadingText] = useState('系统初始化...'); // 动画序列控制 useEffect(() => { let timer; const runSequence = async () => { // 1. 初始散落状态 setPhase('INIT'); setLoadingText('加载核心模块...'); await wait(1000); // 2. 聚合成表 setPhase('ASSEMBLE'); setLoadingText('构建元素矩阵...'); await wait(2500); // 3. 扫描特效 setPhase('SCAN'); setLoadingText('扫描物质结构...'); await wait(3000); // 4. 细节展示循环 setPhase('DETAIL'); setLoadingText('就绪 / 正在分析'); const targets = [1, 6, 14, 26, 47, 79, 92]; // H, C, Si, Fe, Ag, Au, U for (let z of targets) { setActiveZ(z); // 保持展示时间 await wait(3500); setActiveZ(null); await wait(400); } // 循环 runSequence(); }; runSequence(); return () => clearTimeout(timer); }, []); // 全局计时器 (用于旋转和装饰动画) useEffect(() => { let frame; const loop = (t) => { setCurrentTime(t / 1000); frame = requestAnimationFrame(loop); }; frame = requestAnimationFrame(loop); return () => cancelAnimationFrame(frame); }, []); const wait = (ms) => new Promise(resolve => setTimeout(resolve, ms)); // 动态样式计算 const getElementStyle = (el) => { const isInit = phase === 'INIT'; const isDetail = phase === 'DETAIL'; const isActive = activeZ === el.z; // 基础位置 const x = isInit ? el.sx : el.gx; const y = isInit ? el.sy : el.gy; // 透明度与缩放 let opacity = isInit ? 0 : 1; let scale = isInit ? 0 : 1; let zIndex = 1; // 细节模式下的背景暗化 if (isDetail && activeZ && !isActive) { opacity = 0.2; scale = 0.95; } // 激活元素高亮 if (isActive) { scale = 1.6; opacity = 1; zIndex = 10; } return { transform: `translate(${x}px, ${y}px) scale(${scale})`, opacity, zIndex, // SVG中zIndex无效,依赖DOM顺序,但这里用于逻辑标记 transition: 'transform 1.2s cubic-bezier(0.2, 0.8, 0.2, 1), opacity 0.8s ease', cursor: 'default' }; }; const activeElementData = useMemo(() => elements.find(e => e.z === activeZ), [activeZ, elements]); // 计算 Legend 居中位置 // 10个分类,每个宽约100px,总宽约1000px const LEGEND_WIDTH = 1050; const LEGEND_START_X = (VIEW_WIDTH - LEGEND_WIDTH) / 2; return (
{/* 1. 背景层 (Background Layers) */} {/* 装饰圆环 - 居中 */} {/* 2. UI 界面元素 (UI HUD) */} {/* 顶部标题栏 */} 元素周期表 PERIODIC TABLE {/* 状态指示器 */} 系统状态: {loadingText} {/* 3. 元素网格 (Elements Grid) */} {elements.map((el) => { const cat = CATEGORIES[el.category] || CATEGORIES.UNKNOWN; const isActive = activeZ === el.z; const isDimmed = phase === 'DETAIL' && activeZ && !isActive; return ( {/* 单元格背景 */} {/* 装饰角标 */} {!isDimmed && ( )} {/* 原子序数 */} {el.z} {/* 符号 */} {el.s} {/* 中文名 (仅在非散落、非模糊状态下显示) */} {(isActive || (phase !== 'INIT' && !isDimmed)) && ( {el.n} )} ); })} {/* 4. 扫描线特效 (Scan Effect) */} {phase === 'SCAN' && ( )} {/* 5. 全息详情卡片 (Holographic Detail Card) */} {phase === 'DETAIL' && activeElementData && ( {/* 连线到元素位置 */} {/* 卡片背景 */} {/* 内部装饰线 */} {/* 内容布局 */} {/* 旋转的原子结构示意 */} {/* 核心符号 */} {activeElementData.s} {/* 文本信息 */} DATA ANALYSIS 中文名称 / Name {activeElementData.n} 原子序数 / No. {activeElementData.z} 相对原子质量 / Mass {activeElementData.mass} 化学分类 / Category {CATEGORIES[activeElementData.category].label} )} {/* 6. 底部图例 (Legend) - 居中布局 */} {Object.values(CATEGORIES).map((cat, i) => ( {cat.label} ))} {/* 7. 装饰性角落元素 (Corner Stats) */} T-INDEX: {currentTime.toFixed(2)} SYS.VER.4.2
); } ```