<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>轮毂直驱系统 · IFR 最终理想解原理动画</title>
<link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700;900&family=JetBrains+Mono:wght@300;400;500;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<style>
*{margin:0;padding:0;box-sizing:border-box}
:root{
--bg:#050a14;--bg2:#0a1628;--cyan:#00d4ff;--cyan-dim:rgba(0,212,255,0.12);
--orange:#ff6b2b;--green:#00ff88;--yellow:#ffd600;--red:#ff3d5a;
--white:#dce4f0;--muted:#3e5278;--struct:#14203a;--carbon:#0e1830;
--border:#1a2a48;
}
body{background:var(--bg);color:var(--white);font-family:'JetBrains Mono',monospace;overflow:hidden;height:100vh;display:flex;flex-direction:column;user-select:none}
header{padding:12px 28px;display:flex;align-items:baseline;gap:18px;border-bottom:1px solid var(--border);background:linear-gradient(180deg,rgba(10,22,40,0.95),transparent);position:relative;z-index:2}
header h1{font-family:'Orbitron',sans-serif;font-size:18px;font-weight:700;letter-spacing:2px;color:var(--cyan);text-shadow:0 0 20px rgba(0,212,255,0.3)}
header p{font-size:11px;color:var(--muted);letter-spacing:1px}
#svg-wrap{flex:1;display:flex;align-items:center;justify-content:center;overflow:hidden;position:relative}
#main-svg{width:100%;height:100%;max-height:calc(100vh - 110px)}
#controls{padding:10px 24px;display:flex;align-items:center;gap:14px;border-top:1px solid var(--border);background:linear-gradient(0deg,rgba(10,22,40,0.98),rgba(10,22,40,0.85));flex-wrap:wrap;justify-content:center}
.phase-btn{padding:7px 16px;border:1px solid var(--border);border-radius:6px;background:var(--carbon);color:var(--muted);font-family:'JetBrains Mono',monospace;font-size:11px;cursor:pointer;transition:all .25s;letter-spacing:0.5px;display:flex;align-items:center;gap:6px}
.phase-btn:hover{border-color:var(--cyan);color:var(--white)}
.phase-btn.active{border-color:var(--cyan);color:var(--cyan);background:var(--cyan-dim);box-shadow:0 0 12px rgba(0,212,255,0.15)}
.phase-btn i{font-size:10px}
.sep{width:1px;height:24px;background:var(--border);margin:0 4px}
.slider-group{display:flex;align-items:center;gap:8px;font-size:10px;color:var(--muted)}
.slider-group label{white-space:nowrap}
.slider-group input[type=range]{-webkit-appearance:none;width:110px;height:4px;background:var(--border);border-radius:2px;outline:none}
.slider-group input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;width:14px;height:14px;background:var(--cyan);border-radius:50%;cursor:pointer;box-shadow:0 0 6px rgba(0,212,255,0.4)}
.slider-val{color:var(--cyan);min-width:36px;text-align:right;font-weight:700}
.param-group{display:flex;gap:16px;margin-left:auto}
.param-item{text-align:center}
.param-item .val{font-size:16px;font-weight:700;font-family:'Orbitron',sans-serif;line-height:1.2}
.param-item .lbl{font-size:9px;color:var(--muted);letter-spacing:0.5px}
.val-cyan{color:var(--cyan)}.val-orange{color:var(--orange)}.val-green{color:var(--green)}.val-yellow{color:var(--yellow)}
@keyframes pulse{0%,100%{opacity:.6}50%{opacity:1}}
@keyframes dashFlow{to{stroke-dashoffset:-40}}
</style>
</head>
<body>
<header>
<h1>HUB-DIRECT DRIVE</h1>
<p>IFR 最终理想解 · 零传动链原理动画</p>
</header>
<div id="svg-wrap">
<svg id="main-svg" viewBox="0 0 1400 850" preserveAspectRatio="xMidYMid meet">
<defs>
<!-- 碳纤维图案 -->
<pattern id="carbon" width="8" height="8" patternUnits="userSpaceOnUse" patternTransform="rotate(45)">
<rect width="8" height="8" fill="#0e1830"/>
<rect width="4" height="8" fill="#13203e"/>
</pattern>
<!-- 网格背景 -->
<pattern id="grid" width="40" height="40" patternUnits="userSpaceOnUse">
<path d="M40 0L0 0 0 40" fill="none" stroke="#0c1828" stroke-width="0.5"/>
</pattern>
<!-- 辉光滤镜 -->
<filter id="glow-cyan" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur in="SourceGraphic" stdDeviation="6" result="b"/>
<feFlood flood-color="#00d4ff" flood-opacity="0.5"/><feComposite in2="b" operator="in" result="g"/>
<feMerge><feMergeNode in="g"/><feMergeNode in="SourceGraphic"/></feMerge>
</filter>
<filter id="glow-orange" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur in="SourceGraphic" stdDeviation="5" result="b"/>
<feFlood flood-color="#ff6b2b" flood-opacity="0.45"/><feComposite in2="b" operator="in" result="g"/>
<feMerge><feMergeNode in="g"/><feMergeNode in="SourceGraphic"/></feMerge>
</filter>
<filter id="glow-green" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur in="SourceGraphic" stdDeviation="5" result="b"/>
<feFlood flood-color="#00ff88" flood-opacity="0.45"/><feComposite in2="b" operator="in" result="g"/>
<feMerge><feMergeNode in="g"/><feMergeNode in="SourceGraphic"/></feMerge>
</filter>
<filter id="softglow" x="-30%" y="-30%" width="160%" height="160%">
<feGaussianBlur in="SourceGraphic" stdDeviation="3" result="b"/>
<feMerge><feMergeNode in="b"/><feMergeNode in="SourceGraphic"/></feMerge>
</filter>
<!-- 箭头标记 -->
<marker id="arrow-cyan" viewBox="0 0 10 6" refX="10" refY="3" markerWidth="10" markerHeight="6" orient="auto"><path d="M0,0 L10,3 L0,6Z" fill="#00d4ff"/></marker>
<marker id="arrow-orange" viewBox="0 0 10 6" refX="10" refY="3" markerWidth="10" markerHeight="6" orient="auto"><path d="M0,0 L10,3 L0,6Z" fill="#ff6b2b"/></marker>
<marker id="arrow-green" viewBox="0 0 10 6" refX="10" refY="3" markerWidth="10" markerHeight="6" orient="auto"><path d="M0,0 L10,3 L0,6Z" fill="#00ff88"/></marker>
<marker id="arrow-white" viewBox="0 0 10 6" refX="10" refY="3" markerWidth="8" markerHeight="5" orient="auto"><path d="M0,0 L10,3 L0,6Z" fill="#dce4f0"/></marker>
<!-- 金属渐变 -->
<linearGradient id="metal" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#3a4a6a"/><stop offset="50%" stop-color="#5a6a8a"/><stop offset="100%" stop-color="#2a3a5a"/></linearGradient>
<radialGradient id="hub-grad" cx="50%" cy="45%"><stop offset="0%" stop-color="#2a4060"/><stop offset="70%" stop-color="#14203a"/><stop offset="100%" stop-color="#0a1428"/></radialGradient>
<radialGradient id="tire-grad" cx="50%" cy="45%"><stop offset="0%" stop-color="#1a1a2a"/><stop offset="80%" stop-color="#0a0a14"/><stop offset="100%" stop-color="#050508"/></radialGradient>
</defs>
<!-- 背景 -->
<rect width="1400" height="850" fill="url(#grid)"/>
<!-- 底部大气辉光 -->
<ellipse cx="500" cy="700" rx="500" ry="120" fill="rgba(0,212,255,0.02)"/>
<!-- ========== 轨道 ========== -->
<g id="track-group">
<line x1="0" y1="638" x2="920" y2="638" stroke="#1a2a48" stroke-width="3"/>
<line x1="0" y1="644" x2="920" y2="644" stroke="#1a2a48" stroke-width="1.5"/>
<g id="track-ties"></g>
</g>
<!-- ========== 速度线 ========== -->
<g id="speed-lines"></g>
<!-- ========== 车辆主体 ========== -->
<g id="vehicle-group">
<!-- 碳纤维底盘桁架 -->
<g id="chassis" stroke="#1e3050" stroke-width="3" fill="none">
<!-- 上弦杆 -->
<line x1="310" y1="465" x2="690" y2="465" stroke="#2a4060" stroke-width="4"/>
<!-- 下弦杆 -->
<line x1="290" y1="540" x2="710" y2="540" stroke="#2a4060" stroke-width="4"/>
<!-- 竖杆 -->
<line x1="350" y1="465" x2="340" y2="540"/>
<line x1="430" y1="465" x2="420" y2="540"/>
<line x1="510" y1="465" x2="510" y2="540"/>
<line x1="590" y1="465" x2="600" y2="540"/>
<line x1="670" y1="465" x2="680" y2="540"/>
<!-- 斜杆 -->
<line x1="340" y1="540" x2="430" y2="465" stroke="#1e3050" stroke-width="2"/>
<line x1="420" y1="540" x2="510" y2="465" stroke="#1e3050" stroke-width="2"/>
<line x1="510" y1="540" x2="590" y2="465" stroke="#1e3050" stroke-width="2"/>
<line x1="600" y1="540" x2="670" y2="465" stroke="#1e3050" stroke-width="2"/>
<line x1="350" y1="465" x2="420" y2="540" stroke="#1e3050" stroke-width="2" stroke-dasharray="4,4"/>
<line x1="430" y1="465" x2="510" y2="540" stroke="#1e3050" stroke-width="2" stroke-dasharray="4,4"/>
<line x1="510" y1="465" x2="600" y2="540" stroke="#1e3050" stroke-width="2" stroke-dasharray="4,4"/>
<line x1="590" y1="465" x2="680" y2="540" stroke="#1e3050" stroke-width="2" stroke-dasharray="4,4"/>
<!-- 节点 -->
<g fill="#2a4a70">
<circle cx="350" cy="465" r="4"/><circle cx="430" cy="465" r="4"/>
<circle cx="510" cy="465" r="4"/><circle cx="590" cy="465" r="4"/>
<circle cx="670" cy="465" r="4"/><circle cx="340" cy="540" r="4"/>
<circle cx="420" cy="540" r="4"/><circle cx="510" cy="540" r="4"/>
<circle cx="600" cy="540" r="4"/><circle cx="680" cy="540" r="4"/>
</g>
</g>
<!-- 碳纤维纹理覆盖 -->
<rect x="305" y="468" width="390" height="68" fill="url(#carbon)" opacity="0.4" rx="2"/>
<!-- 控制器盒 -->
<rect x="460" y="488" width="100" height="36" rx="4" fill="#0c1828" stroke="#2a4060" stroke-width="1.5"/>
<text x="510" y="510" text-anchor="middle" fill="#3e5278" font-size="8" font-family="JetBrains Mono">CTRL</text>
<rect id="ctrl-led" x="466" y="494" width="6" height="6" rx="3" fill="#1a2a40"/>
<!-- 电池 -->
<rect x="360" y="492" width="70" height="30" rx="3" fill="#0c1424" stroke="#1e3050" stroke-width="1"/>
<text x="395" y="511" text-anchor="middle" fill="#2a4060" font-size="7" font-family="JetBrains Mono">BAT 48V</text>
<rect id="bat-bar" x="366" y="496" width="4" height="20" rx="1" fill="#1a3a20"/>
<!-- 货叉 -->
<g id="fork-group">
<rect x="690" y="475" width="160" height="14" rx="2" fill="url(#carbon)" stroke="#1e3050" stroke-width="1.2"/>
<rect x="690" y="520" width="160" height="14" rx="2" fill="url(#carbon)" stroke="#1e3050" stroke-width="1.2"/>
<line x1="700" y1="489" x2="700" y2="520" stroke="#1e3050" stroke-width="2"/>
<line x1="730" y1="489" x2="730" y2="520" stroke="#1e3050" stroke-width="1.5"/>
<text x="770" y="510" text-anchor="middle" fill="#1e3050" font-size="7" font-family="JetBrains Mono" letter-spacing="1">CARBON FORK</text>
</g>
<!-- ===== 后轮:轮毂电机(主角)===== -->
<g id="hub-motor-group">
<!-- 轮胎 -->
<circle cx="390" cy="590" r="52" fill="url(#tire-grad)" stroke="#1a1a2a" stroke-width="2"/>
<!-- 轮辋 -->
<circle cx="390" cy="590" r="42" fill="none" stroke="#2a3a5a" stroke-width="2.5"/>
<!-- 转子外环(磁钢) -->
<circle cx="390" cy="590" r="36" fill="none" stroke="#1e3050" stroke-width="1"/>
<g id="rotor-magnets">
<!-- 12个磁钢交替N/S -->
</g>
<!-- 气隙 -->
<circle cx="390" cy="590" r="28" fill="none" stroke="#0a1428" stroke-width="0.5" stroke-dasharray="2,2"/>
<!-- 定子铁芯+线圈 -->
<circle cx="390" cy="590" r="24" fill="#0c1424" stroke="#1a2a44" stroke-width="1"/>
<g id="stator-coils">
<!-- 9个线圈 -->
</g>
<!-- 轮毂轴 -->
<circle cx="390" cy="590" r="8" fill="#14203a" stroke="#2a4060" stroke-width="1.5"/>
<circle cx="390" cy="590" r="3" fill="#2a4060"/>
<!-- 辐条 -->
<g stroke="#1e3050" stroke-width="1.5" id="spokes-rear">
<line x1="390" y1="566" x2="390" y2="548"/>
<line x1="390" y1="614" x2="390" y2="632"/>
<line x1="366" y1="590" x2="348" y2="590"/>
<line x1="414" y1="590" x2="432" y2="590"/>
<line x1="373" y1="573" x2="360" y2="560"/>
<line x1="407" y1="607" x2="420" y2="620"/>
<line x1="373" y1="607" x2="360" y2="620"/>
<line x1="407" y1="573" x2="420" y2="560"/>
</g>
<!-- 电磁场力线(动画) -->
<g id="em-field" opacity="0"></g>
<!-- 电机标签 -->
<text x="390" y="530" text-anchor="middle" fill="#2a4060" font-size="7" font-family="JetBrains Mono" letter-spacing="0.5">HUB MOTOR</text>
<text x="390" y="660" text-anchor="middle" fill="#1e3050" font-size="7" font-family="JetBrains Mono">≤4kg × 2</text>
</g>
<!-- ===== 前轮:从动轮 ===== -->
<g id="passive-wheel-group">
<circle cx="670" cy="595" r="42" fill="url(#tire-grad)" stroke="#1a1a2a" stroke-width="1.5"/>
<circle cx="670" cy="595" r="34" fill="none" stroke="#1a2a40" stroke-width="1.5"/>
<circle cx="670" cy="595" r="6" fill="#0e1830" stroke="#1e3050" stroke-width="1"/>
<g stroke="#141e30" stroke-width="1" id="spokes-front">
<line x1="670" y1="563" x2="670" y2="553"/>
<line x1="670" y1="627" x2="670" y2="637"/>
<line x1="638" y1="595" x2="628" y2="595"/>
<line x1="702" y1="595" x2="712" y2="595"/>
</g>
<text x="670" y="655" text-anchor="middle" fill="#141e30" font-size="7" font-family="JetBrains Mono">PASSIVE</text>
</g>
<!-- "零传动链"标注区 -->
<g id="no-shaft-label" opacity="0.6">
<line x1="430" y1="590" x2="630" y2="590" stroke="#1e3050" stroke-width="1" stroke-dasharray="6,4"/>
<text x="530" y="585" text-anchor="middle" fill="#2a4060" font-size="8" font-family="Orbitron" letter-spacing="1">ZERO DRIVE SHAFT</text>
<!-- 禁止符号 -->
<circle cx="530" cy="598" r="8" fill="none" stroke="#2a4060" stroke-width="1"/>
<line x1="524" y1="604" x2="536" y2="592" stroke="#2a4060" stroke-width="1"/>
</g>
</g>
<!-- ========== 电力流向线 ========== -->
<g id="power-flow-group">
<!-- 电池→控制器 -->
<path id="power-path-bc" d="M430,507 L460,507" fill="none" stroke="#2a4060" stroke-width="2" stroke-dasharray="4,4" opacity="0.5"/>
<!-- 控制器→轮毂电机(上方路径) -->
<path id="power-path-cm1" d="M460,495 Q440,460 390,460 Q350,460 350,530 Q350,565 365,575" fill="none" stroke="#2a4060" stroke-width="2" stroke-dasharray="4,6" opacity="0.5"/>
<!-- 控制器→轮毂电机(下方路径) -->
<path id="power-path-cm2" d="M460,520 Q420,555 400,570" fill="none" stroke="#2a4060" stroke-width="1.5" stroke-dasharray="3,5" opacity="0.3"/>
</g>
<!-- ========== 接触力箭头 ========== -->
<g id="force-arrows">
<g id="drive-force" opacity="0">
<line x1="335" y1="640" x2="280" y2="640" stroke="#00d4ff" stroke-width="3" marker-end="url(#arrow-cyan)"/>
<text x="305" y="656" text-anchor="middle" fill="#00d4ff" font-size="9" font-family="Orbitron">F</text>
</g>
<g id="regen-force" opacity="0">
<line x1="280" y1="640" x2="335" y2="640" stroke="#00ff88" stroke-width="3" marker-end="url(#arrow-green)"/>
<text x="305" y="656" text-anchor="middle" fill="#00ff88" font-size="9" font-family="Orbitron">REGEN</text>
</g>
</g>
<!-- ========== 阶段指示 ========== -->
<g id="phase-indicator" transform="translate(50,40)">
<rect x="0" y="0" width="200" height="44" rx="6" fill="rgba(10,22,40,0.85)" stroke="#1a2a48" stroke-width="1"/>
<text id="phase-text" x="100" y="18" text-anchor="middle" fill="#3e5278" font-size="8" font-family="JetBrains Mono" letter-spacing="1">PHASE</text>
<text id="phase-name" x="100" y="35" text-anchor="middle" fill="#00d4ff" font-size="14" font-family="Orbitron" font-weight="700">STANDBY</text>
</g>
<!-- ========== 右侧面板:俯视图 ========== -->
<g transform="translate(940,20)">
<rect x="0" y="0" width="440" height="260" rx="8" fill="rgba(8,16,32,0.9)" stroke="#1a2a48" stroke-width="1"/>
<text x="220" y="22" text-anchor="middle" fill="#3e5278" font-size="9" font-family="Orbitron" letter-spacing="2">TOP VIEW · ELECTRONIC DIFFERENTIAL</text>
<!-- 车体轮廓 -->
<rect x="130" y="50" width="180" height="180" rx="6" fill="url(#carbon)" stroke="#1e3050" stroke-width="1.5" opacity="0.6"/>
<!-- 货叉 -->
<rect x="310" y="80" width="100" height="14" rx="2" fill="#0e1830" stroke="#1e3050" stroke-width="1"/>
<rect x="310" y="186" width="100" height="14" rx="2" fill="#0e1830" stroke="#1e3050" stroke-width="1"/>
<!-- 控制器 -->
<rect x="190" y="120" width="60" height="40" rx="3" fill="#0c1828" stroke="#2a4060" stroke-width="1"/>
<text x="220" y="144" text-anchor="middle" fill="#2a4060" font-size="7" font-family="JetBrains Mono">ECU</text>
<!-- 左轮毂电机 -->
<g id="top-left-motor">
<circle cx="130" cy="140" r="22" fill="#0a1428" stroke="#1a2a44" stroke-width="1.5"/>
<circle cx="130" cy="140" r="14" fill="#0e1830" stroke="#1e3050" stroke-width="1"/>
<circle cx="130" cy="140" r="5" fill="#14203a"/>
<text x="100" y="175" text-anchor="middle" fill="#1e3050" font-size="7" font-family="JetBrains Mono">L-HUB</text>
</g>
<!-- 右轮毂电机 -->
<g id="top-right-motor">
<circle cx="310" cy="140" r="22" fill="#0a1428" stroke="#1a2a44" stroke-width="1.5"/>
<circle cx="310" cy="140" r="14" fill="#0e1830" stroke="#1e3050" stroke-width="1"/>
<circle cx="310" cy="140" r="5" fill="#14203a"/>
<text x="340" y="175" text-anchor="middle" fill="#1e3050" font-size="7" font-family="JetBrains Mono">R-HUB</text>
</g>
<!-- 从动轮 -->
<circle cx="170" cy="58" r="12" fill="#0a0e18" stroke="#141e30" stroke-width="1"/>
<circle cx="270" cy="58" r="12" fill="#0a0e18" stroke="#141e30" stroke-width="1"/>
<circle cx="170" cy="222" r="12" fill="#0a0e18" stroke="#141e30" stroke-width="1"/>
<circle cx="270" cy="222" r="12" fill="#0a0e18" stroke="#141e30" stroke-width="1"/>
<!-- 差速控制线 -->
<path id="diff-line-l" d="M190,140 L155,140" fill="none" stroke="#2a4060" stroke-width="1.5" stroke-dasharray="3,3"/>
<path id="diff-line-r" d="M250,140 L285,140" fill="none" stroke="#2a4060" stroke-width="1.5" stroke-dasharray="3,3"/>
<!-- 同步指示 -->
<g id="sync-indicator" opacity="0">
<text x="220" y="200" text-anchor="middle" fill="#00d4ff" font-size="8" font-family="Orbitron" letter-spacing="1">SYNC</text>
<rect x="185" y="205" width="70" height="3" rx="1.5" fill="#0a1428"/>
<rect id="sync-bar" x="185" y="205" width="70" height="3" rx="1.5" fill="#00d4ff" opacity="0.6"/>
</g>
<!-- 方向箭头 -->
<g id="direction-arrow" opacity="0">
<line x1="220" y1="30" x2="220" y2="10" stroke="#00d4ff" stroke-width="2" marker-end="url(#arrow-cyan)"/>
</g>
</g>
<!-- ========== 右侧面板:轮毂电机剖面 ========== -->
<g transform="translate(940,300)">
<rect x="0" y="0" width="440" height="280" rx="8" fill="rgba(8,16,32,0.9)" stroke="#1a2a48" stroke-width="1"/>
<text x="220" y="22" text-anchor="middle" fill="#3e5278" font-size="9" font-family="Orbitron" letter-spacing="2">HUB MOTOR CROSS-SECTION</text>
<!-- 电机剖面图 -->
<g transform="translate(160,155)">
<!-- 轮胎 -->
<circle r="100" fill="url(#tire-grad)" stroke="#1a1a2a" stroke-width="2"/>
<!-- 轮辋 -->
<circle r="85" fill="none" stroke="#2a3a5a" stroke-width="2"/>
<!-- 转子环 -->
<circle r="75" fill="none" stroke="#1e3050" stroke-width="6" id="rotor-ring"/>
<!-- 磁钢(剖面中为矩形块) -->
<g id="detail-magnets"></g>
<!-- 气隙 -->
<circle r="60" fill="none" stroke="rgba(0,212,255,0.1)" stroke-width="2" stroke-dasharray="2,3"/>
<!-- 定子铁芯 -->
<circle r="52" fill="#0c1424" stroke="#1a2a44" stroke-width="1.5"/>
<!-- 定子齿+线圈 -->
<g id="detail-stator"></g>
<!-- 轴 -->
<circle r="14" fill="#14203a" stroke="#2a4060" stroke-width="2"/>
<circle r="5" fill="#1e3050"/>
<!-- 电磁力线(动画层) -->
<g id="detail-em-field" opacity="0"></g>
<!-- 力矩标注 -->
<g id="torque-arc" opacity="0">
<path d="M-40,-90 A98,98 0 0,1 40,-90" fill="none" stroke="#00d4ff" stroke-width="2.5" marker-end="url(#arrow-cyan)"/>
<text x="0" y="-96" text-anchor="middle" fill="#00d4ff" font-size="10" font-family="Orbitron" font-weight="700">TORQUE</text>
</g>
<!-- 再生制动力矩 -->
<g id="regen-arc" opacity="0">
<path d="M40,-90 A98,98 0 0,1 -40,-90" fill="none" stroke="#00ff88" stroke-width="2.5" marker-end="url(#arrow-green)"/>
<text x="0" y="-96" text-anchor="middle" fill="#00ff88" font-size="10" font-family="Orbitron" font-weight="700">REGEN</text>
</g>
</g>
<!-- 右侧说明 -->
<g transform="translate(310,60)" fill="#3e5278" font-size="8" font-family="JetBrains Mono">
<text y="0" fill="#2a4060">STRUCTURE</text>
<circle cx="-8" cy="16" r="3" fill="#1a1a2a"/><text x="2" y="20">Tire</text>
<circle cx="-8" cy="32" r="3" fill="#2a3a5a"/><text x="2" y="36">Rim</text>
<circle cx="-8" cy="48" r="3" fill="#ff3d5a" opacity="0.6"/><text x="2" y="52">Rotor Mag</text>
<circle cx="-8" cy="64" r="3" fill="#00d4ff" opacity="0.6"/><text x="2" y="68">Stator Coil</text>
<circle cx="-8" cy="80" r="3" fill="#14203a"/><text x="2" y="84">Axle</text>
</g>
</g>
<!-- ========== 右侧面板:IFR 原则 ========== -->
<g transform="translate(940,600)">
<rect x="0" y="0" width="440" height="230" rx="8" fill="rgba(8,16,32,0.9)" stroke="#1a2a48" stroke-width="1"/>
<text x="220" y="22" text-anchor="middle" fill="#3e5278" font-size="9" font-family="Orbitron" letter-spacing="2">IFR · IDEAL FINAL RESULT</text>
<!-- IFR核心原则 -->
<g transform="translate(24,40)" font-family="JetBrains Mono" font-size="10">
<text fill="#00d4ff" font-size="11" font-weight="700" font-family="Orbitron">Wheel = Motor</text>
<text y="20" fill="#5a7aaa">轮即电机,动力直达轮缘</text>
<text y="40" fill="#5a7aaa">零传动链 · 零机械间隙 · 零中间损耗</text>
<line x1="0" y1="52" x2="392" y2="52" stroke="#1a2a48" stroke-width="0.5"/>
<!-- 重量再分配 -->
<text y="70" fill="#2a4060" font-size="8" letter-spacing="1">WEIGHT REALLOCATION</text>
<g transform="translate(0,82)">
<rect width="120" height="24" rx="4" fill="rgba(255,107,43,0.1)" stroke="#ff6b2b" stroke-width="0.8"/>
<text x="60" y="16" text-anchor="middle" fill="#ff6b2b" font-size="9">传动系统 15kg</text>
<text x="145" y="16" fill="#3e5278" font-size="14">→</text>
<rect x="170" width="120" height="24" rx="4" fill="rgba(0,255,136,0.08)" stroke="#00ff88" stroke-width="0.8"/>
<text x="230" y="16" text-anchor="middle" fill="#00ff88" font-size="9">结构强度 +15kg</text>
</g>
<line x1="0" y1="116" x2="392" y2="116" stroke="#1a2a48" stroke-width="0.5"/>
<!-- 边界条件 -->
<text y="134" fill="#2a4060" font-size="8" letter-spacing="1">BOUNDARY / RISK</text>
<text y="152" fill="#5a3a3a" font-size="9">⚠ 长时间极限制动 → 磁钢退磁风险</text>
<text y="168" fill="#5a3a3a" font-size="9">⚠ 5m/s 轨道接缝冲击 → 轴承疲劳</text>
<text y="184" fill="#5a3a3a" font-size="9">⚠ 粉尘/湿滑轨道 → 打滑失控</text>
</g>
</g>
<!-- ========== 底部参数条 ========== -->
<g id="svg-params" transform="translate(50,790)" font-family="Orbitron" font-size="10">
<g transform="translate(0,0)">
<text fill="#3e5278" font-size="8" letter-spacing="1">SPEED</text>
<text id="svg-speed" y="18" fill="#00d4ff" font-size="16" font-weight="700">0.0</text>
<text x="52" y="18" fill="#3e5278" font-size="8">m/s</text>
</g>
<g transform="translate(120,0)">
<text fill="#3e5278" font-size="8" letter-spacing="1">ACCEL</text>
<text id="svg-accel" y="18" fill="#ff6b2b" font-size="16" font-weight="700">0.0</text>
<text x="52" y="18" fill="#3e5278" font-size="8">m/s²</text>
</g>
<g transform="translate(240,0)">
<text fill="#3e5278" font-size="8" letter-spacing="1">TORQUE</text>
<text id="svg-torque" y="18" fill="#00d4ff" font-size="16" font-weight="700">0</text>
<text x="38" y="18" fill="#3e5278" font-size="8">Nm</text>
</g>
<g transform="translate(350,0)">
<text fill="#3e5278" font-size="8" letter-spacing="1">REGEN</text>
<text id="svg-regen" y="18" fill="#00ff88" font-size="16" font-weight="700">0</text>
<text x="44" y="18" fill="#3e5278" font-size="8">W</text>
</g>
<g transform="translate(460,0)">
<text fill="#3e5278" font-size="8" letter-spacing="1">MOTOR TEMP</text>
<text id="svg-temp" y="18" fill="#ffd600" font-size="16" font-weight="700">25</text>
<text x="42" y="18" fill="#3e5278" font-size="8">°C</text>
</g>
</g>
</svg>
</div>
<!-- 控制面板 -->
<div id="controls">
<button class="phase-btn active" data-phase="standby"><i class="fas fa-power-off"></i>待机</button>
<button class="phase-btn" data-phase="accelerate"><i class="fas fa-bolt"></i>起步加速</button>
<button class="phase-btn" data-phase="cruise"><i class="fas fa-gauge-high"></i>稳速行驶</button>
<button class="phase-btn" data-phase="brake"><i class="fas fa-rotate-left"></i>再生制动</button>
<button class="phase-btn" data-phase="stop"><i class="fas fa-stop"></i>抱刹停止</button>
<div class="sep"></div>
<div class="slider-group">
<label>目标速度</label>
<input type="range" id="speed-slider" min="1" max="7" step="0.5" value="5"/>
<span class="slider-val" id="speed-val">5.0 m/s</span>
</div>
<div class="slider-group">
<label>轨道摩擦</label>
<input type="range" id="friction-slider" min="0.3" max="1" step="0.1" value="0.8"/>
<span class="slider-val" id="friction-val">0.8</span>
</div>
</div>
<script>
const SVG_NS = 'http://www.w3.org/2000/svg';
const svg = document.getElementById('main-svg');
// ===== 状态管理 =====
const state = {
phase: 'standby',
speed: 0,
targetSpeed: 5,
friction: 0.8,
acceleration: 0,
torque: 0,
regenPower: 0,
motorTemp: 25,
trackOffset: 0,
wheelAngle: 0,
time: 0,
phaseTime: 0,
powerFlowOffset: 0,
emFieldAngle: 0,
};
// ===== 初始化 SVG 动态元素 =====
function initDynamicElements() {
// 轨道枕木
const tiesGroup = document.getElementById('track-ties');
for (let i = 0; i < 40; i++) {
const rect = document.createElementNS(SVG_NS, 'rect');
rect.setAttribute('x', i * 50);
rect.setAttribute('y', '632');
rect.setAttribute('width', '20');
rect.setAttribute('height', '16');
rect.setAttribute('rx', '1');
rect.setAttribute('fill', '#0e1830');
rect.setAttribute('stroke', '#1a2a44');
rect.setAttribute('stroke-width', '0.5');
rect.setAttribute('class', 'track-tie');
tiesGroup.appendChild(rect);
}
// 轮毂电机磁钢(主视图)
const rotorG = document.getElementById('rotor-magnets');
for (let i = 0; i < 12; i++) {
const angle = (i * 30) * Math.PI / 180;
const r = 36;
const x = 390 + Math.cos(angle) * r;
const y = 590 + Math.sin(angle) * r;
const rect = document.createElementNS(SVG_NS, 'rect');
rect.setAttribute('x', x - 4);
rect.setAttribute('y', y - 3);
rect.setAttribute('width', '8');
rect.setAttribute('height', '6');
rect.setAttribute('rx', '1');
rect.setAttribute('fill', i % 2 === 0 ? '#ff3d5a' : '#3d5aff');
rect.setAttribute('opacity', '0.5');
rect.setAttribute('transform', `rotate(${i * 30}, ${x}, ${y})`);
rotorG.appendChild(rect);
}
// 轮毂电机定子线圈(主视图)
const statorG = document.getElementById('stator-coils');
for (let i = 0; i < 9; i++) {
const angle = (i * 40) * Math.PI / 180;
const r = 22;
const x = 390 + Math.cos(angle) * r;
const y = 590 + Math.sin(angle) * r;
const circle = document.createElementNS(SVG_NS, 'circle');
circle.setAttribute('cx', x);
circle.setAttribute('cy', y);
circle.setAttribute('r', '4');
circle.setAttribute('fill', '#0a1428');
circle.setAttribute('stroke', '#00d4ff');
circle.setAttribute('stroke-width', '0.8');
circle.setAttribute('opacity', '0.4');
circle.setAttribute('class', 'stator-coil');
statorG.appendChild(circle);
}
// 剖面图磁钢
const detailMag = document.getElementById('detail-magnets');
for (let i = 0; i < 20; i++) {
const angle = (i * 18) * Math.PI / 180;
const r = 75;
const x = Math.cos(angle) * r;
const y = Math.sin(angle) * r;
const rect = document.createElementNS(SVG_NS, 'rect');
rect.setAttribute('x', x - 5);
rect.setAttribute('y', y - 3.5);
rect.setAttribute('width', '10');
rect.setAttribute('height', '7');
rect.setAttribute('rx', '1');
rect.setAttribute('fill', i % 2 === 0 ? '#ff3d5a' : '#3d5aff');
rect.setAttribute('opacity', '0.45');
rect.setAttribute('transform', `rotate(${i * 18}, ${x}, ${y})`);
detailMag.appendChild(rect);
}
// 剖面图定子齿+线圈
const detailStator = document.getElementById('detail-stator');
for (let i = 0; i < 12; i++) {
const angle = (i * 30) * Math.PI / 180;
const g = document.createElementNS(SVG_NS, 'g');
g.setAttribute('transform', `rotate(${i * 30})`);
// 齿
const tooth = document.createElementNS(SVG_NS, 'rect');
tooth.setAttribute('x', '-3');
tooth.setAttribute('y', '-52');
tooth.setAttribute('width', '6');
tooth.setAttribute('height', '14');
tooth.setAttribute('rx', '1');
tooth.setAttribute('fill', '#1a2a44');
g.appendChild(tooth);
// 线圈
const coil = document.createElementNS(SVG_NS, 'rect');
coil.setAttribute('x', '-6');
coil.setAttribute('y', '-48');
coil.setAttribute('width', '12');
coil.setAttribute('height', '8');
coil.setAttribute('rx', '2');
coil.setAttribute('fill', 'none');
coil.setAttribute('stroke', '#00d4ff');
coil.setAttribute('stroke-width', '0.8');
coil.setAttribute('opacity', '0.3');
coil.setAttribute('class', 'detail-coil');
g.appendChild(coil);
detailStator.appendChild(g);
}
// 速度线
const speedG = document.getElementById('speed-lines');
for (let i = 0; i < 20; i++) {
const line = document.createElementNS(SVG_NS, 'line');
line.setAttribute('x1', '0');
line.setAttribute('y1', String(350 + Math.random() * 280));
line.setAttribute('x2', String(30 + Math.random() * 80));
line.setAttribute('y2', String(350 + Math.random() * 280));
line.setAttribute('stroke', '#00d4ff');
line.setAttribute('stroke-width', '1');
line.setAttribute('opacity', '0');
line.setAttribute('class', 'speed-line');
speedG.appendChild(line);
}
}
// ===== 阶段切换 =====
function setPhase(newPhase) {
state.phase = newPhase;
state.phaseTime = 0;
// 更新按钮状态
document.querySelectorAll('.phase-btn').forEach(btn => {
btn.classList.toggle('active', btn.dataset.phase === newPhase);
});
// 更新阶段指示
const names = {standby:'STANDBY', accelerate:'ACCELERATE', cruise:'CRUISE', brake:'REGEN BRAKE', stop:'FULL STOP'};
const colors = {standby:'#3e5278', accelerate:'#00d4ff', cruise:'#ffd600', brake:'#00ff88', stop:'#ff3d5a'};
document.getElementById('phase-name').textContent = names[newPhase];
document.getElementById('phase-name').setAttribute('fill', colors[newPhase]);
}
// ===== 状态更新 =====
function updateState(dt) {
state.time += dt;
state.phaseTime += dt;
switch (state.phase) {
case 'standby':
state.speed = Math.max(0, state.speed - 3 * dt);
state.torque = 0;
state.acceleration = -3;
state.regenPower = 0;
break;
case 'accelerate': {
const maxA = 2.5 * state.friction;
state.acceleration = maxA;
state.speed = Math.min(state.targetSpeed, state.speed + maxA * dt);
state.torque = 100 * (1 - state.speed / state.targetSpeed * 0.4) * state.friction;
state.regenPower = 0;
if (state.speed >= state.targetSpeed * 0.98) setPhase('cruise');
break;
}
case 'cruise':
state.speed = state.targetSpeed;
state.acceleration = 0;
state.torque = 15;
state.regenPower = 0;
break;
case 'brake': {
const brakeA = -4 * state.friction;
state.acceleration = brakeA;
state.speed = Math.max(0, state.speed + brakeA * dt);
state.torque = -80 * state.friction;
state.regenPower = Math.abs(state.speed * state.torque * 0.08);
state.motorTemp = Math.min(135, state.motorTemp + 20 * dt);
if (state.speed <= 0.05) setPhase('stop');
break;
}
case 'stop':
state.speed = 0;
state.acceleration = 0;
state.torque = 0;
state.regenPower = 0;
break;
}
// 电机冷却
if (state.phase !== 'brake') {
state.motorTemp = Math.max(25, state.motorTemp - 3 * dt);
}
// 轨道偏移
state.trackOffset = (state.trackOffset + state.speed * 25 * dt) % 50;
// 轮子旋转
state.wheelAngle += state.speed * 3 * dt;
// 电力流动偏移
state.powerFlowOffset = (state.powerFlowOffset + state.speed * 80 * dt) % 40;
// 电磁场旋转
state.emFieldAngle += (state.speed * 2 + (state.phase === 'accelerate' ? 3 : 0)) * dt;
}
// ===== 渲染 =====
function render() {
const isAccel = state.phase === 'accelerate';
const isCruise = state.phase === 'cruise';
const isBrake = state.phase === 'brake';
const isStop = state.phase === 'stop';
const isStandby = state.phase === 'standby';
const isActive = isAccel || isCruise;
const speedRatio = state.speed / 7;
// 轨道枕木滚动
const ties = document.querySelectorAll('.track-tie');
ties.forEach((tie, i) => {
const x = (i * 50 - state.trackOffset + 920) % (50 * 40) - 50;
tie.setAttribute('x', String(x));
});
// 后轮旋转
const rearSpokes = document.getElementById('spokes-rear');
rearSpokes.setAttribute('transform', `rotate(${state.wheelAngle * 180 / Math.PI}, 390, 590)`);
const rotorMag = document.getElementById('rotor-magnets');
rotorMag.setAttribute('transform', `rotate(${state.wheelAngle * 180 / Math.PI * 0.95}, 390, 590)`);
// 前轮旋转
const frontSpokes = document.getElementById('spokes-front');
frontSpokes.setAttribute('transform', `rotate(${state.wheelAngle * 180 / Math.PI}, 670, 595)`);
// 定子线圈高亮
const coils = document.querySelectorAll('.stator-coil');
coils.forEach((c, i) => {
const phase = (state.emFieldAngle * 3 + i * 0.7) % (Math.PI * 2);
const bright = isActive ? 0.3 + 0.7 * Math.max(0, Math.sin(phase)) : 0.15;
c.setAttribute('opacity', String(bright));
c.setAttribute('stroke', isBrake ? '#00ff88' : '#00d4ff');
});
// 剖面图线圈高亮
const detailCoils = document.querySelectorAll('.detail-coil');
detailCoils.forEach((c, i) => {
const phase = (state.emFieldAngle * 3 + i * 0.5) % (Math.PI * 2);
const bright = isActive || isBrake ? 0.2 + 0.8 * Math.max(0, Math.sin(phase)) : 0.1;
c.setAttribute('opacity', String(bright));
c.setAttribute('stroke', isBrake ? '#00ff88' : '#00d4ff');
});
// 电力路径流动
['power-path-cm1', 'power-path-cm2', 'power-path-bc'].forEach(id => {
const el = document.getElementById(id);
if (isActive) {
el.setAttribute('stroke', '#00d4ff');
el.setAttribute('stroke-dashoffset', String(-state.powerFlowOffset));
el.setAttribute('opacity', String(0.4 + speedRatio * 0.5));
} else if (isBrake) {
el.setAttribute('stroke', '#00ff88');
el.setAttribute('stroke-dashoffset', String(state.powerFlowOffset));
el.setAttribute('opacity', String(0.3 + speedRatio * 0.4));
} else {
el.setAttribute('stroke', '#2a4060');
el.setAttribute('opacity', '0.25');
}
});
// 驱动力/再生力箭头
const driveForce = document.getElementById('drive-force');
const regenForce = document.getElementById('regen-force');
driveForce.setAttribute('opacity', String(isActive ? Math.min(1, Math.abs(state.torque) / 60) : 0));
regenForce.setAttribute('opacity', String(isBrake ? Math.min(1, Math.abs(state.torque) / 60) : 0));
// 力矩弧线
document.getElementById('torque-arc').setAttribute('opacity', String(isActive ? 0.8 : 0));
document.getElementById('regen-arc').setAttribute('opacity', String(isBrake ? 0.8 : 0));
// 控制器LED
const led = document.getElementById('ctrl-led');
if (isActive) {
led.setAttribute('fill', '#00d4ff');
led.setAttribute('filter', 'url(#softglow)');
} else if (isBrake) {
led.setAttribute('fill', '#00ff88');
led.setAttribute('filter', 'url(#softglow)');
} else if (isStop) {
led.setAttribute('fill', '#ff3d5a');
led.removeAttribute('filter');
} else {
led.setAttribute('fill', '#1a2a40');
led.removeAttribute('filter');
}
// 电池条
const batBar = document.getElementById('bat-bar');
const batLevel = isBrake ? Math.min(1, 0.5 + state.regenPower / 80) : Math.max(0.2, 0.8 - speedRatio * 0.3);
batBar.setAttribute('height', String(20 * batLevel));
batBar.setAttribute('fill', isBrake ? '#00ff88' : '#1a4a2a');
// 俯视图差速线
const diffL = document.getElementById('diff-line-l');
const diffR = document.getElementById('diff-line-r');
if (isActive || isBrake) {
const color = isBrake ? '#00ff88' : '#00d4ff';
diffL.setAttribute('stroke', color);
diffR.setAttribute('stroke', color);
diffL.setAttribute('stroke-dashoffset', String(-state.powerFlowOffset));
diffR.setAttribute('stroke-dashoffset', String(-state.powerFlowOffset));
diffL.setAttribute('opacity', '0.8');
diffR.setAttribute('opacity', '0.8');
} else {
diffL.setAttribute('stroke', '#2a4060');
diffR.setAttribute('stroke', '#2a4060');
diffL.setAttribute('opacity', '0.4');
diffR.setAttribute('opacity', '0.4');
}
// 同步指示
const syncInd = document.getElementById('sync-indicator');
syncInd.setAttribute('opacity', String(isActive || isCruise ? 0.9 : 0));
// 方向箭头
const dirArrow = document.getElementById('direction-arrow');
dirArrow.setAttribute('opacity', String(state.speed > 0.1 ? 0.8 : 0));
// 速度线
const speedLines = document.querySelectorAll('.speed-line');
speedLines.forEach((line, i) => {
if (state.speed > 1) {
const alpha = Math.min(0.5, (state.speed - 1) / 6) * (0.3 + Math.random() * 0.7);
line.setAttribute('opacity', String(alpha));
line.setAttribute('stroke', isBrake ? '#00ff88' : '#00d4ff');
const y = 360 + ((i * 47 + state.time * 200) % 260);
const len = 30 + state.speed * 15 + Math.random() * 40;
const x = -len - Math.random() * 200;
line.setAttribute('y1', String(y));
line.setAttribute('y2', String(y));
line.setAttribute('x1', String(x));
line.setAttribute('x2', String(x + len));
} else {
line.setAttribute('opacity', '0');
}
});
// 轮毂电机发光
const hubGroup = document.getElementById('hub-motor-group');
if (isActive) {
hubGroup.setAttribute('filter', 'url(#glow-cyan)');
} else if (isBrake) {
hubGroup.setAttribute('filter', 'url(#glow-green)');
} else {
hubGroup.removeAttribute('filter');
}
// 零传动链标注
const noShaft = document.getElementById('no-shaft-label');
noShaft.setAttribute('opacity', String(0.35 + speedRatio * 0.35));
// 俯视图电机高亮
['top-left-motor', 'top-right-motor'].forEach(id => {
const g = document.getElementById(id);
const circle = g.querySelector('circle:nth-child(2)');
if (isActive) {
circle.setAttribute('stroke', '#00d4ff');
circle.setAttribute('stroke-width', '2');
} else if (isBrake) {
circle.setAttribute('stroke', '#00ff88');
circle.setAttribute('stroke-width', '2');
} else {
circle.setAttribute('stroke', '#1e3050');
circle.setAttribute('stroke-width', '1');
}
});
// 参数更新
document.getElementById('svg-speed').textContent = state.speed.toFixed(1);
document.getElementById('svg-accel').textContent = Math.abs(state.acceleration).toFixed(1);
document.getElementById('svg-torque').textContent = Math.abs(Math.round(state.torque));
document.getElementById('svg-regen').textContent = Math.round(state.regenPower);
document.getElementById('svg-temp').textContent = Math.round(state.motorTemp);
// 温度颜色
const tempEl = document.getElementById('svg-temp');
if (state.motorTemp > 100) tempEl.setAttribute('fill', '#ff3d5a');
else if (state.motorTemp > 70) tempEl.setAttribute('fill', '#ff6b2b');
else if (state.motorTemp > 45) tempEl.setAttribute('fill', '#ffd600');
else tempEl.setAttribute('fill', '#ffd600');
// 加速度颜色
const accelEl = document.getElementById('svg-accel');
accelEl.setAttribute('fill', state.acceleration >= 0 ? '#00d4ff' : '#ff6b2b');
// 力矩颜色
const torqueEl = document.getElementById('svg-torque');
torqueEl.setAttribute('fill', state.torque >= 0 ? '#00d4ff' : '#00ff88');
}
// ===== 动画循环 =====
let lastTime = 0;
function animate(timestamp) {
const dt = Math.min(0.05, (timestamp - lastTime) / 1000);
lastTime = timestamp;
updateState(dt);
render();
requestAnimationFrame(animate);
}
// ===== 事件绑定 =====
document.querySelectorAll('.phase-btn').forEach(btn => {
btn.addEventListener('click', () => setPhase(btn.dataset.phase));
});
document.getElementById('speed-slider').addEventListener('input', e => {
state.targetSpeed = parseFloat(e.target.value);
document.getElementById('speed-val').textContent = state.targetSpeed.toFixed(1) + ' m/s';
});
document.getElementById('friction-slider').addEventListener('input', e => {
state.friction = parseFloat(e.target.value);
document.getElementById('friction-val').textContent = state.friction.toFixed(1);
});
// ===== 启动 =====
initDynamicElements();
setPhase('standby');
requestAnimationFrame(animate);
</script>
</body>
</html>
实现说明
本动画以 TRIZ 的"最终理想解 (IFR)"思想为核心设计线索,聚焦展示轮毂直驱系统"轮即电机、动力直达轮缘"的本质机理:
IFR 聚焦:主视图直接呈现消除传动链后的理想构型——轮毂电机内部定子线圈与转子磁钢直接耦合,电磁力不经任何中间环节即转化为轮缘驱动力。两轮之间标注"ZERO DRIVE SHAFT"并附禁止符号,视觉强化"零传动链"概念。
资源利用可视化:右下角 IFR 面板以箭头动画展示"传动系统重量 → 结构强度"的再分配逻辑——将省下的 15kg 传动系统重量让渡给碳纤维承载结构,极致压榨推重比。
动态视觉引导:
- 加速阶段:青色电力流动线从控制器直达轮毂电机,定子线圈按三相时序明灭,力矩弧线标注"TORQUE"
- 制动阶段:流动线反转为绿色,标注"REGEN",能量回馈电池
- 俯视差速图:双轮毂电机同步脉冲可视化,"SYNC"指示器确认电子差速协同
交互性:底部控制面板可手动切换五个运行阶段(待机/起步加速/稳速/再生制动/抱刹),滑块可调目标速度(1-7 m/s)和轨道摩擦系数,摩擦降低时加速度与制动力相应衰减,模拟打滑边界。
剖面细节:右侧轮毂电机剖面图展示轮胎→轮辋→转子磁钢(红蓝交替)→气隙→定子齿+线圈→中心轴的完整结构,线圈按电磁相位实时明灭,直观呈现"电机即车轮"的 IFR 本质。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>轮毂直驱系统 · IFR 最终理想解原理动画</title>
<link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700;900&family=JetBrains+Mono:wght@300;400;500;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<style>
*{margin:0;padding:0;box-sizing:border-box}
:root{
--bg:#050a14;--bg2:#0a1628;--cyan:#00d4ff;--cyan-dim:rgba(0,212,255,0.12);
--orange:#ff6b2b;--green:#00ff88;--yellow:#ffd600;--red:#ff3d5a;
--white:#dce4f0;--muted:#3e5278;--struct:#14203a;--carbon:#0e1830;
--border:#1a2a48;
}
body{background:var(--bg);color:var(--white);font-family:'JetBrains Mono',monospace;overflow:hidden;height:100vh;display:flex;flex-direction:column;user-select:none}
header{padding:12px 28px;display:flex;align-items:baseline;gap:18px;border-bottom:1px solid var(--border);background:linear-gradient(180deg,rgba(10,22,40,0.95),transparent);position:relative;z-index:2}
header h1{font-family:'Orbitron',sans-serif;font-size:18px;font-weight:700;letter-spacing:2px;color:var(--cyan);text-shadow:0 0 20px rgba(0,212,255,0.3)}
header p{font-size:11px;color:var(--muted);letter-spacing:1px}
#svg-wrap{flex:1;display:flex;align-items:center;justify-content:center;overflow:hidden;position:relative}
#main-svg{width:100%;height:100%;max-height:calc(100vh - 120px)}
#controls{padding:10px 24px;display:flex;align-items:center;gap:14px;border-top:1px solid var(--border);background:linear-gradient(0deg,rgba(10,22,40,0.98),rgba(10,22,40,0.85));flex-wrap:wrap;justify-content:center}
.ctrl-section{display:flex;align-items:center;gap:10px}
.ctrl-label{font-size:9px;color:#2a4060;font-family:'Orbitron',sans-serif;letter-spacing:1px;text-transform:uppercase;writing-mode:vertical-lr;text-orientation:mixed}
.phase-btn{padding:7px 16px;border:1px solid var(--border);border-radius:6px;background:var(--carbon);color:var(--muted);font-family:'JetBrains Mono',monospace;font-size:11px;cursor:pointer;transition:all .25s;letter-spacing:0.5px;display:flex;align-items:center;gap:6px}
.phase-btn:hover{border-color:var(--cyan);color:var(--white)}
.phase-btn.active{border-color:var(--cyan);color:var(--cyan);background:var(--cyan-dim);box-shadow:0 0 12px rgba(0,212,255,0.15)}
.phase-btn i{font-size:10px}
.sep{width:1px;height:28px;background:var(--border);margin:0 6px;flex-shrink:0}
.slider-group{display:flex;align-items:center;gap:8px;font-size:10px;color:var(--muted)}
.slider-group label{white-space:nowrap}
.slider-group input[type=range]{-webkit-appearance:none;width:100px;height:4px;background:var(--border);border-radius:2px;outline:none}
.slider-group input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;width:14px;height:14px;background:var(--cyan);border-radius:50%;cursor:pointer;box-shadow:0 0 6px rgba(0,212,255,0.4)}
.slider-val{color:var(--cyan);min-width:36px;text-align:right;font-weight:700;font-size:11px}
.fork-btn{padding:7px 18px;border:1px solid var(--border);border-radius:6px;background:var(--carbon);color:var(--muted);font-family:'JetBrains Mono',monospace;font-size:11px;cursor:pointer;transition:all .25s;display:flex;align-items:center;gap:6px}
.fork-btn:hover{border-color:var(--orange);color:var(--white)}
.fork-btn.extending{border-color:var(--orange);color:var(--orange);background:rgba(255,107,43,0.1);box-shadow:0 0 12px rgba(255,107,43,0.15)}
.fork-btn.retracting{border-color:var(--green);color:var(--green);background:rgba(0,255,136,0.08)}
@keyframes pulse{0%,100%{opacity:.6}50%{opacity:1}}
</style>
</head>
<body>
<header>
<h1>HUB-DIRECT DRIVE</h1>
<p>IFR 最终理想解 · 零传动链原理动画</p>
</header>
<div id="svg-wrap">
<svg id="main-svg" viewBox="0 0 1400 850" preserveAspectRatio="xMidYMid meet">
<defs>
<pattern id="carbon" width="8" height="8" patternUnits="userSpaceOnUse" patternTransform="rotate(45)">
<rect width="8" height="8" fill="#0e1830"/>
<rect width="4" height="8" fill="#13203e"/>
</pattern>
<pattern id="grid" width="40" height="40" patternUnits="userSpaceOnUse">
<path d="M40 0L0 0 0 40" fill="none" stroke="#0c1828" stroke-width="0.5"/>
</pattern>
<filter id="glow-cyan" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur in="SourceGraphic" stdDeviation="6" result="b"/>
<feFlood flood-color="#00d4ff" flood-opacity="0.5"/><feComposite in2="b" operator="in" result="g"/>
<feMerge><feMergeNode in="g"/><feMergeNode in="SourceGraphic"/></feMerge>
</filter>
<filter id="glow-orange" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur in="SourceGraphic" stdDeviation="5" result="b"/>
<feFlood flood-color="#ff6b2b" flood-opacity="0.45"/><feComposite in2="b" operator="in" result="g"/>
<feMerge><feMergeNode in="g"/><feMergeNode in="SourceGraphic"/></feMerge>
</filter>
<filter id="glow-green" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur in="SourceGraphic" stdDeviation="5" result="b"/>
<feFlood flood-color="#00ff88" flood-opacity="0.45"/><feComposite in2="b" operator="in" result="g"/>
<feMerge><feMergeNode in="g"/><feMergeNode in="SourceGraphic"/></feMerge>
</filter>
<filter id="softglow" x="-30%" y="-30%" width="160%" height="160%">
<feGaussianBlur in="SourceGraphic" stdDeviation="3" result="b"/>
<feMerge><feMergeNode in="b"/><feMergeNode in="SourceGraphic"/></feMerge>
</filter>
<marker id="arrow-cyan" viewBox="0 0 10 6" refX="10" refY="3" markerWidth="10" markerHeight="6" orient="auto"><path d="M0,0 L10,3 L0,6Z" fill="#00d4ff"/></marker>
<marker id="arrow-orange" viewBox="0 0 10 6" refX="10" refY="3" markerWidth="10" markerHeight="6" orient="auto"><path d="M0,0 L10,3 L0,6Z" fill="#ff6b2b"/></marker>
<marker id="arrow-green" viewBox="0 0 10 6" refX="10" refY="3" markerWidth="10" markerHeight="6" orient="auto"><path d="M0,0 L10,3 L0,6Z" fill="#00ff88"/></marker>
<linearGradient id="metal" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#3a4a6a"/><stop offset="50%" stop-color="#5a6a8a"/><stop offset="100%" stop-color="#2a3a5a"/></linearGradient>
<radialGradient id="hub-grad" cx="50%" cy="45%"><stop offset="0%" stop-color="#2a4060"/><stop offset="70%" stop-color="#14203a"/><stop offset="100%" stop-color="#0a1428"/></radialGradient>
<radialGradient id="tire-grad" cx="50%" cy="45%"><stop offset="0%" stop-color="#1a1a2a"/><stop offset="80%" stop-color="#0a0a14"/><stop offset="100%" stop-color="#050508"/></radialGradient>
<!-- 货叉导轨渐变 -->
<linearGradient id="rail-grad" x1="0" y1="0" x2="1" y2="0">
<stop offset="0%" stop-color="#1e3050"/><stop offset="50%" stop-color="#2a4568"/><stop offset="100%" stop-color="#1e3050"/>
</linearGradient>
<linearGradient id="rail-glow-grad" x1="0" y1="0" x2="1" y2="0">
<stop offset="0%" stop-color="#ff6b2b" stop-opacity="0"/><stop offset="20%" stop-color="#ff6b2b" stop-opacity="0.6"/><stop offset="80%" stop-color="#ff6b2b" stop-opacity="0.6"/><stop offset="100%" stop-color="#ff6b2b" stop-opacity="0"/>
</linearGradient>
</defs>
<!-- 背景 -->
<rect width="1400" height="850" fill="url(#grid)"/>
<ellipse cx="500" cy="700" rx="500" ry="120" fill="rgba(0,212,255,0.02)"/>
<!-- ===== 轨道 ===== -->
<g id="track-group">
<line x1="0" y1="638" x2="920" y2="638" stroke="#1a2a48" stroke-width="3"/>
<line x1="0" y1="644" x2="920" y2="644" stroke="#1a2a48" stroke-width="1.5"/>
<g id="track-ties"></g>
</g>
<!-- ===== 速度线 ===== -->
<g id="speed-lines"></g>
<!-- ===== 车辆主体 ===== -->
<g id="vehicle-group">
<!-- 碳纤维底盘桁架 -->
<g id="chassis" stroke="#1e3050" stroke-width="3" fill="none">
<line x1="310" y1="465" x2="690" y2="465" stroke="#2a4060" stroke-width="4"/>
<line x1="290" y1="540" x2="710" y2="540" stroke="#2a4060" stroke-width="4"/>
<line x1="350" y1="465" x2="340" y2="540"/>
<line x1="430" y1="465" x2="420" y2="540"/>
<line x1="510" y1="465" x2="510" y2="540"/>
<line x1="590" y1="465" x2="600" y2="540"/>
<line x1="670" y1="465" x2="680" y2="540"/>
<line x1="340" y1="540" x2="430" y2="465" stroke="#1e3050" stroke-width="2"/>
<line x1="420" y1="540" x2="510" y2="465" stroke="#1e3050" stroke-width="2"/>
<line x1="510" y1="540" x2="590" y2="465" stroke="#1e3050" stroke-width="2"/>
<line x1="600" y1="540" x2="670" y2="465" stroke="#1e3050" stroke-width="2"/>
<line x1="350" y1="465" x2="420" y2="540" stroke="#1e3050" stroke-width="2" stroke-dasharray="4,4"/>
<line x1="430" y1="465" x2="510" y2="540" stroke="#1e3050" stroke-width="2" stroke-dasharray="4,4"/>
<line x1="510" y1="465" x2="600" y2="540" stroke="#1e3050" stroke-width="2" stroke-dasharray="4,4"/>
<line x1="590" y1="465" x2="680" y2="540" stroke="#1e3050" stroke-width="2" stroke-dasharray="4,4"/>
<g fill="#2a4a70">
<circle cx="350" cy="465" r="4"/><circle cx="430" cy="465" r="4"/>
<circle cx="510" cy="465" r="4"/><circle cx="590" cy="465" r="4"/>
<circle cx="670" cy="465" r="4"/><circle cx="340" cy="540" r="4"/>
<circle cx="420" cy="540" r="4"/><circle cx="510" cy="540" r="4"/>
<circle cx="600" cy="540" r="4"/><circle cx="680" cy="540" r="4"/>
</g>
</g>
<rect x="305" y="468" width="390" height="68" fill="url(#carbon)" opacity="0.4" rx="2"/>
<!-- 控制器盒 -->
<rect x="460" y="488" width="100" height="36" rx="4" fill="#0c1828" stroke="#2a4060" stroke-width="1.5"/>
<text x="510" y="510" text-anchor="middle" fill="#3e5278" font-size="8" font-family="JetBrains Mono">CTRL</text>
<rect id="ctrl-led" x="466" y="494" width="6" height="6" rx="3" fill="#1a2a40"/>
<!-- 电池 -->
<rect x="360" y="492" width="70" height="30" rx="3" fill="#0c1424" stroke="#1e3050" stroke-width="1"/>
<text x="395" y="511" text-anchor="middle" fill="#2a4060" font-size="7" font-family="JetBrains Mono">BAT 48V</text>
<rect id="bat-bar" x="366" y="496" width="4" height="20" rx="1" fill="#1a3a20"/>
<!-- ===== 货叉系统(可伸缩)===== -->
<g id="fork-system">
<!-- 导轨(固定在车架上) -->
<g id="fork-rails">
<!-- 上导轨 -->
<rect x="690" y="473" width="200" height="4" rx="1" fill="url(#rail-grad)" opacity="0.6"/>
<rect x="690" y="487" width="200" height="4" rx="1" fill="url(#rail-grad)" opacity="0.6"/>
<!-- 下导轨 -->
<rect x="690" y="518" width="200" height="4" rx="1" fill="url(#rail-grad)" opacity="0.6"/>
<rect x="690" y="532" width="200" height="4" rx="1" fill="url(#rail-grad)" opacity="0.6"/>
<!-- 导轨端盖 -->
<rect x="886" y="470" width="6" height="24" rx="2" fill="#1a2a44"/>
<rect x="886" y="515" width="6" height="24" rx="2" fill="#1a2a44"/>
</g>
<!-- 导轨发光层(伸缩时激活) -->
<g id="rail-glow" opacity="0">
<rect x="690" y="473" width="200" height="18" rx="1" fill="url(#rail-glow-grad)" opacity="0.15"/>
<rect x="690" y="518" width="200" height="18" rx="1" fill="url(#rail-glow-grad)" opacity="0.15"/>
</g>
<!-- 伸缩货叉 - 上层 -->
<g id="fork-upper">
<rect x="690" y="477" width="200" height="10" rx="1.5" fill="url(#carbon)" stroke="#1e3050" stroke-width="1.2"/>
<!-- 碳纤维纹理线 -->
<line x1="700" y1="479" x2="880" y2="479" stroke="#1a2a44" stroke-width="0.5"/>
<line x1="700" y1="485" x2="880" y2="485" stroke="#1a2a44" stroke-width="0.5"/>
<!-- 叉尖 -->
<path d="M890,477 L910,480 L910,484 L890,487Z" fill="#14203a" stroke="#1e3050" stroke-width="0.8"/>
<!-- 限位标记 -->
<g id="fork-marks-upper" fill="#1e3050" font-size="6" font-family="JetBrains Mono">
<text x="720" y="485">0</text>
<text x="770" y="485">50</text>
<text x="820" y="485">100</text>
<text x="860" y="485">150</text>
</g>
</g>
<!-- 伸缩货叉 - 下层 -->
<g id="fork-lower">
<rect x="690" y="522" width="200" height="10" rx="1.5" fill="url(#carbon)" stroke="#1e3050" stroke-width="1.2"/>
<line x1="700" y1="524" x2="880" y2="524" stroke="#1a2a44" stroke-width="0.5"/>
<line x1="700" y1="530" x2="880" y2="530" stroke="#1a2a44" stroke-width="0.5"/>
<path d="M890,522 L910,525 L910,529 L890,532Z" fill="#14203a" stroke="#1e3050" stroke-width="0.8"/>
<g id="fork-marks-lower" fill="#1e3050" font-size="6" font-family="JetBrains Mono">
<text x="720" y="530">0</text>
<text x="770" y="530">50</text>
<text x="820" y="530">100</text>
<text x="860" y="530">150</text>
</g>
</g>
<!-- 竖向连接件(随货叉移动) -->
<g id="fork-verticals">
<line x1="700" y1="487" x2="700" y2="522" stroke="#1e3050" stroke-width="2.5"/>
<line x1="735" y1="487" x2="735" y2="522" stroke="#1e3050" stroke-width="1.5"/>
</g>
<!-- 伸缩位移标注 -->
<g id="fork-dimension" opacity="0">
<!-- 标注线 -->
<line x1="690" y1="460" x2="690" y2="455" stroke="#ff6b2b" stroke-width="0.8"/>
<line id="fork-dim-end" x1="690" y1="460" x2="690" y2="455" stroke="#ff6b2b" stroke-width="0.8"/>
<line x1="690" y1="457" x2="690" y2="457" stroke="#ff6b2b" stroke-width="0.8" marker-end="url(#arrow-orange)" marker-start="url(#arrow-orange)"/>
<text id="fork-dim-text" x="690" y="452" text-anchor="middle" fill="#ff6b2b" font-size="9" font-family="Orbitron" font-weight="700">0mm</text>
</g>
<!-- 驱动指示(微型电动推杆) -->
<g id="fork-actuator" opacity="0.5">
<rect x="692" y="498" width="50" height="6" rx="2" fill="#0e1830" stroke="#1e3050" stroke-width="0.8"/>
<rect id="fork-actuator-rod" x="740" y="499.5" width="30" height="3" rx="1" fill="#2a4060"/>
</g>
<!-- 货叉标签 -->
<text x="790" y="510" text-anchor="middle" fill="#1e3050" font-size="7" font-family="JetBrains Mono" letter-spacing="1" id="fork-label-text">CARBON FORK</text>
</g>
<!-- ===== 后轮:轮毂电机 ===== -->
<g id="hub-motor-group">
<circle cx="390" cy="590" r="52" fill="url(#tire-grad)" stroke="#1a1a2a" stroke-width="2"/>
<circle cx="390" cy="590" r="42" fill="none" stroke="#2a3a5a" stroke-width="2.5"/>
<circle cx="390" cy="590" r="36" fill="none" stroke="#1e3050" stroke-width="1"/>
<g id="rotor-magnets"></g>
<circle cx="390" cy="590" r="28" fill="none" stroke="#0a1428" stroke-width="0.5" stroke-dasharray="2,2"/>
<circle cx="390" cy="590" r="24" fill="#0c1424" stroke="#1a2a44" stroke-width="1"/>
<g id="stator-coils"></g>
<circle cx="390" cy="590" r="8" fill="#14203a" stroke="#2a4060" stroke-width="1.5"/>
<circle cx="390" cy="590" r="3" fill="#2a4060"/>
<g stroke="#1e3050" stroke-width="1.5" id="spokes-rear">
<line x1="390" y1="566" x2="390" y2="548"/>
<line x1="390" y1="614" x2="390" y2="632"/>
<line x1="366" y1="590" x2="348" y2="590"/>
<line x1="414" y1="590" x2="432" y2="590"/>
<line x1="373" y1="573" x2="360" y2="560"/>
<line x1="407" y1="607" x2="420" y2="620"/>
<line x1="373" y1="607" x2="360" y2="620"/>
<line x1="407" y1="573" x2="420" y2="560"/>
</g>
<text x="390" y="530" text-anchor="middle" fill="#2a4060" font-size="7" font-family="JetBrains Mono" letter-spacing="0.5">HUB MOTOR</text>
<text x="390" y="660" text-anchor="middle" fill="#1e3050" font-size="7" font-family="JetBrains Mono">≤4kg × 2</text>
</g>
<!-- ===== 前轮:从动轮 ===== -->
<g id="passive-wheel-group">
<circle cx="670" cy="595" r="42" fill="url(#tire-grad)" stroke="#1a1a2a" stroke-width="1.5"/>
<circle cx="670" cy="595" r="34" fill="none" stroke="#1a2a40" stroke-width="1.5"/>
<circle cx="670" cy="595" r="6" fill="#0e1830" stroke="#1e3050" stroke-width="1"/>
<g stroke="#141e30" stroke-width="1" id="spokes-front">
<line x1="670" y1="563" x2="670" y2="553"/>
<line x1="670" y1="627" x2="670" y2="637"/>
<line x1="638" y1="595" x2="628" y2="595"/>
<line x1="702" y1="595" x2="712" y2="595"/>
</g>
<text x="670" y="655" text-anchor="middle" fill="#141e30" font-size="7" font-family="JetBrains Mono">PASSIVE</text>
</g>
<!-- "零传动链"标注 -->
<g id="no-shaft-label" opacity="0.6">
<line x1="430" y1="590" x2="630" y2="590" stroke="#1e3050" stroke-width="1" stroke-dasharray="6,4"/>
<text x="530" y="585" text-anchor="middle" fill="#2a4060" font-size="8" font-family="Orbitron" letter-spacing="1">ZERO DRIVE SHAFT</text>
<circle cx="530" cy="598" r="8" fill="none" stroke="#2a4060" stroke-width="1"/>
<line x1="524" y1="604" x2="536" y2="592" stroke="#2a4060" stroke-width="1"/>
</g>
</g>
<!-- ===== 电力流向线 ===== -->
<g id="power-flow-group">
<path id="power-path-bc" d="M430,507 L460,507" fill="none" stroke="#2a4060" stroke-width="2" stroke-dasharray="4,4" opacity="0.5"/>
<path id="power-path-cm1" d="M460,495 Q440,460 390,460 Q350,460 350,530 Q350,565 365,575" fill="none" stroke="#2a4060" stroke-width="2" stroke-dasharray="4,6" opacity="0.5"/>
<path id="power-path-cm2" d="M460,520 Q420,555 400,570" fill="none" stroke="#2a4060" stroke-width="1.5" stroke-dasharray="3,5" opacity="0.3"/>
</g>
<!-- ===== 接触力箭头 ===== -->
<g id="force-arrows">
<g id="drive-force" opacity="0">
<line x1="335" y1="640" x2="280" y2="640" stroke="#00d4ff" stroke-width="3" marker-end="url(#arrow-cyan)"/>
<text x="305" y="656" text-anchor="middle" fill="#00d4ff" font-size="9" font-family="Orbitron">F</text>
</g>
<g id="regen-force" opacity="0">
<line x1="280" y1="640" x2="335" y2="640" stroke="#00ff88" stroke-width="3" marker-end="url(#arrow-green)"/>
<text x="305" y="656" text-anchor="middle" fill="#00ff88" font-size="9" font-family="Orbitron">REGEN</text>
</g>
</g>
<!-- ===== 阶段指示 ===== -->
<g id="phase-indicator" transform="translate(50,40)">
<rect x="0" y="0" width="200" height="44" rx="6" fill="rgba(10,22,40,0.85)" stroke="#1a2a48" stroke-width="1"/>
<text id="phase-text" x="100" y="18" text-anchor="middle" fill="#3e5278" font-size="8" font-family="JetBrains Mono" letter-spacing="1">PHASE</text>
<text id="phase-name" x="100" y="35" text-anchor="middle" fill="#00d4ff" font-size="14" font-family="Orbitron" font-weight="700">STANDBY</text>
</g>
<!-- ===== 货叉伸缩状态指示 ===== -->
<g id="fork-status" transform="translate(50,92)">
<rect x="0" y="0" width="200" height="38" rx="6" fill="rgba(10,22,40,0.85)" stroke="#1a2a48" stroke-width="1"/>
<text x="100" y="16" text-anchor="middle" fill="#3e5278" font-size="8" font-family="JetBrains Mono" letter-spacing="1">FORK EXTENSION</text>
<text id="fork-status-val" x="100" y="32" text-anchor="middle" fill="#ff6b2b" font-size="13" font-family="Orbitron" font-weight="700">0 mm</text>
</g>
<!-- ===== 右侧面板:俯视图 ===== -->
<g transform="translate(940,20)">
<rect x="0" y="0" width="440" height="260" rx="8" fill="rgba(8,16,32,0.9)" stroke="#1a2a48" stroke-width="1"/>
<text x="220" y="22" text-anchor="middle" fill="#3e5278" font-size="9" font-family="Orbitron" letter-spacing="2">TOP VIEW · ELECTRONIC DIFFERENTIAL</text>
<!-- 车体轮廓 -->
<rect x="130" y="50" width="180" height="180" rx="6" fill="url(#carbon)" stroke="#1e3050" stroke-width="1.5" opacity="0.6"/>
<!-- 俯视货叉(可伸缩) -->
<g id="fork-top-group">
<rect id="fork-top-upper" x="310" y="80" width="100" height="14" rx="2" fill="#0e1830" stroke="#1e3050" stroke-width="1"/>
<rect id="fork-top-lower" x="310" y="186" width="100" height="14" rx="2" fill="#0e1830" stroke="#1e3050" stroke-width="1"/>
<!-- 俯视叉尖 -->
<path id="fork-top-tip-u" d="M410,80 L425,83 L425,91 L410,94Z" fill="#14203a" stroke="#1e3050" stroke-width="0.6"/>
<path id="fork-top-tip-l" d="M410,186 L425,189 L425,197 L410,200Z" fill="#14203a" stroke="#1e3050" stroke-width="0.6"/>
</g>
<!-- 控制器 -->
<rect x="190" y="120" width="60" height="40" rx="3" fill="#0c1828" stroke="#2a4060" stroke-width="1"/>
<text x="220" y="144" text-anchor="middle" fill="#2a4060" font-size="7" font-family="JetBrains Mono">ECU</text>
<!-- 左轮毂电机 -->
<g id="top-left-motor">
<circle cx="130" cy="140" r="22" fill="#0a1428" stroke="#1a2a44" stroke-width="1.5"/>
<circle cx="130" cy="140" r="14" fill="#0e1830" stroke="#1e3050" stroke-width="1"/>
<circle cx="130" cy="140" r="5" fill="#14203a"/>
<text x="100" y="175" text-anchor="middle" fill="#1e3050" font-size="7" font-family="JetBrains Mono">L-HUB</text>
</g>
<!-- 右轮毂电机 -->
<g id="top-right-motor">
<circle cx="310" cy="140" r="22" fill="#0a1428" stroke="#1a2a44" stroke-width="1.5"/>
<circle cx="310" cy="140" r="14" fill="#0e1830" stroke="#1e3050" stroke-width="1"/>
<circle cx="310" cy="140" r="5" fill="#14203a"/>
<text x="340" y="175" text-anchor="middle" fill="#1e3050" font-size="7" font-family="JetBrains Mono">R-HUB</text>
</g>
<!-- 从动轮 -->
<circle cx="170" cy="58" r="12" fill="#0a0e18" stroke="#141e30" stroke-width="1"/>
<circle cx="270" cy="58" r="12" fill="#0a0e18" stroke="#141e30" stroke-width="1"/>
<circle cx="170" cy="222" r="12" fill="#0a0e18" stroke="#141e30" stroke-width="1"/>
<circle cx="270" cy="222" r="12" fill="#0a0e18" stroke="#141e30" stroke-width="1"/>
<!-- 差速控制线 -->
<path id="diff-line-l" d="M190,140 L155,140" fill="none" stroke="#2a4060" stroke-width="1.5" stroke-dasharray="3,3"/>
<path id="diff-line-r" d="M250,140 L285,140" fill="none" stroke="#2a4060" stroke-width="1.5" stroke-dasharray="3,3"/>
<!-- 同步指示 -->
<g id="sync-indicator" opacity="0">
<text x="220" y="200" text-anchor="middle" fill="#00d4ff" font-size="8" font-family="Orbitron" letter-spacing="1">SYNC</text>
<rect x="185" y="205" width="70" height="3" rx="1.5" fill="#0a1428"/>
<rect id="sync-bar" x="185" y="205" width="70" height="3" rx="1.5" fill="#00d4ff" opacity="0.6"/>
</g>
<!-- 方向箭头 -->
<g id="direction-arrow" opacity="0">
<line x1="220" y1="30" x2="220" y2="10" stroke="#00d4ff" stroke-width="2" marker-end="url(#arrow-cyan)"/>
</g>
</g>
<!-- ===== 右侧面板:轮毂电机剖面 ===== -->
<g transform="translate(940,300)">
<rect x="0" y="0" width="440" height="280" rx="8" fill="rgba(8,16,32,0.9)" stroke="#1a2a48" stroke-width="1"/>
<text x="220" y="22" text-anchor="middle" fill="#3e5278" font-size="9" font-family="Orbitron" letter-spacing="2">HUB MOTOR CROSS-SECTION</text>
<g transform="translate(160,155)">
<circle r="100" fill="url(#tire-grad)" stroke="#1a1a2a" stroke-width="2"/>
<circle r="85" fill="none" stroke="#2a3a5a" stroke-width="2"/>
<circle r="75" fill="none" stroke="#1e3050" stroke-width="6" id="rotor-ring"/>
<g id="detail-magnets"></g>
<circle r="60" fill="none" stroke="rgba(0,212,255,0.1)" stroke-width="2" stroke-dasharray="2,3"/>
<circle r="52" fill="#0c1424" stroke="#1a2a44" stroke-width="1.5"/>
<g id="detail-stator"></g>
<circle r="14" fill="#14203a" stroke="#2a4060" stroke-width="2"/>
<circle r="5" fill="#1e3050"/>
<g id="torque-arc" opacity="0">
<path d="M-40,-90 A98,98 0 0,1 40,-90" fill="none" stroke="#00d4ff" stroke-width="2.5" marker-end="url(#arrow-cyan)"/>
<text x="0" y="-96" text-anchor="middle" fill="#00d4ff" font-size="10" font-family="Orbitron" font-weight="700">TORQUE</text>
</g>
<g id="regen-arc" opacity="0">
<path d="M40,-90 A98,98 0 0,1 -40,-90" fill="none" stroke="#00ff88" stroke-width="2.5" marker-end="url(#arrow-green)"/>
<text x="0" y="-96" text-anchor="middle" fill="#00ff88" font-size="10" font-family="Orbitron" font-weight="700">REGEN</text>
</g>
</g>
<g transform="translate(310,60)" fill="#3e5278" font-size="8" font-family="JetBrains Mono">
<text y="0" fill="#2a4060">STRUCTURE</text>
<circle cx="-8" cy="16" r="3" fill="#1a1a2a"/><text x="2" y="20">Tire</text>
<circle cx="-8" cy="32" r="3" fill="#2a3a5a"/><text x="2" y="36">Rim</text>
<circle cx="-8" cy="48" r="3" fill="#ff3d5a" opacity="0.6"/><text x="2" y="52">Rotor Mag</text>
<circle cx="-8" cy="64" r="3" fill="#00d4ff" opacity="0.6"/><text x="2" y="68">Stator Coil</text>
<circle cx="-8" cy="80" r="3" fill="#14203a"/><text x="2" y="84">Axle</text>
</g>
</g>
<!-- ===== 右侧面板:IFR 原则 ===== -->
<g transform="translate(940,600)">
<rect x="0" y="0" width="440" height="230" rx="8" fill="rgba(8,16,32,0.9)" stroke="#1a2a48" stroke-width="1"/>
<text x="220" y="22" text-anchor="middle" fill="#3e5278" font-size="9" font-family="Orbitron" letter-spacing="2">IFR · IDEAL FINAL RESULT</text>
<g transform="translate(24,40)" font-family="JetBrains Mono" font-size="10">
<text fill="#00d4ff" font-size="11" font-weight="700" font-family="Orbitron">Wheel = Motor</text>
<text y="20" fill="#5a7aaa">轮即电机,动力直达轮缘</text>
<text y="40" fill="#5a7aaa">零传动链 · 零机械间隙 · 零中间损耗</text>
<line x1="0" y1="52" x2="392" y2="52" stroke="#1a2a48" stroke-width="0.5"/>
<text y="70" fill="#2a4060" font-size="8" letter-spacing="1">WEIGHT REALLOCATION</text>
<g transform="translate(0,82)">
<rect width="120" height="24" rx="4" fill="rgba(255,107,43,0.1)" stroke="#ff6b2b" stroke-width="0.8"/>
<text x="60" y="16" text-anchor="middle" fill="#ff6b2b" font-size="9">传动系统 15kg</text>
<text x="145" y="16" fill="#3e5278" font-size="14">→</text>
<rect x="170" width="120" height="24" rx="4" fill="rgba(0,255,136,0.08)" stroke="#00ff88" stroke-width="0.8"/>
<text x="230" y="16" text-anchor="middle" fill="#00ff88" font-size="9">结构强度 +15kg</text>
</g>
<line x1="0" y1="116" x2="392" y2="116" stroke="#1a2a48" stroke-width="0.5"/>
<text y="134" fill="#2a4060" font-size="8" letter-spacing="1">BOUNDARY / RISK</text>
<text y="152" fill="#5a3a3a" font-size="9">⚠ 长时间极限制动 → 磁钢退磁风险</text>
<text y="168" fill="#5a3a3a" font-size="9">⚠ 5m/s 轨道接缝冲击 → 轴承疲劳</text>
<text y="184" fill="#5a3a3a" font-size="9">⚠ 粉尘/湿滑轨道 → 打滑失控</text>
</g>
</g>
<!-- ===== 底部参数条 ===== -->
<g id="svg-params" transform="translate(50,790)" font-family="Orbitron" font-size="10">
<g transform="translate(0,0)">
<text fill="#3e5278" font-size="8" letter-spacing="1">SPEED</text>
<text id="svg-speed" y="18" fill="#00d4ff" font-size="16" font-weight="700">0.0</text>
<text x="52" y="18" fill="#3e5278" font-size="8">m/s</text>
</g>
<g transform="translate(120,0)">
<text fill="#3e5278" font-size="8" letter-spacing="1">ACCEL</text>
<text id="svg-accel" y="18" fill="#ff6b2b" font-size="16" font-weight="700">0.0</text>
<text x="52" y="18" fill="#3e5278" font-size="8">m/s²</text>
</g>
<g transform="translate(240,0)">
<text fill="#3e5278" font-size="8" letter-spacing="1">TORQUE</text>
<text id="svg-torque" y="18" fill="#00d4ff" font-size="16" font-weight="700">0</text>
<text x="38" y="18" fill="#3e5278" font-size="8">Nm</text>
</g>
<g transform="translate(350,0)">
<text fill="#3e5278" font-size="8" letter-spacing="1">REGEN</text>
<text id="svg-regen" y="18" fill="#00ff88" font-size="16" font-weight="700">0</text>
<text x="44" y="18" fill="#3e5278" font-size="8">W</text>
</g>
<g transform="translate(460,0)">
<text fill="#3e5278" font-size="8" letter-spacing="1">MOTOR TEMP</text>
<text id="svg-temp" y="18" fill="#ffd600" font-size="16" font-weight="700">25</text>
<text x="42" y="18" fill="#3e5278" font-size="8">°C</text>
</g>
<g transform="translate(590,0)">
<text fill="#3e5278" font-size="8" letter-spacing="1">FORK</text>
<text id="svg-fork" y="18" fill="#ff6b2b" font-size="16" font-weight="700">0</text>
<text x="38" y="18" fill="#3e5278" font-size="8">mm</text>
</g>
</g>
</svg>
</div>
<!-- 控制面板 -->
<div id="controls">
<div class="ctrl-section">
<span class="ctrl-label">驱动</span>
<button class="phase-btn active" data-phase="standby"><i class="fas fa-power-off"></i>待机</button>
<button class="phase-btn" data-phase="accelerate"><i class="fas fa-bolt"></i>起步加速</button>
<button class="phase-btn" data-phase="cruise"><i class="fas fa-gauge-high"></i>稳速行驶</button>
<button class="phase-btn" data-phase="brake"><i class="fas fa-rotate-left"></i>再生制动</button>
<button class="phase-btn" data-phase="stop"><i class="fas fa-stop"></i>抱刹停止</button>
</div>
<div class="sep"></div>
<div class="ctrl-section">
<span class="ctrl-label">货叉</span>
<button class="fork-btn" id="fork-extend-btn"><i class="fas fa-arrows-left-right"></i>伸出货叉</button>
<button class="fork-btn" id="fork-retract-btn"><i class="fas fa-compress"></i>缩回货叉</button>
<div class="slider-group">
<label>伸缩量</label>
<input type="range" id="fork-slider" min="0" max="150" step="5" value="0"/>
<span class="slider-val" id="fork-val">0 mm</span>
</div>
</div>
<div class="sep"></div>
<div class="ctrl-section">
<span class="ctrl-label">参数</span>
<div class="slider-group">
<label>目标速度</label>
<input type="range" id="speed-slider" min="1" max="7" step="0.5" value="5"/>
<span class="slider-val" id="speed-val">5.0 m/s</span>
</div>
<div class="slider-group">
<label>轨道摩擦</label>
<input type="range" id="friction-slider" min="0.3" max="1" step="0.1" value="0.8"/>
<span class="slider-val" id="friction-val">0.8</span>
</div>
</div>
</div>
<script>
const SVG_NS = 'http://www.w3.org/2000/svg';
// ===== 状态管理 =====
const state = {
phase: 'standby',
speed: 0,
targetSpeed: 5,
friction: 0.8,
acceleration: 0,
torque: 0,
regenPower: 0,
motorTemp: 25,
trackOffset: 0,
wheelAngle: 0,
time: 0,
phaseTime: 0,
powerFlowOffset: 0,
emFieldAngle: 0,
// 货叉状态
forkExtension: 0, // 当前伸缩量(像素单位,0-150)
forkTarget: 0, // 目标伸缩量
forkSpeed: 0, // 伸缩速度
forkMoving: false, // 是否正在伸缩
forkDirection: 0, // 1=伸出, -1=缩回, 0=静止
};
// 货叉伸缩的物理参数
const FORK_MAX = 150; // 最大伸缩像素
const FORK_SPEED = 80; // 伸缩速度 px/s
const FORK_SCALE = 1.0; // 1px = 1mm(标注用)
// ===== 初始化 SVG 动态元素 =====
function initDynamicElements() {
// 轨道枕木
const tiesGroup = document.getElementById('track-ties');
for (let i = 0; i < 40; i++) {
const rect = document.createElementNS(SVG_NS, 'rect');
rect.setAttribute('x', i * 50);
rect.setAttribute('y', '632');
rect.setAttribute('width', '20');
rect.setAttribute('height', '16');
rect.setAttribute('rx', '1');
rect.setAttribute('fill', '#0e1830');
rect.setAttribute('stroke', '#1a2a44');
rect.setAttribute('stroke-width', '0.5');
rect.setAttribute('class', 'track-tie');
tiesGroup.appendChild(rect);
}
// 轮毂电机磁钢
const rotorG = document.getElementById('rotor-magnets');
for (let i = 0; i < 12; i++) {
const angle = (i * 30) * Math.PI / 180;
const r = 36;
const x = 390 + Math.cos(angle) * r;
const y = 590 + Math.sin(angle) * r;
const rect = document.createElementNS(SVG_NS, 'rect');
rect.setAttribute('x', x - 4);
rect.setAttribute('y', y - 3);
rect.setAttribute('width', '8');
rect.setAttribute('height', '6');
rect.setAttribute('rx', '1');
rect.setAttribute('fill', i % 2 === 0 ? '#ff3d5a' : '#3d5aff');
rect.setAttribute('opacity', '0.5');
rect.setAttribute('transform', `rotate(${i * 30}, ${x}, ${y})`);
rotorG.appendChild(rect);
}
// 定子线圈
const statorG = document.getElementById('stator-coils');
for (let i = 0; i < 9; i++) {
const angle = (i * 40) * Math.PI / 180;
const r = 22;
const x = 390 + Math.cos(angle) * r;
const y = 590 + Math.sin(angle) * r;
const circle = document.createElementNS(SVG_NS, 'circle');
circle.setAttribute('cx', x);
circle.setAttribute('cy', y);
circle.setAttribute('r', '4');
circle.setAttribute('fill', '#0a1428');
circle.setAttribute('stroke', '#00d4ff');
circle.setAttribute('stroke-width', '0.8');
circle.setAttribute('opacity', '0.4');
circle.setAttribute('class', 'stator-coil');
statorG.appendChild(circle);
}
// 剖面图磁钢
const detailMag = document.getElementById('detail-magnets');
for (let i = 0; i < 20; i++) {
const angle = (i * 18) * Math.PI / 180;
const r = 75;
const x = Math.cos(angle) * r;
const y = Math.sin(angle) * r;
const rect = document.createElementNS(SVG_NS, 'rect');
rect.setAttribute('x', x - 5);
rect.setAttribute('y', y - 3.5);
rect.setAttribute('width', '10');
rect.setAttribute('height', '7');
rect.setAttribute('rx', '1');
rect.setAttribute('fill', i % 2 === 0 ? '#ff3d5a' : '#3d5aff');
rect.setAttribute('opacity', '0.45');
rect.setAttribute('transform', `rotate(${i * 18}, ${x}, ${y})`);
detailMag.appendChild(rect);
}
// 剖面图定子齿+线圈
const detailStator = document.getElementById('detail-stator');
for (let i = 0; i < 12; i++) {
const g = document.createElementNS(SVG_NS, 'g');
g.setAttribute('transform', `rotate(${i * 30})`);
const tooth = document.createElementNS(SVG_NS, 'rect');
tooth.setAttribute('x', '-3');
tooth.setAttribute('y', '-52');
tooth.setAttribute('width', '6');
tooth.setAttribute('height', '14');
tooth.setAttribute('rx', '1');
tooth.setAttribute('fill', '#1a2a44');
g.appendChild(tooth);
const coil = document.createElementNS(SVG_NS, 'rect');
coil.setAttribute('x', '-6');
coil.setAttribute('y', '-48');
coil.setAttribute('width', '12');
coil.setAttribute('height', '8');
coil.setAttribute('rx', '2');
coil.setAttribute('fill', 'none');
coil.setAttribute('stroke', '#00d4ff');
coil.setAttribute('stroke-width', '0.8');
coil.setAttribute('opacity', '0.3');
coil.setAttribute('class', 'detail-coil');
g.appendChild(coil);
detailStator.appendChild(g);
}
// 速度线
const speedG = document.getElementById('speed-lines');
for (let i = 0; i < 20; i++) {
const line = document.createElementNS(SVG_NS, 'line');
line.setAttribute('x1', '0');
line.setAttribute('y1', String(350 + Math.random() * 280));
line.setAttribute('x2', String(30 + Math.random() * 80));
line.setAttribute('y2', String(350 + Math.random() * 280));
line.setAttribute('stroke', '#00d4ff');
line.setAttribute('stroke-width', '1');
line.setAttribute('opacity', '0');
line.setAttribute('class', 'speed-line');
speedG.appendChild(line);
}
}
// ===== 阶段切换 =====
function setPhase(newPhase) {
state.phase = newPhase;
state.phaseTime = 0;
document.querySelectorAll('.phase-btn').forEach(btn => {
btn.classList.toggle('active', btn.dataset.phase === newPhase);
});
const names = {standby:'STANDBY', accelerate:'ACCELERATE', cruise:'CRUISE', brake:'REGEN BRAKE', stop:'FULL STOP'};
const colors = {standby:'#3e5278', accelerate:'#00d4ff', cruise:'#ffd600', brake:'#00ff88', stop:'#ff3d5a'};
document.getElementById('phase-name').textContent = names[newPhase];
document.getElementById('phase-name').setAttribute('fill', colors[newPhase]);
}
// ===== 货叉控制 =====
function extendFork() {
state.forkTarget = FORK_MAX;
state.forkDirection = 1;
state.forkMoving = true;
document.getElementById('fork-extend-btn').classList.add('extending');
document.getElementById('fork-retract-btn').classList.remove('retracting');
}
function retractFork() {
state.forkTarget = 0;
state.forkDirection = -1;
state.forkMoving = true;
document.getElementById('fork-retract-btn').classList.add('retracting');
document.getElementById('fork-extend-btn').classList.remove('extending');
}
function setForkTarget(val) {
state.forkTarget = val;
state.forkDirection = val > state.forkExtension ? 1 : (val < state.forkExtension ? -1 : 0);
state.forkMoving = Math.abs(val - state.forkExtension) > 0.5;
}
// ===== 状态更新 =====
function updateState(dt) {
state.time += dt;
state.phaseTime += dt;
// 驱动状态更新
switch (state.phase) {
case 'standby':
state.speed = Math.max(0, state.speed - 3 * dt);
state.torque = 0;
state.acceleration = state.speed > 0.01 ? -3 : 0;
state.regenPower = 0;
break;
case 'accelerate': {
const maxA = 2.5 * state.friction;
state.acceleration = maxA;
state.speed = Math.min(state.targetSpeed, state.speed + maxA * dt);
state.torque = 100 * (1 - state.speed / state.targetSpeed * 0.4) * state.friction;
state.regenPower = 0;
if (state.speed >= state.targetSpeed * 0.98) setPhase('cruise');
break;
}
case 'cruise':
state.speed = state.targetSpeed;
state.acceleration = 0;
state.torque = 15;
state.regenPower = 0;
break;
case 'brake': {
const brakeA = -4 * state.friction;
state.acceleration = brakeA;
state.speed = Math.max(0, state.speed + brakeA * dt);
state.torque = -80 * state.friction;
state.regenPower = Math.abs(state.speed * state.torque * 0.08);
state.motorTemp = Math.min(135, state.motorTemp + 20 * dt);
if (state.speed <= 0.05) setPhase('stop');
break;
}
case 'stop':
state.speed = 0;
state.acceleration = 0;
state.torque = 0;
state.regenPower = 0;
break;
}
if (state.phase !== 'brake') {
state.motorTemp = Math.max(25, state.motorTemp - 3 * dt);
}
// 货叉伸缩更新
if (state.forkMoving) {
const diff = state.forkTarget - state.forkExtension;
const step = FORK_SPEED * dt;
if (Math.abs(diff) <= step) {
state.forkExtension = state.forkTarget;
state.forkMoving = false;
state.forkDirection = 0;
state.forkSpeed = 0;
document.getElementById('fork-extend-btn').classList.remove('extending');
document.getElementById('fork-retract-btn').classList.remove('retracting');
} else {
state.forkExtension += Math.sign(diff) * step;
state.forkSpeed = Math.sign(diff) * FORK_SPEED;
}
} else {
state.forkSpeed = 0;
}
// 轨道偏移
state.trackOffset = (state.trackOffset + state.speed * 25 * dt) % 50;
// 轮子旋转
state.wheelAngle += state.speed * 3 * dt;
// 电力流动偏移
state.powerFlowOffset = (state.powerFlowOffset + state.speed * 80 * dt) % 40;
// 电磁场旋转
state.emFieldAngle += (state.speed * 2 + (state.phase === 'accelerate' ? 3 : 0)) * dt;
}
// ===== 渲染 =====
function render() {
const isAccel = state.phase === 'accelerate';
const isCruise = state.phase === 'cruise';
const isBrake = state.phase === 'brake';
const isStop = state.phase === 'stop';
const isStandby = state.phase === 'standby';
const isActive = isAccel || isCruise;
const speedRatio = state.speed / 7;
const ext = state.forkExtension;
const isForkMoving = state.forkMoving;
// ---- 轨道枕木滚动 ----
const ties = document.querySelectorAll('.track-tie');
ties.forEach((tie, i) => {
const x = (i * 50 - state.trackOffset + 920) % (50 * 40) - 50;
tie.setAttribute('x', String(x));
});
// ---- 轮子旋转 ----
document.getElementById('spokes-rear').setAttribute('transform', `rotate(${state.wheelAngle * 180 / Math.PI}, 390, 590)`);
document.getElementById('rotor-magnets').setAttribute('transform', `rotate(${state.wheelAngle * 180 / Math.PI * 0.95}, 390, 590)`);
document.getElementById('spokes-front').setAttribute('transform', `rotate(${state.wheelAngle * 180 / Math.PI}, 670, 595)`);
// ---- 定子线圈高亮 ----
const coils = document.querySelectorAll('.stator-coil');
coils.forEach((c, i) => {
const phase = (state.emFieldAngle * 3 + i * 0.7) % (Math.PI * 2);
const bright = isActive ? 0.3 + 0.7 * Math.max(0, Math.sin(phase)) : 0.15;
c.setAttribute('opacity', String(bright));
c.setAttribute('stroke', isBrake ? '#00ff88' : '#00d4ff');
});
const detailCoils = document.querySelectorAll('.detail-coil');
detailCoils.forEach((c, i) => {
const phase = (state.emFieldAngle * 3 + i * 0.5) % (Math.PI * 2);
const bright = isActive || isBrake ? 0.2 + 0.8 * Math.max(0, Math.sin(phase)) : 0.1;
c.setAttribute('opacity', String(bright));
c.setAttribute('stroke', isBrake ? '#00ff88' : '#00d4ff');
});
// ---- 电力路径流动 ----
['power-path-cm1', 'power-path-cm2', 'power-path-bc'].forEach(id => {
const el = document.getElementById(id);
if (isActive) {
el.setAttribute('stroke', '#00d4ff');
el.setAttribute('stroke-dashoffset', String(-state.powerFlowOffset));
el.setAttribute('opacity', String(0.4 + speedRatio * 0.5));
} else if (isBrake) {
el.setAttribute('stroke', '#00ff88');
el.setAttribute('stroke-dashoffset', String(state.powerFlowOffset));
el.setAttribute('opacity', String(0.3 + speedRatio * 0.4));
} else {
el.setAttribute('stroke', '#2a4060');
el.setAttribute('opacity', '0.25');
}
});
// ---- 驱动力/再生力箭头 ----
document.getElementById('drive-force').setAttribute('opacity', String(isActive ? Math.min(1, Math.abs(state.torque) / 60) : 0));
document.getElementById('regen-force').setAttribute('opacity', String(isBrake ? Math.min(1, Math.abs(state.torque) / 60) : 0));
// ---- 力矩弧线 ----
document.getElementById('torque-arc').setAttribute('opacity', String(isActive ? 0.8 : 0));
document.getElementById('regen-arc').setAttribute('opacity', String(isBrake ? 0.8 : 0));
// ---- 控制器LED ----
const led = document.getElementById('ctrl-led');
if (isActive) {
led.setAttribute('fill', '#00d4ff');
led.setAttribute('filter', 'url(#softglow)');
} else if (isBrake) {
led.setAttribute('fill', '#00ff88');
led.setAttribute('filter', 'url(#softglow)');
} else if (isStop) {
led.setAttribute('fill', '#ff3d5a');
led.removeAttribute('filter');
} else {
led.setAttribute('fill', '#1a2a40');
led.removeAttribute('filter');
}
// ---- 电池条 ----
const batBar = document.getElementById('bat-bar');
const batLevel = isBrake ? Math.min(1, 0.5 + state.regenPower / 80) : Math.max(0.2, 0.8 - speedRatio * 0.3);
batBar.setAttribute('height', String(20 * batLevel));
batBar.setAttribute('fill', isBrake ? '#00ff88' : '#1a4a2a');
// ---- 俯视图差速线 ----
const diffL = document.getElementById('diff-line-l');
const diffR = document.getElementById('diff-line-r');
if (isActive || isBrake) {
const color = isBrake ? '#00ff88' : '#00d4ff';
diffL.setAttribute('stroke', color); diffR.setAttribute('stroke', color);
diffL.setAttribute('stroke-dashoffset', String(-state.powerFlowOffset));
diffR.setAttribute('stroke-dashoffset', String(-state.powerFlowOffset));
diffL.setAttribute('opacity', '0.8'); diffR.setAttribute('opacity', '0.8');
} else {
diffL.setAttribute('stroke', '#2a4060'); diffR.setAttribute('stroke', '#2a4060');
diffL.setAttribute('opacity', '0.4'); diffR.setAttribute('opacity', '0.4');
}
document.getElementById('sync-indicator').setAttribute('opacity', String(isActive || isCruise ? 0.9 : 0));
document.getElementById('direction-arrow').setAttribute('opacity', String(state.speed > 0.1 ? 0.8 : 0));
// ---- 速度线 ----
const speedLines = document.querySelectorAll('.speed-line');
speedLines.forEach((line, i) => {
if (state.speed > 1) {
const alpha = Math.min(0.5, (state.speed - 1) / 6) * (0.3 + Math.random() * 0.7);
line.setAttribute('opacity', String(alpha));
line.setAttribute('stroke', isBrake ? '#00ff88' : '#00d4ff');
const y = 360 + ((i * 47 + state.time * 200) % 260);
const len = 30 + state.speed * 15 + Math.random() * 40;
const x = -len - Math.random() * 200;
line.setAttribute('y1', String(y)); line.setAttribute('y2', String(y));
line.setAttribute('x1', String(x)); line.setAttribute('x2', String(x + len));
} else {
line.setAttribute('opacity', '0');
}
});
// ---- 轮毂电机发光 ----
const hubGroup = document.getElementById('hub-motor-group');
if (isActive) hubGroup.setAttribute('filter', 'url(#glow-cyan)');
else if (isBrake) hubGroup.setAttribute('filter', 'url(#glow-green)');
else hubGroup.removeAttribute('filter');
// ---- 零传动链标注 ----
document.getElementById('no-shaft-label').setAttribute('opacity', String(0.35 + speedRatio * 0.35));
// ---- 俯视图电机高亮 ----
['top-left-motor', 'top-right-motor'].forEach(id => {
const g = document.getElementById(id);
const circle = g.querySelector('circle:nth-child(2)');
if (isActive) { circle.setAttribute('stroke', '#00d4ff'); circle.setAttribute('stroke-width', '2'); }
else if (isBrake) { circle.setAttribute('stroke', '#00ff88'); circle.setAttribute('stroke-width', '2'); }
else { circle.setAttribute('stroke', '#1e3050'); circle.setAttribute('stroke-width', '1'); }
});
// ============================================================
// 货叉伸缩动画渲染
// ============================================================
// 主视图货叉平移
const forkUpper = document.getElementById('fork-upper');
const forkLower = document.getElementById('fork-lower');
const forkVerts = document.getElementById('fork-verticals');
forkUpper.setAttribute('transform', `translate(${ext}, 0)`);
forkLower.setAttribute('transform', `translate(${ext}, 0)`);
// 竖向连接件跟随
forkVerts.setAttribute('transform', `translate(${ext}, 0)`);
// 俯视图货叉伸缩
const topScale = ext / FORK_MAX;
const topExt = ext * 0.6; // 俯视图中的伸缩量(缩放比例)
const forkTopUpper = document.getElementById('fork-top-upper');
const forkTopLower = document.getElementById('fork-top-lower');
const forkTopTipU = document.getElementById('fork-top-tip-u');
const forkTopTipL = document.getElementById('fork-top-tip-l');
forkTopUpper.setAttribute('x', String(310 + topExt));
forkTopUpper.setAttribute('width', String(100 + topExt * 0.3));
forkTopLower.setAttribute('x', String(310 + topExt));
forkTopLower.setAttribute('width', String(100 + topExt * 0.3));
// 叉尖跟随
const tipUx = 310 + topExt + 100 + topExt * 0.3;
forkTopTipU.setAttribute('d', `M${tipUx},80 L${tipUx + 15},83 L${tipUx + 15},91 L${tipUx},94Z`);
forkTopTipL.setAttribute('d', `M${tipUx},186 L${tipUx + 15},189 L${tipUx + 15},197 L${tipUx},200Z`);
// 导轨发光
const railGlow = document.getElementById('rail-glow');
const glowOpacity = isForkMoving ? (0.5 + 0.3 * Math.sin(state.time * 8)) : (ext > 0 ? 0.15 : 0);
railGlow.setAttribute('opacity', String(glowOpacity));
if (isForkMoving) {
const color = state.forkDirection > 0 ? '#ff6b2b' : '#00ff88';
railGlow.querySelectorAll('rect').forEach(r => {
r.setAttribute('fill', `url(#rail-glow-grad)`);
});
// 更新渐变颜色
const gradStops = document.getElementById('rail-glow-grad').querySelectorAll('stop');
gradStops[1].setAttribute('stop-color', color);
gradStops[2].setAttribute('stop-color', color);
}
// 推杆指示
const actuatorRod = document.getElementById('fork-actuator-rod');
const rodExt = Math.min(25, ext * 0.3);
actuatorRod.setAttribute('x', String(740 + rodExt));
actuatorRod.setAttribute('width', String(30 - rodExt * 0.5));
const actuatorGroup = document.getElementById('fork-actuator');
actuatorGroup.setAttribute('opacity', String(isForkMoving ? 0.9 : 0.4));
// 伸缩位移标注
const forkDim = document.getElementById('fork-dimension');
const dimEnd = document.getElementById('fork-dim-end');
const dimText = document.getElementById('fork-dim-text');
const showDim = ext > 2 || isForkMoving;
forkDim.setAttribute('opacity', String(showDim ? 0.9 : 0));
if (showDim) {
const startX = 690;
const endX = 690 + ext;
dimEnd.setAttribute('x1', String(endX));
dimEnd.setAttribute('x2', String(endX));
// 标注水平线
const dimLine = forkDim.querySelector('line:nth-child(3)');
if (dimLine) {
dimLine.setAttribute('x1', String(startX));
dimLine.setAttribute('x2', String(endX));
}
dimText.setAttribute('x', String((startX + endX) / 2));
dimText.textContent = Math.round(ext * FORK_SCALE) + 'mm';
// 颜色
const dimColor = state.forkDirection > 0 ? '#ff6b2b' : (state.forkDirection < 0 ? '#00ff88' : '#ff6b2b');
dimText.setAttribute('fill', dimColor);
}
// 货叉标签 - 随伸缩移动
const forkLabel = document.getElementById('fork-label-text');
forkLabel.setAttribute('x', String(790 + ext * 0.5));
if (isForkMoving) {
forkLabel.setAttribute('fill', state.forkDirection > 0 ? '#ff6b2b' : '#00ff88');
forkLabel.textContent = state.forkDirection > 0 ? 'EXTENDING...' : 'RETRACTING...';
} else if (ext > 0) {
forkLabel.setAttribute('fill', '#2a4060');
forkLabel.textContent = 'CARBON FORK';
} else {
forkLabel.setAttribute('fill', '#1e3050');
forkLabel.textContent = 'CARBON FORK';
}
// 伸缩过程中叉尖闪烁
if (isForkMoving) {
const blink = 0.5 + 0.5 * Math.sin(state.time * 12);
forkUpper.querySelector('path').setAttribute('fill', `rgba(255,107,43,${blink * 0.3})`);
forkLower.querySelector('path').setAttribute('fill', `rgba(255,107,43,${blink * 0.3})`);
forkUpper.querySelector('path').setAttribute('stroke', state.forkDirection > 0 ? '#ff6b2b' : '#00ff88');
forkLower.querySelector('path').setAttribute('stroke', state.forkDirection > 0 ? '#ff6b2b' : '#00ff88');
} else {
forkUpper.querySelector('path').setAttribute('fill', '#14203a');
forkLower.querySelector('path').setAttribute('fill', '#14203a');
forkUpper.querySelector('path').setAttribute('stroke', '#1e3050');
forkLower.querySelector('path').setAttribute('stroke', '#1e3050');
}
// ---- 参数更新 ----
document.getElementById('svg-speed').textContent = state.speed.toFixed(1);
document.getElementById('svg-accel').textContent = Math.abs(state.acceleration).toFixed(1);
document.getElementById('svg-torque').textContent = Math.abs(Math.round(state.torque));
document.getElementById('svg-regen').textContent = Math.round(state.regenPower);
document.getElementById('svg-temp').textContent = Math.round(state.motorTemp);
document.getElementById('svg-fork').textContent = Math.round(ext * FORK_SCALE);
// 温度颜色
const tempEl = document.getElementById('svg-temp');
if (state.motorTemp > 100) tempEl.setAttribute('fill', '#ff3d5a');
else if (state.motorTemp > 70) tempEl.setAttribute('fill', '#ff6b2b');
else if (state.motorTemp > 45) tempEl.setAttribute('fill', '#ffd600');
else tempEl.setAttribute('fill', '#ffd600');
// 加速度颜色
const accelEl = document.getElementById('svg-accel');
accelEl.setAttribute('fill', state.acceleration >= 0 ? '#00d4ff' : '#ff6b2b');
// 力矩颜色
const torqueEl = document.getElementById('svg-torque');
torqueEl.setAttribute('fill', state.torque >= 0 ? '#00d4ff' : '#00ff88');
// 货叉状态值
document.getElementById('fork-status-val').textContent = Math.round(ext * FORK_SCALE) + ' mm';
const forkStatusColor = isForkMoving ? (state.forkDirection > 0 ? '#ff6b2b' : '#00ff88') : (ext > 0 ? '#ff6b2b' : '#3e5278');
document.getElementById('fork-status-val').setAttribute('fill', forkStatusColor);
}
// ===== 动画循环 =====
let lastTime = 0;
function animate(timestamp) {
const dt = Math.min(0.05, (timestamp - lastTime) / 1000);
lastTime = timestamp;
updateState(dt);
render();
requestAnimationFrame(animate);
}
// ===== 事件绑定 =====
document.querySelectorAll('.phase-btn').forEach(btn => {
btn.addEventListener('click', () => setPhase(btn.dataset.phase));
});
document.getElementById('speed-slider').addEventListener('input', e => {
state.targetSpeed = parseFloat(e.target.value);
document.getElementById('speed-val').textContent = state.targetSpeed.toFixed(1) + ' m/s';
});
document.getElementById('friction-slider').addEventListener('input', e => {
state.friction = parseFloat(e.target.value);
document.getElementById('friction-val').textContent = state.friction.toFixed(1);
});
// 货叉伸出按钮
document.getElementById('fork-extend-btn').addEventListener('click', extendFork);
// 货叉缩回按钮
document.getElementById('fork-retract-btn').addEventListener('click', retractFork);
// 货叉滑块
document.getElementById('fork-slider').addEventListener('input', e => {
const val = parseFloat(e.target.value);
setForkTarget(val);
document.getElementById('fork-val').textContent = Math.round(val * FORK_SCALE) + ' mm';
});
// ===== 启动 =====
initDynamicElements();
setPhase('standby');
requestAnimationFrame(animate);
</script>
</body>
</html>
新增货叉伸缩动画说明
在原有轮毂直驱系统动画基础上,完整实现了碳纤维层叠板货叉的伸缩动画系统:
主视图伸缩:货叉上下两层碳纤维板沿导轨平滑滑出/缩回,最大行程 150mm。导轨在伸缩过程中发出橙色(伸出)或绿色(缩回)脉冲辉光,叉尖在运动时闪烁提示方向。
位移标注:伸缩过程中自动显示实时位移尺寸线(如 "85mm"),颜色随伸缩方向切换——伸出为橙色、缩回为绿色。
推杆驱动可视化:微型电动推杆随伸缩量联动伸缩,运动时高亮显示。
俯视图联动:右侧俯视图中货叉同步伸缩,叉尖位置实时跟随。
三种控制方式:
- 按钮:一键伸出/缩回到极限位置(带匀速动画过渡)
- 滑块:拖动精确控制任意伸缩量(0-150mm)
- 状态面板:左上角实时显示当前伸缩量
底部参数栏:新增 "FORK" 参数位,以 Orbitron 字体实时显示伸缩毫米数。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>轮毂直驱系统 · IFR 最终理想解原理动画</title>
<link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700;900&family=JetBrains+Mono:wght@300;400;500;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<style>
*{margin:0;padding:0;box-sizing:border-box}
:root{
--bg:#050a14;--bg2:#0a1628;--cyan:#00d4ff;--cyan-dim:rgba(0,212,255,0.12);
--orange:#ff6b2b;--green:#00ff88;--yellow:#ffd600;--red:#ff3d5a;
--white:#dce4f0;--muted:#3e5278;--struct:#14203a;--carbon:#0e1830;
--border:#1a2a48;
}
body{background:var(--bg);color:var(--white);font-family:'JetBrains Mono',monospace;overflow:hidden;height:100vh;display:flex;flex-direction:column;user-select:none}
header{padding:12px 28px;display:flex;align-items:baseline;gap:18px;border-bottom:1px solid var(--border);background:linear-gradient(180deg,rgba(10,22,40,0.95),transparent);position:relative;z-index:2}
header h1{font-family:'Orbitron',sans-serif;font-size:18px;font-weight:700;letter-spacing:2px;color:var(--cyan);text-shadow:0 0 20px rgba(0,212,255,0.3)}
header p{font-size:11px;color:var(--muted);letter-spacing:1px}
#svg-wrap{flex:1;display:flex;align-items:center;justify-content:center;overflow:hidden;position:relative}
#main-svg{width:100%;height:100%;max-height:calc(100vh - 120px)}
#controls{padding:10px 24px;display:flex;align-items:center;gap:14px;border-top:1px solid var(--border);background:linear-gradient(0deg,rgba(10,22,40,0.98),rgba(10,22,40,0.85));flex-wrap:wrap;justify-content:center}
.ctrl-section{display:flex;align-items:center;gap:10px}
.ctrl-label{font-size:9px;color:#2a4060;font-family:'Orbitron',sans-serif;letter-spacing:1px;text-transform:uppercase;writing-mode:vertical-lr;text-orientation:mixed}
.phase-btn{padding:7px 16px;border:1px solid var(--border);border-radius:6px;background:var(--carbon);color:var(--muted);font-family:'JetBrains Mono',monospace;font-size:11px;cursor:pointer;transition:all .25s;letter-spacing:0.5px;display:flex;align-items:center;gap:6px}
.phase-btn:hover{border-color:var(--cyan);color:var(--white)}
.phase-btn.active{border-color:var(--cyan);color:var(--cyan);background:var(--cyan-dim);box-shadow:0 0 12px rgba(0,212,255,0.15)}
.phase-btn i{font-size:10px}
.sep{width:1px;height:28px;background:var(--border);margin:0 6px;flex-shrink:0}
.slider-group{display:flex;align-items:center;gap:8px;font-size:10px;color:var(--muted)}
.slider-group label{white-space:nowrap}
.slider-group input[type=range]{-webkit-appearance:none;width:100px;height:4px;background:var(--border);border-radius:2px;outline:none}
.slider-group input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;width:14px;height:14px;background:var(--cyan);border-radius:50%;cursor:pointer;box-shadow:0 0 6px rgba(0,212,255,0.4)}
.slider-val{color:var(--cyan);min-width:36px;text-align:right;font-weight:700;font-size:11px}
.fork-btn{padding:7px 18px;border:1px solid var(--border);border-radius:6px;background:var(--carbon);color:var(--muted);font-family:'JetBrains Mono',monospace;font-size:11px;cursor:pointer;transition:all .25s;display:flex;align-items:center;gap:6px}
.fork-btn:hover{border-color:var(--orange);color:var(--white)}
.fork-btn.extending{border-color:var(--orange);color:var(--orange);background:rgba(255,107,43,0.1);box-shadow:0 0 12px rgba(255,107,43,0.15)}
.fork-btn.retracting{border-color:var(--green);color:var(--green);background:rgba(0,255,136,0.08)}
</style>
</head>
<body>
<header>
<h1>HUB-DIRECT DRIVE</h1>
<p>IFR 最终理想解 · 零传动链原理动画 · 轮径160mm</p>
</header>
<div id="svg-wrap">
<svg id="main-svg" viewBox="0 0 1400 850" preserveAspectRatio="xMidYMid meet">
<defs>
<pattern id="carbon" width="8" height="8" patternUnits="userSpaceOnUse" patternTransform="rotate(45)">
<rect width="8" height="8" fill="#0e1830"/><rect width="4" height="8" fill="#13203e"/>
</pattern>
<pattern id="grid" width="40" height="40" patternUnits="userSpaceOnUse">
<path d="M40 0L0 0 0 40" fill="none" stroke="#0c1828" stroke-width="0.5"/>
</pattern>
<filter id="glow-cyan" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur in="SourceGraphic" stdDeviation="6" result="b"/>
<feFlood flood-color="#00d4ff" flood-opacity="0.5"/><feComposite in2="b" operator="in" result="g"/>
<feMerge><feMergeNode in="g"/><feMergeNode in="SourceGraphic"/></feMerge>
</filter>
<filter id="glow-green" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur in="SourceGraphic" stdDeviation="5" result="b"/>
<feFlood flood-color="#00ff88" flood-opacity="0.45"/><feComposite in2="b" operator="in" result="g"/>
<feMerge><feMergeNode in="g"/><feMergeNode in="SourceGraphic"/></feMerge>
</filter>
<filter id="softglow" x="-30%" y="-30%" width="160%" height="160%">
<feGaussianBlur in="SourceGraphic" stdDeviation="3" result="b"/>
<feMerge><feMergeNode in="b"/><feMergeNode in="SourceGraphic"/></feMerge>
</filter>
<marker id="arrow-cyan" viewBox="0 0 10 6" refX="10" refY="3" markerWidth="10" markerHeight="6" orient="auto"><path d="M0,0 L10,3 L0,6Z" fill="#00d4ff"/></marker>
<marker id="arrow-orange" viewBox="0 0 10 6" refX="10" refY="3" markerWidth="10" markerHeight="6" orient="auto"><path d="M0,0 L10,3 L0,6Z" fill="#ff6b2b"/></marker>
<marker id="arrow-green" viewBox="0 0 10 6" refX="10" refY="3" markerWidth="10" markerHeight="6" orient="auto"><path d="M0,0 L10,3 L0,6Z" fill="#00ff88"/></marker>
<radialGradient id="tire-grad" cx="50%" cy="45%"><stop offset="0%" stop-color="#1a1a2a"/><stop offset="80%" stop-color="#0a0a14"/><stop offset="100%" stop-color="#050508"/></radialGradient>
<linearGradient id="rail-grad" x1="0" y1="0" x2="1" y2="0">
<stop offset="0%" stop-color="#1e3050"/><stop offset="50%" stop-color="#2a4568"/><stop offset="100%" stop-color="#1e3050"/>
</linearGradient>
<linearGradient id="rail-glow-grad" x1="0" y1="0" x2="1" y2="0">
<stop offset="0%" stop-color="#ff6b2b" stop-opacity="0"/><stop offset="20%" stop-color="#ff6b2b" stop-opacity="0.6"/><stop offset="80%" stop-color="#ff6b2b" stop-opacity="0.6"/><stop offset="100%" stop-color="#ff6b2b" stop-opacity="0"/>
</linearGradient>
</defs>
<rect width="1400" height="850" fill="url(#grid)"/>
<ellipse cx="500" cy="700" rx="500" ry="120" fill="rgba(0,212,255,0.02)"/>
<!-- ===== 轨道 ===== -->
<g id="track-group">
<line x1="0" y1="670" x2="920" y2="670" stroke="#1a2a48" stroke-width="3"/>
<line x1="0" y1="676" x2="920" y2="676" stroke="#1a2a48" stroke-width="1.5"/>
<g id="track-ties"></g>
</g>
<!-- ===== 速度线 ===== -->
<g id="speed-lines"></g>
<!-- ===== 车辆主体 ===== -->
<!--
轮径160mm → SVG半径32
后轮中心: (390, 638) 底部y=670
前轮中心: (670, 644) 底部y=670
底盘: y=580~615
-->
<g id="vehicle-group">
<!-- 碳纤维底盘桁架 -->
<g id="chassis" stroke="#1e3050" stroke-width="2.5" fill="none">
<line x1="310" y1="580" x2="690" y2="580" stroke="#2a4060" stroke-width="3.5"/>
<line x1="290" y1="615" x2="710" y2="615" stroke="#2a4060" stroke-width="3.5"/>
<line x1="350" y1="580" x2="345" y2="615"/>
<line x1="430" y1="580" x2="425" y2="615"/>
<line x1="510" y1="580" x2="510" y2="615"/>
<line x1="590" y1="580" x2="595" y2="615"/>
<line x1="670" y1="580" x2="675" y2="615"/>
<line x1="345" y1="615" x2="430" y2="580" stroke="#1e3050" stroke-width="1.8"/>
<line x1="425" y1="615" x2="510" y2="580" stroke="#1e3050" stroke-width="1.8"/>
<line x1="510" y1="615" x2="590" y2="580" stroke="#1e3050" stroke-width="1.8"/>
<line x1="595" y1="615" x2="670" y2="580" stroke="#1e3050" stroke-width="1.8"/>
<line x1="350" y1="580" x2="425" y2="615" stroke="#1e3050" stroke-width="1.5" stroke-dasharray="4,4"/>
<line x1="430" y1="580" x2="510" y2="615" stroke="#1e3050" stroke-width="1.5" stroke-dasharray="4,4"/>
<line x1="510" y1="580" x2="595" y2="615" stroke="#1e3050" stroke-width="1.5" stroke-dasharray="4,4"/>
<line x1="590" y1="580" x2="675" y2="615" stroke="#1e3050" stroke-width="1.5" stroke-dasharray="4,4"/>
<g fill="#2a4a70">
<circle cx="350" cy="580" r="3.5"/><circle cx="430" cy="580" r="3.5"/>
<circle cx="510" cy="580" r="3.5"/><circle cx="590" cy="580" r="3.5"/>
<circle cx="670" cy="580" r="3.5"/><circle cx="345" cy="615" r="3.5"/>
<circle cx="425" cy="615" r="3.5"/><circle cx="510" cy="615" r="3.5"/>
<circle cx="595" cy="615" r="3.5"/><circle cx="675" cy="615" r="3.5"/>
</g>
</g>
<rect x="305" y="583" width="390" height="29" fill="url(#carbon)" opacity="0.4" rx="2"/>
<!-- 控制器盒 -->
<rect x="460" y="586" width="80" height="24" rx="3" fill="#0c1828" stroke="#2a4060" stroke-width="1.2"/>
<text x="500" y="602" text-anchor="middle" fill="#3e5278" font-size="7" font-family="JetBrains Mono">CTRL</text>
<rect id="ctrl-led" x="464" y="590" width="5" height="5" rx="2.5" fill="#1a2a40"/>
<!-- 电池 -->
<rect x="360" y="588" width="60" height="20" rx="2" fill="#0c1424" stroke="#1e3050" stroke-width="1"/>
<text x="390" y="602" text-anchor="middle" fill="#2a4060" font-size="6" font-family="JetBrains Mono">BAT 48V</text>
<rect id="bat-bar" x="365" y="591" width="3" height="14" rx="1" fill="#1a3a20"/>
<!-- 悬挂连接:底盘→后轮轴 -->
<g stroke="#1e3050" stroke-width="2" fill="none">
<line x1="390" y1="615" x2="390" y2="628"/>
<line x1="385" y1="615" x2="378" y2="630"/>
<line x1="395" y1="615" x2="402" y2="630"/>
</g>
<!-- 悬挂连接:底盘→前轮轴 -->
<g stroke="#141e30" stroke-width="1.5" fill="none">
<line x1="670" y1="615" x2="670" y2="630"/>
<line x1="665" y1="615" x2="660" y2="630"/>
<line x1="675" y1="615" x2="680" y2="630"/>
</g>
<!-- ===== 货叉系统 ===== -->
<g id="fork-system">
<g id="fork-rails">
<rect x="690" y="583" width="180" height="3" rx="1" fill="url(#rail-grad)" opacity="0.6"/>
<rect x="690" y="595" width="180" height="3" rx="1" fill="url(#rail-grad)" opacity="0.6"/>
<rect x="690" y="610" width="180" height="3" rx="1" fill="url(#rail-grad)" opacity="0.6"/>
<rect x="690" y="622" width="180" height="3" rx="1" fill="url(#rail-grad)" opacity="0.6"/>
<rect x="866" y="580" width="5" height="20" rx="2" fill="#1a2a44"/>
<rect x="866" y="607" width="5" height="20" rx="2" fill="#1a2a44"/>
</g>
<g id="rail-glow" opacity="0">
<rect x="690" y="581" width="180" height="20" rx="1" fill="url(#rail-glow-grad)" opacity="0.15"/>
<rect x="690" y="607" width="180" height="20" rx="1" fill="url(#rail-glow-grad)" opacity="0.15"/>
</g>
<!-- 伸缩货叉 - 上层 -->
<g id="fork-upper">
<rect x="690" y="586" width="160" height="8" rx="1" fill="url(#carbon)" stroke="#1e3050" stroke-width="1"/>
<line x1="700" y1="588" x2="840" y2="588" stroke="#1a2a44" stroke-width="0.4"/>
<line x1="700" y1="592" x2="840" y2="592" stroke="#1a2a44" stroke-width="0.4"/>
<path d="M850,586 L865,588 L865,592 L850,594Z" fill="#14203a" stroke="#1e3050" stroke-width="0.7"/>
</g>
<!-- 伸缩货叉 - 下层 -->
<g id="fork-lower">
<rect x="690" y="613" width="160" height="8" rx="1" fill="url(#carbon)" stroke="#1e3050" stroke-width="1"/>
<line x1="700" y1="615" x2="840" y2="615" stroke="#1a2a44" stroke-width="0.4"/>
<line x1="700" y1="619" x2="840" y2="619" stroke="#1a2a44" stroke-width="0.4"/>
<path d="M850,613 L865,615 L865,619 L850,621Z" fill="#14203a" stroke="#1e3050" stroke-width="0.7"/>
</g>
<!-- 竖向连接件 -->
<g id="fork-verticals">
<line x1="700" y1="594" x2="700" y2="613" stroke="#1e3050" stroke-width="2"/>
<line x1="730" y1="594" x2="730" y2="613" stroke="#1e3050" stroke-width="1.2"/>
</g>
<!-- 伸缩位移标注 -->
<g id="fork-dimension" opacity="0">
<line id="fork-dim-start" x1="690" y1="575" x2="690" y2="570" stroke="#ff6b2b" stroke-width="0.7"/>
<line id="fork-dim-end" x1="690" y1="575" x2="690" y2="570" stroke="#ff6b2b" stroke-width="0.7"/>
<line id="fork-dim-line" x1="690" y1="572" x2="690" y2="572" stroke="#ff6b2b" stroke-width="0.7" marker-end="url(#arrow-orange)" marker-start="url(#arrow-orange)"/>
<text id="fork-dim-text" x="690" y="568" text-anchor="middle" fill="#ff6b2b" font-size="8" font-family="Orbitron" font-weight="700">0mm</text>
</g>
<!-- 驱动推杆 -->
<g id="fork-actuator" opacity="0.4">
<rect x="692" y="600" width="40" height="4" rx="1.5" fill="#0e1830" stroke="#1e3050" stroke-width="0.7"/>
<rect id="fork-actuator-rod" x="730" y="601" width="20" height="2" rx="1" fill="#2a4060"/>
</g>
<text id="fork-label-text" x="770" y="606" text-anchor="middle" fill="#1e3050" font-size="6" font-family="JetBrains Mono" letter-spacing="1">CARBON FORK</text>
</g>
<!-- ===== 后轮:轮毂电机 (r=32, φ160mm) ===== -->
<g id="hub-motor-group">
<circle cx="390" cy="638" r="32" fill="url(#tire-grad)" stroke="#1a1a2a" stroke-width="1.5"/>
<circle cx="390" cy="638" r="26" fill="none" stroke="#2a3a5a" stroke-width="2"/>
<circle cx="390" cy="638" r="22" fill="none" stroke="#1e3050" stroke-width="0.8"/>
<g id="rotor-magnets"></g>
<circle cx="390" cy="638" r="17" fill="none" stroke="#0a1428" stroke-width="0.4" stroke-dasharray="1.5,1.5"/>
<circle cx="390" cy="638" r="14" fill="#0c1424" stroke="#1a2a44" stroke-width="0.8"/>
<g id="stator-coils"></g>
<circle cx="390" cy="638" r="5" fill="#14203a" stroke="#2a4060" stroke-width="1"/>
<circle cx="390" cy="638" r="2" fill="#2a4060"/>
<g stroke="#1e3050" stroke-width="1.2" id="spokes-rear">
<line x1="390" y1="623" x2="390" y2="612"/>
<line x1="390" y1="653" x2="390" y2="664"/>
<line x1="375" y1="638" x2="364" y2="638"/>
<line x1="405" y1="638" x2="416" y2="638"/>
<line x1="379" y1="627" x2="371" y2="619"/>
<line x1="401" y1="649" x2="409" y2="657"/>
<line x1="379" y1="649" x2="371" y2="657"/>
<line x1="401" y1="627" x2="409" y2="619"/>
</g>
<text x="390" y="600" text-anchor="middle" fill="#2a4060" font-size="6" font-family="JetBrains Mono" letter-spacing="0.5">HUB MOTOR</text>
<text x="390" y="682" text-anchor="middle" fill="#1e3050" font-size="6" font-family="JetBrains Mono">≤4kg · φ160</text>
</g>
<!-- ===== 前轮:从动轮 (r=26) ===== -->
<g id="passive-wheel-group">
<circle cx="670" cy="644" r="26" fill="url(#tire-grad)" stroke="#1a1a2a" stroke-width="1.2"/>
<circle cx="670" cy="644" r="21" fill="none" stroke="#1a2a40" stroke-width="1.2"/>
<circle cx="670" cy="644" r="4" fill="#0e1830" stroke="#1e3050" stroke-width="0.8"/>
<g stroke="#141e30" stroke-width="0.8" id="spokes-front">
<line x1="670" y1="624" x2="670" y2="618"/>
<line x1="670" y1="664" x2="670" y2="670"/>
<line x1="650" y1="644" x2="644" y2="644"/>
<line x1="690" y1="644" x2="696" y2="644"/>
</g>
<text x="670" y="682" text-anchor="middle" fill="#141e30" font-size="6" font-family="JetBrains Mono">PASSIVE</text>
</g>
<!-- "零传动链"标注 -->
<g id="no-shaft-label" opacity="0.6">
<line x1="420" y1="644" x2="640" y2="644" stroke="#1e3050" stroke-width="0.8" stroke-dasharray="5,3"/>
<text x="530" y="640" text-anchor="middle" fill="#2a4060" font-size="7" font-family="Orbitron" letter-spacing="1">ZERO DRIVE SHAFT</text>
<circle cx="530" cy="650" r="6" fill="none" stroke="#2a4060" stroke-width="0.8"/>
<line x1="525" y1="655" x2="535" y2="645" stroke="#2a4060" stroke-width="0.8"/>
</g>
</g>
<!-- ===== 电力流向线 ===== -->
<g id="power-flow-group">
<path id="power-path-bc" d="M420,598 L460,598" fill="none" stroke="#2a4060" stroke-width="1.5" stroke-dasharray="4,4" opacity="0.5"/>
<path id="power-path-cm1" d="M460,590 Q440,570 390,570 Q355,570 355,610 Q355,628 370,633" fill="none" stroke="#2a4060" stroke-width="1.5" stroke-dasharray="4,6" opacity="0.5"/>
<path id="power-path-cm2" d="M460,605 Q430,625 405,633" fill="none" stroke="#2a4060" stroke-width="1" stroke-dasharray="3,5" opacity="0.3"/>
</g>
<!-- ===== 接触力箭头 ===== -->
<g id="force-arrows">
<g id="drive-force" opacity="0">
<line x1="352" y1="670" x2="310" y2="670" stroke="#00d4ff" stroke-width="2.5" marker-end="url(#arrow-cyan)"/>
<text x="330" y="684" text-anchor="middle" fill="#00d4ff" font-size="8" font-family="Orbitron">F</text>
</g>
<g id="regen-force" opacity="0">
<line x1="310" y1="670" x2="352" y2="670" stroke="#00ff88" stroke-width="2.5" marker-end="url(#arrow-green)"/>
<text x="330" y="684" text-anchor="middle" fill="#00ff88" font-size="8" font-family="Orbitron">REGEN</text>
</g>
</g>
<!-- ===== 阶段指示 ===== -->
<g id="phase-indicator" transform="translate(50,40)">
<rect x="0" y="0" width="200" height="44" rx="6" fill="rgba(10,22,40,0.85)" stroke="#1a2a48" stroke-width="1"/>
<text x="100" y="18" text-anchor="middle" fill="#3e5278" font-size="8" font-family="JetBrains Mono" letter-spacing="1">PHASE</text>
<text id="phase-name" x="100" y="35" text-anchor="middle" fill="#00d4ff" font-size="14" font-family="Orbitron" font-weight="700">STANDBY</text>
</g>
<!-- ===== 货叉伸缩状态 ===== -->
<g id="fork-status" transform="translate(50,92)">
<rect x="0" y="0" width="200" height="38" rx="6" fill="rgba(10,22,40,0.85)" stroke="#1a2a48" stroke-width="1"/>
<text x="100" y="16" text-anchor="middle" fill="#3e5278" font-size="8" font-family="JetBrains Mono" letter-spacing="1">FORK EXTENSION</text>
<text id="fork-status-val" x="100" y="32" text-anchor="middle" fill="#ff6b2b" font-size="13" font-family="Orbitron" font-weight="700">0 mm</text>
</g>
<!-- ===== 右侧面板:俯视图 ===== -->
<g transform="translate(940,20)">
<rect x="0" y="0" width="440" height="260" rx="8" fill="rgba(8,16,32,0.9)" stroke="#1a2a48" stroke-width="1"/>
<text x="220" y="22" text-anchor="middle" fill="#3e5278" font-size="9" font-family="Orbitron" letter-spacing="2">TOP VIEW · ELECTRONIC DIFFERENTIAL</text>
<rect x="130" y="50" width="180" height="180" rx="6" fill="url(#carbon)" stroke="#1e3050" stroke-width="1.5" opacity="0.6"/>
<g id="fork-top-group">
<rect id="fork-top-upper" x="310" y="80" width="100" height="14" rx="2" fill="#0e1830" stroke="#1e3050" stroke-width="1"/>
<rect id="fork-top-lower" x="310" y="186" width="100" height="14" rx="2" fill="#0e1830" stroke="#1e3050" stroke-width="1"/>
<path id="fork-top-tip-u" d="M410,80 L425,83 L425,91 L410,94Z" fill="#14203a" stroke="#1e3050" stroke-width="0.6"/>
<path id="fork-top-tip-l" d="M410,186 L425,189 L425,197 L410,200Z" fill="#14203a" stroke="#1e3050" stroke-width="0.6"/>
</g>
<rect x="190" y="120" width="60" height="40" rx="3" fill="#0c1828" stroke="#2a4060" stroke-width="1"/>
<text x="220" y="144" text-anchor="middle" fill="#2a4060" font-size="7" font-family="JetBrains Mono">ECU</text>
<g id="top-left-motor">
<circle cx="130" cy="140" r="22" fill="#0a1428" stroke="#1a2a44" stroke-width="1.5"/>
<circle cx="130" cy="140" r="14" fill="#0e1830" stroke="#1e3050" stroke-width="1"/>
<circle cx="130" cy="140" r="5" fill="#14203a"/>
<text x="100" y="175" text-anchor="middle" fill="#1e3050" font-size="7" font-family="JetBrains Mono">L-HUB</text>
</g>
<g id="top-right-motor">
<circle cx="310" cy="140" r="22" fill="#0a1428" stroke="#1a2a44" stroke-width="1.5"/>
<circle cx="310" cy="140" r="14" fill="#0e1830" stroke="#1e3050" stroke-width="1"/>
<circle cx="310" cy="140" r="5" fill="#14203a"/>
<text x="340" y="175" text-anchor="middle" fill="#1e3050" font-size="7" font-family="JetBrains Mono">R-HUB</text>
</g>
<circle cx="170" cy="58" r="12" fill="#0a0e18" stroke="#141e30" stroke-width="1"/>
<circle cx="270" cy="58" r="12" fill="#0a0e18" stroke="#141e30" stroke-width="1"/>
<circle cx="170" cy="222" r="12" fill="#0a0e18" stroke="#141e30" stroke-width="1"/>
<circle cx="270" cy="222" r="12" fill="#0a0e18" stroke="#141e30" stroke-width="1"/>
<path id="diff-line-l" d="M190,140 L155,140" fill="none" stroke="#2a4060" stroke-width="1.5" stroke-dasharray="3,3"/>
<path id="diff-line-r" d="M250,140 L285,140" fill="none" stroke="#2a4060" stroke-width="1.5" stroke-dasharray="3,3"/>
<g id="sync-indicator" opacity="0">
<text x="220" y="200" text-anchor="middle" fill="#00d4ff" font-size="8" font-family="Orbitron" letter-spacing="1">SYNC</text>
<rect x="185" y="205" width="70" height="3" rx="1.5" fill="#0a1428"/>
<rect id="sync-bar" x="185" y="205" width="70" height="3" rx="1.5" fill="#00d4ff" opacity="0.6"/>
</g>
<g id="direction-arrow" opacity="0">
<line x1="220" y1="30" x2="220" y2="10" stroke="#00d4ff" stroke-width="2" marker-end="url(#arrow-cyan)"/>
</g>
</g>
<!-- ===== 右侧面板:轮毂电机剖面 ===== -->
<g transform="translate(940,300)">
<rect x="0" y="0" width="440" height="280" rx="8" fill="rgba(8,16,32,0.9)" stroke="#1a2a48" stroke-width="1"/>
<text x="220" y="22" text-anchor="middle" fill="#3e5278" font-size="9" font-family="Orbitron" letter-spacing="2">HUB MOTOR CROSS-SECTION · φ160mm</text>
<g transform="translate(160,155)">
<!-- 160mm直径 → 剖面半径80 -->
<circle r="80" fill="url(#tire-grad)" stroke="#1a1a2a" stroke-width="1.5"/>
<circle r="68" fill="none" stroke="#2a3a5a" stroke-width="1.5"/>
<circle r="60" fill="none" stroke="#1e3050" stroke-width="4" id="rotor-ring"/>
<g id="detail-magnets"></g>
<circle r="48" fill="none" stroke="rgba(0,212,255,0.1)" stroke-width="1.5" stroke-dasharray="2,3"/>
<circle r="42" fill="#0c1424" stroke="#1a2a44" stroke-width="1"/>
<g id="detail-stator"></g>
<circle r="11" fill="#14203a" stroke="#2a4060" stroke-width="1.5"/>
<circle r="4" fill="#1e3050"/>
<g id="torque-arc" opacity="0">
<path d="M-32,-72 A78,78 0 0,1 32,-72" fill="none" stroke="#00d4ff" stroke-width="2" marker-end="url(#arrow-cyan)"/>
<text x="0" y="-78" text-anchor="middle" fill="#00d4ff" font-size="9" font-family="Orbitron" font-weight="700">TORQUE</text>
</g>
<g id="regen-arc" opacity="0">
<path d="M32,-72 A78,78 0 0,1 -32,-72" fill="none" stroke="#00ff88" stroke-width="2" marker-end="url(#arrow-green)"/>
<text x="0" y="-78" text-anchor="middle" fill="#00ff88" font-size="9" font-family="Orbitron" font-weight="700">REGEN</text>
</g>
<!-- 直径标注 -->
<g opacity="0.4">
<line x1="-80" y1="0" x2="80" y2="0" stroke="#2a4060" stroke-width="0.5" stroke-dasharray="3,3"/>
<text x="0" y="-4" text-anchor="middle" fill="#2a4060" font-size="7" font-family="JetBrains Mono">φ160mm</text>
</g>
</g>
<g transform="translate(300,55)" fill="#3e5278" font-size="7" font-family="JetBrains Mono">
<text y="0" fill="#2a4060" font-size="7">STRUCTURE</text>
<circle cx="-7" cy="14" r="2.5" fill="#1a1a2a"/><text x="2" y="17">Tire</text>
<circle cx="-7" cy="28" r="2.5" fill="#2a3a5a"/><text x="2" y="31">Rim</text>
<circle cx="-7" cy="42" r="2.5" fill="#ff3d5a" opacity="0.6"/><text x="2" y="45">Rotor Mag</text>
<circle cx="-7" cy="56" r="2.5" fill="#00d4ff" opacity="0.6"/><text x="2" y="59">Stator Coil</text>
<circle cx="-7" cy="70" r="2.5" fill="#14203a"/><text x="2" y="73">Axle</text>
<!-- 160mm参数 -->
<text y="95" fill="#1e3050" font-size="6" letter-spacing="0.5">Wheel: φ160mm</text>
<text y="108" fill="#1e3050" font-size="6" letter-spacing="0.5">Motor: ≤4kg</text>
</g>
</g>
<!-- ===== 右侧面板:IFR 原则 ===== -->
<g transform="translate(940,600)">
<rect x="0" y="0" width="440" height="230" rx="8" fill="rgba(8,16,32,0.9)" stroke="#1a2a48" stroke-width="1"/>
<text x="220" y="22" text-anchor="middle" fill="#3e5278" font-size="9" font-family="Orbitron" letter-spacing="2">IFR · IDEAL FINAL RESULT</text>
<g transform="translate(24,40)" font-family="JetBrains Mono" font-size="10">
<text fill="#00d4ff" font-size="11" font-weight="700" font-family="Orbitron">Wheel = Motor</text>
<text y="20" fill="#5a7aaa">轮即电机,动力直达轮缘</text>
<text y="40" fill="#5a7aaa">零传动链 · 零机械间隙 · 零中间损耗</text>
<line x1="0" y1="52" x2="392" y2="52" stroke="#1a2a48" stroke-width="0.5"/>
<text y="70" fill="#2a4060" font-size="8" letter-spacing="1">WEIGHT REALLOCATION</text>
<g transform="translate(0,82)">
<rect width="120" height="24" rx="4" fill="rgba(255,107,43,0.1)" stroke="#ff6b2b" stroke-width="0.8"/>
<text x="60" y="16" text-anchor="middle" fill="#ff6b2b" font-size="9">传动系统 15kg</text>
<text x="145" y="16" fill="#3e5278" font-size="14">→</text>
<rect x="170" width="120" height="24" rx="4" fill="rgba(0,255,136,0.08)" stroke="#00ff88" stroke-width="0.8"/>
<text x="230" y="16" text-anchor="middle" fill="#00ff88" font-size="9">结构强度 +15kg</text>
</g>
<line x1="0" y1="116" x2="392" y2="116" stroke="#1a2a48" stroke-width="0.5"/>
<text y="134" fill="#2a4060" font-size="8" letter-spacing="1">BOUNDARY / RISK</text>
<text y="152" fill="#5a3a3a" font-size="9">⚠ 长时间极限制动 → 磁钢退磁风险</text>
<text y="168" fill="#5a3a3a" font-size="9">⚠ 5m/s 轨道接缝冲击 → 轴承疲劳</text>
<text y="184" fill="#5a3a3a" font-size="9">⚠ 粉尘/湿滑轨道 → 打滑失控</text>
</g>
</g>
<!-- ===== 底部参数条 ===== -->
<g id="svg-params" transform="translate(50,790)" font-family="Orbitron" font-size="10">
<g transform="translate(0,0)">
<text fill="#3e5278" font-size="8" letter-spacing="1">SPEED</text>
<text id="svg-speed" y="18" fill="#00d4ff" font-size="16" font-weight="700">0.0</text>
<text x="52" y="18" fill="#3e5278" font-size="8">m/s</text>
</g>
<g transform="translate(120,0)">
<text fill="#3e5278" font-size="8" letter-spacing="1">ACCEL</text>
<text id="svg-accel" y="18" fill="#ff6b2b" font-size="16" font-weight="700">0.0</text>
<text x="52" y="18" fill="#3e5278" font-size="8">m/s²</text>
</g>
<g transform="translate(240,0)">
<text fill="#3e5278" font-size="8" letter-spacing="1">TORQUE</text>
<text id="svg-torque" y="18" fill="#00d4ff" font-size="16" font-weight="700">0</text>
<text x="38" y="18" fill="#3e5278" font-size="8">Nm</text>
</g>
<g transform="translate(350,0)">
<text fill="#3e5278" font-size="8" letter-spacing="1">REGEN</text>
<text id="svg-regen" y="18" fill="#00ff88" font-size="16" font-weight="700">0</text>
<text x="44" y="18" fill="#3e5278" font-size="8">W</text>
</g>
<g transform="translate(460,0)">
<text fill="#3e5278" font-size="8" letter-spacing="1">MOTOR TEMP</text>
<text id="svg-temp" y="18" fill="#ffd600" font-size="16" font-weight="700">25</text>
<text x="42" y="18" fill="#3e5278" font-size="8">°C</text>
</g>
<g transform="translate(590,0)">
<text fill="#3e5278" font-size="8" letter-spacing="1">FORK</text>
<text id="svg-fork" y="18" fill="#ff6b2b" font-size="16" font-weight="700">0</text>
<text x="38" y="18" fill="#3e5278" font-size="8">mm</text>
</g>
</g>
</svg>
</div>
<div id="controls">
<div class="ctrl-section">
<span class="ctrl-label">驱动</span>
<button class="phase-btn active" data-phase="standby"><i class="fas fa-power-off"></i>待机</button>
<button class="phase-btn" data-phase="accelerate"><i class="fas fa-bolt"></i>起步加速</button>
<button class="phase-btn" data-phase="cruise"><i class="fas fa-gauge-high"></i>稳速行驶</button>
<button class="phase-btn" data-phase="brake"><i class="fas fa-rotate-left"></i>再生制动</button>
<button class="phase-btn" data-phase="stop"><i class="fas fa-stop"></i>抱刹停止</button>
</div>
<div class="sep"></div>
<div class="ctrl-section">
<span class="ctrl-label">货叉</span>
<button class="fork-btn" id="fork-extend-btn"><i class="fas fa-arrows-left-right"></i>伸出货叉</button>
<button class="fork-btn" id="fork-retract-btn"><i class="fas fa-compress"></i>缩回货叉</button>
<div class="slider-group">
<label>伸缩量</label>
<input type="range" id="fork-slider" min="0" max="150" step="5" value="0"/>
<span class="slider-val" id="fork-val">0 mm</span>
</div>
</div>
<div class="sep"></div>
<div class="ctrl-section">
<span class="ctrl-label">参数</span>
<div class="slider-group">
<label>目标速度</label>
<input type="range" id="speed-slider" min="1" max="7" step="0.5" value="5"/>
<span class="slider-val" id="speed-val">5.0 m/s</span>
</div>
<div class="slider-group">
<label>轨道摩擦</label>
<input type="range" id="friction-slider" min="0.3" max="1" step="0.1" value="0.8"/>
<span class="slider-val" id="friction-val">0.8</span>
</div>
</div>
</div>
<script>
const SVG_NS = 'http://www.w3.org/2000/svg';
// 轮毂电机参数(主视图后轮中心390,638 / 半径32 / 内部各层)
const HUB = { cx: 390, cy: 638, rTire: 32, rRim: 26, rRotor: 22, rGap: 17, rStator: 14, rShaft: 5 };
const state = {
phase: 'standby', speed: 0, targetSpeed: 5, friction: 0.8,
acceleration: 0, torque: 0, regenPower: 0, motorTemp: 25,
trackOffset: 0, wheelAngle: 0, time: 0, phaseTime: 0,
powerFlowOffset: 0, emFieldAngle: 0,
forkExtension: 0, forkTarget: 0, forkSpeed: 0, forkMoving: false, forkDirection: 0,
};
const FORK_MAX = 150, FORK_SPEED = 80, FORK_SCALE = 1.0;
function initDynamicElements() {
// 轨道枕木
const tiesGroup = document.getElementById('track-ties');
for (let i = 0; i < 40; i++) {
const r = document.createElementNS(SVG_NS, 'rect');
r.setAttribute('x', i * 50); r.setAttribute('y', '664');
r.setAttribute('width', '20'); r.setAttribute('height', '12');
r.setAttribute('rx', '1'); r.setAttribute('fill', '#0e1830');
r.setAttribute('stroke', '#1a2a44'); r.setAttribute('stroke-width', '0.4');
r.setAttribute('class', 'track-tie');
tiesGroup.appendChild(r);
}
// 轮毂电机磁钢
const rotorG = document.getElementById('rotor-magnets');
for (let i = 0; i < 12; i++) {
const a = (i * 30) * Math.PI / 180;
const x = HUB.cx + Math.cos(a) * HUB.rRotor;
const y = HUB.cy + Math.sin(a) * HUB.rRotor;
const r = document.createElementNS(SVG_NS, 'rect');
r.setAttribute('x', x - 3); r.setAttribute('y', y - 2.5);
r.setAttribute('width', '6'); r.setAttribute('height', '5');
r.setAttribute('rx', '0.8');
r.setAttribute('fill', i % 2 === 0 ? '#ff3d5a' : '#3d5aff');
r.setAttribute('opacity', '0.5');
r.setAttribute('transform', `rotate(${i * 30}, ${x}, ${y})`);
rotorG.appendChild(r);
}
// 定子线圈
const statorG = document.getElementById('stator-coils');
for (let i = 0; i < 9; i++) {
const a = (i * 40) * Math.PI / 180;
const x = HUB.cx + Math.cos(a) * (HUB.rStator - 2);
const y = HUB.cy + Math.sin(a) * (HUB.rStator - 2);
const c = document.createElementNS(SVG_NS, 'circle');
c.setAttribute('cx', x); c.setAttribute('cy', y); c.setAttribute('r', '2.5');
c.setAttribute('fill', '#0a1428'); c.setAttribute('stroke', '#00d4ff');
c.setAttribute('stroke-width', '0.6'); c.setAttribute('opacity', '0.4');
c.setAttribute('class', 'stator-coil');
statorG.appendChild(c);
}
// 剖面图磁钢(φ160对应r=80,转子环r=60)
const detailMag = document.getElementById('detail-magnets');
for (let i = 0; i < 20; i++) {
const a = (i * 18) * Math.PI / 180;
const x = Math.cos(a) * 60; const y = Math.sin(a) * 60;
const r = document.createElementNS(SVG_NS, 'rect');
r.setAttribute('x', x - 4); r.setAttribute('y', y - 2.8);
r.setAttribute('width', '8'); r.setAttribute('height', '5.5');
r.setAttribute('rx', '0.8');
r.setAttribute('fill', i % 2 === 0 ? '#ff3d5a' : '#3d5aff');
r.setAttribute('opacity', '0.45');
r.setAttribute('transform', `rotate(${i * 18}, ${x}, ${y})`);
detailMag.appendChild(r);
}
// 剖面图定子
const detailStator = document.getElementById('detail-stator');
for (let i = 0; i < 12; i++) {
const g = document.createElementNS(SVG_NS, 'g');
g.setAttribute('transform', `rotate(${i * 30})`);
const t = document.createElementNS(SVG_NS, 'rect');
t.setAttribute('x', '-2.5'); t.setAttribute('y', '-42');
t.setAttribute('width', '5'); t.setAttribute('height', '11');
t.setAttribute('rx', '0.8'); t.setAttribute('fill', '#1a2a44');
g.appendChild(t);
const c = document.createElementNS(SVG_NS, 'rect');
c.setAttribute('x', '-5'); c.setAttribute('y', '-39');
c.setAttribute('width', '10'); c.setAttribute('height', '6');
c.setAttribute('rx', '1.5'); c.setAttribute('fill', 'none');
c.setAttribute('stroke', '#00d4ff'); c.setAttribute('stroke-width', '0.6');
c.setAttribute('opacity', '0.3'); c.setAttribute('class', 'detail-coil');
g.appendChild(c);
detailStator.appendChild(g);
}
// 速度线
const speedG = document.getElementById('speed-lines');
for (let i = 0; i < 20; i++) {
const l = document.createElementNS(SVG_NS, 'line');
l.setAttribute('x1', '0'); l.setAttribute('y1', String(380 + Math.random() * 280));
l.setAttribute('x2', String(30 + Math.random() * 80));
l.setAttribute('y2', String(380 + Math.random() * 280));
l.setAttribute('stroke', '#00d4ff'); l.setAttribute('stroke-width', '1');
l.setAttribute('opacity', '0'); l.setAttribute('class', 'speed-line');
speedG.appendChild(l);
}
}
function setPhase(p) {
state.phase = p; state.phaseTime = 0;
document.querySelectorAll('.phase-btn').forEach(b => b.classList.toggle('active', b.dataset.phase === p));
const n = {standby:'STANDBY',accelerate:'ACCELERATE',cruise:'CRUISE',brake:'REGEN BRAKE',stop:'FULL STOP'};
const c = {standby:'#3e5278',accelerate:'#00d4ff',cruise:'#ffd600',brake:'#00ff88',stop:'#ff3d5a'};
document.getElementById('phase-name').textContent = n[p];
document.getElementById('phase-name').setAttribute('fill', c[p]);
}
function extendFork() {
state.forkTarget = FORK_MAX; state.forkDirection = 1; state.forkMoving = true;
document.getElementById('fork-extend-btn').classList.add('extending');
document.getElementById('fork-retract-btn').classList.remove('retracting');
}
function retractFork() {
state.forkTarget = 0; state.forkDirection = -1; state.forkMoving = true;
document.getElementById('fork-retract-btn').classList.add('retracting');
document.getElementById('fork-extend-btn').classList.remove('extending');
}
function setForkTarget(v) {
state.forkTarget = v;
state.forkDirection = v > state.forkExtension ? 1 : (v < state.forkExtension ? -1 : 0);
state.forkMoving = Math.abs(v - state.forkExtension) > 0.5;
}
function updateState(dt) {
state.time += dt; state.phaseTime += dt;
switch (state.phase) {
case 'standby':
state.speed = Math.max(0, state.speed - 3 * dt);
state.torque = 0; state.acceleration = state.speed > 0.01 ? -3 : 0; state.regenPower = 0;
break;
case 'accelerate': {
const ma = 2.5 * state.friction;
state.acceleration = ma;
state.speed = Math.min(state.targetSpeed, state.speed + ma * dt);
state.torque = 100 * (1 - state.speed / state.targetSpeed * 0.4) * state.friction;
state.regenPower = 0;
if (state.speed >= state.targetSpeed * 0.98) setPhase('cruise');
break;
}
case 'cruise':
state.speed = state.targetSpeed; state.acceleration = 0; state.torque = 15; state.regenPower = 0;
break;
case 'brake': {
const ba = -4 * state.friction;
state.acceleration = ba;
state.speed = Math.max(0, state.speed + ba * dt);
state.torque = -80 * state.friction;
state.regenPower = Math.abs(state.speed * state.torque * 0.08);
state.motorTemp = Math.min(135, state.motorTemp + 20 * dt);
if (state.speed <= 0.05) setPhase('stop');
break;
}
case 'stop':
state.speed = 0; state.acceleration = 0; state.torque = 0; state.regenPower = 0;
break;
}
if (state.phase !== 'brake') state.motorTemp = Math.max(25, state.motorTemp - 3 * dt);
// 货叉伸缩
if (state.forkMoving) {
const diff = state.forkTarget - state.forkExtension;
const step = FORK_SPEED * dt;
if (Math.abs(diff) <= step) {
state.forkExtension = state.forkTarget; state.forkMoving = false;
state.forkDirection = 0; state.forkSpeed = 0;
document.getElementById('fork-extend-btn').classList.remove('extending');
document.getElementById('fork-retract-btn').classList.remove('retracting');
} else {
state.forkExtension += Math.sign(diff) * step;
state.forkSpeed = Math.sign(diff) * FORK_SPEED;
}
} else { state.forkSpeed = 0; }
state.trackOffset = (state.trackOffset + state.speed * 25 * dt) % 50;
state.wheelAngle += state.speed * 3 * dt;
state.powerFlowOffset = (state.powerFlowOffset + state.speed * 80 * dt) % 40;
state.emFieldAngle += (state.speed * 2 + (state.phase === 'accelerate' ? 3 : 0)) * dt;
}
function render() {
const isAccel = state.phase === 'accelerate';
const isCruise = state.phase === 'cruise';
const isBrake = state.phase === 'brake';
const isStop = state.phase === 'stop';
const isActive = isAccel || isCruise;
const sr = state.speed / 7;
const ext = state.forkExtension;
const isFM = state.forkMoving;
// 枕木
document.querySelectorAll('.track-tie').forEach((t, i) => {
t.setAttribute('x', String((i * 50 - state.trackOffset + 920) % 2000 - 50));
});
// 轮子旋转
const wa = state.wheelAngle * 180 / Math.PI;
document.getElementById('spokes-rear').setAttribute('transform', `rotate(${wa}, ${HUB.cx}, ${HUB.cy})`);
document.getElementById('rotor-magnets').setAttribute('transform', `rotate(${wa * 0.95}, ${HUB.cx}, ${HUB.cy})`);
document.getElementById('spokes-front').setAttribute('transform', `rotate(${wa}, 670, 644)`);
// 定子线圈
document.querySelectorAll('.stator-coil').forEach((c, i) => {
const ph = (state.emFieldAngle * 3 + i * 0.7) % (Math.PI * 2);
c.setAttribute('opacity', String(isActive ? 0.3 + 0.7 * Math.max(0, Math.sin(ph)) : 0.15));
c.setAttribute('stroke', isBrake ? '#00ff88' : '#00d4ff');
});
document.querySelectorAll('.detail-coil').forEach((c, i) => {
const ph = (state.emFieldAngle * 3 + i * 0.5) % (Math.PI * 2);
c.setAttribute('opacity', String(isActive || isBrake ? 0.2 + 0.8 * Math.max(0, Math.sin(ph)) : 0.1));
c.setAttribute('stroke', isBrake ? '#00ff88' : '#00d4ff');
});
// 电力路径
['power-path-cm1','power-path-cm2','power-path-bc'].forEach(id => {
const el = document.getElementById(id);
if (isActive) {
el.setAttribute('stroke', '#00d4ff'); el.setAttribute('stroke-dashoffset', String(-state.powerFlowOffset));
el.setAttribute('opacity', String(0.4 + sr * 0.5));
} else if (isBrake) {
el.setAttribute('stroke', '#00ff88'); el.setAttribute('stroke-dashoffset', String(state.powerFlowOffset));
el.setAttribute('opacity', String(0.3 + sr * 0.4));
} else { el.setAttribute('stroke', '#2a4060'); el.setAttribute('opacity', '0.25'); }
});
// 力箭头
document.getElementById('drive-force').setAttribute('opacity', String(isActive ? Math.min(1, Math.abs(state.torque) / 60) : 0));
document.getElementById('regen-force').setAttribute('opacity', String(isBrake ? Math.min(1, Math.abs(state.torque) / 60) : 0));
document.getElementById('torque-arc').setAttribute('opacity', String(isActive ? 0.8 : 0));
document.getElementById('regen-arc').setAttribute('opacity', String(isBrake ? 0.8 : 0));
// LED
const led = document.getElementById('ctrl-led');
if (isActive) { led.setAttribute('fill', '#00d4ff'); led.setAttribute('filter', 'url(#softglow)'); }
else if (isBrake) { led.setAttribute('fill', '#00ff88'); led.setAttribute('filter', 'url(#softglow)'); }
else if (isStop) { led.setAttribute('fill', '#ff3d5a'); led.removeAttribute('filter'); }
else { led.setAttribute('fill', '#1a2a40'); led.removeAttribute('filter'); }
// 电池
const bl = isBrake ? Math.min(1, 0.5 + state.regenPower / 80) : Math.max(0.2, 0.8 - sr * 0.3);
document.getElementById('bat-bar').setAttribute('height', String(14 * bl));
document.getElementById('bat-bar').setAttribute('fill', isBrake ? '#00ff88' : '#1a4a2a');
// 俯视图差速
const dl = document.getElementById('diff-line-l'), dr = document.getElementById('diff-line-r');
if (isActive || isBrake) {
const col = isBrake ? '#00ff88' : '#00d4ff';
dl.setAttribute('stroke', col); dr.setAttribute('stroke', col);
dl.setAttribute('stroke-dashoffset', String(-state.powerFlowOffset));
dr.setAttribute('stroke-dashoffset', String(-state.powerFlowOffset));
dl.setAttribute('opacity', '0.8'); dr.setAttribute('opacity', '0.8');
} else { dl.setAttribute('stroke', '#2a4060'); dr.setAttribute('stroke', '#2a4060'); dl.setAttribute('opacity', '0.4'); dr.setAttribute('opacity', '0.4'); }
document.getElementById('sync-indicator').setAttribute('opacity', String(isActive || isCruise ? 0.9 : 0));
document.getElementById('direction-arrow').setAttribute('opacity', String(state.speed > 0.1 ? 0.8 : 0));
// 速度线
document.querySelectorAll('.speed-line').forEach((l, i) => {
if (state.speed > 1) {
const al = Math.min(0.5, (state.speed - 1) / 6) * (0.3 + Math.random() * 0.7);
l.setAttribute('opacity', String(al)); l.setAttribute('stroke', isBrake ? '#00ff88' : '#00d4ff');
const y = 390 + ((i * 47 + state.time * 200) % 260);
const len = 30 + state.speed * 15 + Math.random() * 40;
l.setAttribute('y1', String(y)); l.setAttribute('y2', String(y));
l.setAttribute('x1', String(-len - Math.random() * 200)); l.setAttribute('x2', String(-Math.random() * 200));
} else { l.setAttribute('opacity', '0'); }
});
// 轮毂发光
const hg = document.getElementById('hub-motor-group');
if (isActive) hg.setAttribute('filter', 'url(#glow-cyan)');
else if (isBrake) hg.setAttribute('filter', 'url(#glow-green)');
else hg.removeAttribute('filter');
document.getElementById('no-shaft-label').setAttribute('opacity', String(0.35 + sr * 0.35));
['top-left-motor','top-right-motor'].forEach(id => {
const g = document.getElementById(id);
const c = g.querySelector('circle:nth-child(2)');
if (isActive) { c.setAttribute('stroke', '#00d4ff'); c.setAttribute('stroke-width', '2'); }
else if (isBrake) { c.setAttribute('stroke', '#00ff88'); c.setAttribute('stroke-width', '2'); }
else { c.setAttribute('stroke', '#1e3050'); c.setAttribute('stroke-width', '1'); }
});
// ===== 货叉伸缩渲染 =====
document.getElementById('fork-upper').setAttribute('transform', `translate(${ext}, 0)`);
document.getElementById('fork-lower').setAttribute('transform', `translate(${ext}, 0)`);
document.getElementById('fork-verticals').setAttribute('transform', `translate(${ext}, 0)`);
const te = ext * 0.6;
const ftu = document.getElementById('fork-top-upper');
const ftl = document.getElementById('fork-top-lower');
ftu.setAttribute('x', String(310 + te)); ftu.setAttribute('width', String(100 + te * 0.3));
ftl.setAttribute('x', String(310 + te)); ftl.setAttribute('width', String(100 + te * 0.3));
const tipX = 310 + te + 100 + te * 0.3;
document.getElementById('fork-top-tip-u').setAttribute('d', `M${tipX},80 L${tipX+15},83 L${tipX+15},91 L${tipX},94Z`);
document.getElementById('fork-top-tip-l').setAttribute('d', `M${tipX},186 L${tipX+15},189 L${tipX+15},197 L${tipX},200Z`);
// 导轨发光
const rg = document.getElementById('rail-glow');
rg.setAttribute('opacity', String(isFM ? (0.5 + 0.3 * Math.sin(state.time * 8)) : (ext > 0 ? 0.15 : 0)));
if (isFM) {
const gc = state.forkDirection > 0 ? '#ff6b2b' : '#00ff88';
document.getElementById('rail-glow-grad').querySelectorAll('stop').forEach((s, i) => {
if (i === 1 || i === 2) s.setAttribute('stop-color', gc);
});
}
// 推杆
const rod = document.getElementById('fork-actuator-rod');
rod.setAttribute('x', String(730 + Math.min(18, ext * 0.2)));
rod.setAttribute('width', String(Math.max(5, 20 - ext * 0.1)));
document.getElementById('fork-actuator').setAttribute('opacity', String(isFM ? 0.9 : 0.4));
// 位移标注
const fd = document.getElementById('fork-dimension');
const showD = ext > 2 || isFM;
fd.setAttribute('opacity', String(showD ? 0.9 : 0));
if (showD) {
const sx = 690, ex = 690 + ext;
document.getElementById('fork-dim-end').setAttribute('x1', String(ex));
document.getElementById('fork-dim-end').setAttribute('x2', String(ex));
document.getElementById('fork-dim-line').setAttribute('x1', String(sx));
document.getElementById('fork-dim-line').setAttribute('x2', String(ex));
document.getElementById('fork-dim-text').setAttribute('x', String((sx + ex) / 2));
document.getElementById('fork-dim-text').textContent = Math.round(ext * FORK_SCALE) + 'mm';
document.getElementById('fork-dim-text').setAttribute('fill', state.forkDirection > 0 ? '#ff6b2b' : (state.forkDirection < 0 ? '#00ff88' : '#ff6b2b'));
}
// 货叉标签
const fl = document.getElementById('fork-label-text');
fl.setAttribute('x', String(770 + ext * 0.5));
if (isFM) { fl.setAttribute('fill', state.forkDirection > 0 ? '#ff6b2b' : '#00ff88'); fl.textContent = state.forkDirection > 0 ? 'EXTENDING...' : 'RETRACTING...'; }
else { fl.setAttribute('fill', ext > 0 ? '#2a4060' : '#1e3050'); fl.textContent = 'CARBON FORK'; }
// 叉尖闪烁
if (isFM) {
const bk = 0.5 + 0.5 * Math.sin(state.time * 12);
const fc = state.forkDirection > 0 ? `rgba(255,107,43,${bk * 0.3})` : `rgba(0,255,136,${bk * 0.3})`;
const sc = state.forkDirection > 0 ? '#ff6b2b' : '#00ff88';
document.getElementById('fork-upper').querySelector('path').setAttribute('fill', fc);
document.getElementById('fork-upper').querySelector('path').setAttribute('stroke', sc);
document.getElementById('fork-lower').querySelector('path').setAttribute('fill', fc);
document.getElementById('fork-lower').querySelector('path').setAttribute('stroke', sc);
} else {
document.getElementById('fork-upper').querySelector('path').setAttribute('fill', '#14203a');
document.getElementById('fork-upper').querySelector('path').setAttribute('stroke', '#1e3050');
document.getElementById('fork-lower').querySelector('path').setAttribute('fill', '#14203a');
document.getElementById('fork-lower').querySelector('path').setAttribute('stroke', '#1e3050');
}
// 参数
document.getElementById('svg-speed').textContent = state.speed.toFixed(1);
document.getElementById('svg-accel').textContent = Math.abs(state.acceleration).toFixed(1);
document.getElementById('svg-torque').textContent = Math.abs(Math.round(state.torque));
document.getElementById('svg-regen').textContent = Math.round(state.regenPower);
document.getElementById('svg-temp').textContent = Math.round(state.motorTemp);
document.getElementById('svg-fork').textContent = Math.round(ext * FORK_SCALE);
const tEl = document.getElementById('svg-temp');
tEl.setAttribute('fill', state.motorTemp > 100 ? '#ff3d5a' : state.motorTemp > 70 ? '#ff6b2b' : '#ffd600');
document.getElementById('svg-accel').setAttribute('fill', state.acceleration >= 0 ? '#00d4ff' : '#ff6b2b');
document.getElementById('svg-torque').setAttribute('fill', state.torque >= 0 ? '#00d4ff' : '#00ff88');
document.getElementById('fork-status-val').textContent = Math.round(ext * FORK_SCALE) + ' mm';
document.getElementById('fork-status-val').setAttribute('fill', isFM ? (state.forkDirection > 0 ? '#ff6b2b' : '#00ff88') : (ext > 0 ? '#ff6b2b' : '#3e5278'));
}
let lastTime = 0;
function animate(ts) {
const dt = Math.min(0.05, (ts - lastTime) / 1000);
lastTime = ts;
updateState(dt); render();
requestAnimationFrame(animate);
}
document.querySelectorAll('.phase-btn').forEach(b => b.addEventListener('click', () => setPhase(b.dataset.phase)));
document.getElementById('speed-slider').addEventListener('input', e => {
state.targetSpeed = parseFloat(e.target.value);
document.getElementById('speed-val').textContent = state.targetSpeed.toFixed(1) + ' m/s';
});
document.getElementById('friction-slider').addEventListener('input', e => {
state.friction = parseFloat(e.target.value);
document.getElementById('friction-val').textContent = state.friction.toFixed(1);
});
document.getElementById('fork-extend-btn').addEventListener('click', extendFork);
document.getElementById('fork-retract-btn').addEventListener('click', retractFork);
document.getElementById('fork-slider').addEventListener('input', e => {
const v = parseFloat(e.target.value);
setForkTarget(v);
document.getElementById('fork-val').textContent = Math.round(v * FORK_SCALE) + ' mm';
});
initDynamicElements(); setPhase('standby'); requestAnimationFrame(animate);
</script>
</body>
</html>
轮径调整说明
已将全部轮径缩至 φ160mm(SVG 半径 32px),并同步调整了所有关联元素:
| 调整项 | 旧值 | 新值 |
|---|---|---|
| 后轮(轮毂电机)半径 | 52px | 32px |
| 前轮(从动轮)半径 | 42px | 26px(等比缩小) |
| 后轮中心 Y | 590 | 638(保持轮缘触轨 y=670) |
| 前轮中心 Y | 595 | 644 |
| 底盘桁架 | y=465~540 | y=580~615(下移贴合小轮) |
| 轮毂内部分层(转子/定子/气隙/轴) | 按原比例 | 按32半径等比缩放 |
| 剖面图 | r=100 | r=80(对应φ160mm) |
| 磁钢/线圈/辐条尺寸 | — | 等比缩小 |
| 悬挂连接杆 | 无 | 新增(底盘→轮轴的 V 形连接) |
| 轨道面 Y | 638 | 670(随整车下移) |
标签 "≤4kg · φ160" 标注在轮毂电机下方,剖面图标题也更新为 "φ160mm"。
等待动画代码生成...
