分享图
A
动画渲染工坊
就绪
<!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=Rajdhani:wght@300;500;700&family=JetBrains+Mono:wght@300;400;500&display=swap" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css" rel="stylesheet">
<style>
*,*::before,*::after{margin:0;padding:0;box-sizing:border-box}
:root{
  --bg:#050a14;--bg2:#0b1628;--fg:#e4ddd3;--muted:#4a5c75;
  --accent:#ff6b2b;--cool:#00d4ff;--aerogel:#c9956a;--aerogel-dim:rgba(201,149,106,0.25);
  --solar:#ffd700;--danger:#ff2244;--hud:#00ff88;--cable:#7a8fa5;
}
html{font-size:16px}
body{background:var(--bg);color:var(--fg);font-family:'Rajdhani',sans-serif;min-height:100vh;display:flex;flex-direction:column;align-items:center;overflow-x:hidden}
header{text-align:center;padding:1.5rem 1rem 0.5rem;width:100%}
header h1{font-size:2rem;font-weight:700;letter-spacing:0.06em;background:linear-gradient(135deg,var(--aerogel),var(--accent));-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}
header p{font-size:0.95rem;color:var(--muted);letter-spacing:0.12em;margin-top:0.2rem;font-weight:300}
#scene-wrap{width:100%;max-width:1400px;flex:1;display:flex;justify-content:center;align-items:center;padding:0.5rem}
#scene-wrap svg{width:100%;height:auto;max-height:78vh;display:block}
#controls{width:100%;max-width:1200px;padding:0.8rem 1.5rem 1.5rem;display:flex;flex-wrap:wrap;gap:1rem;align-items:center;justify-content:center}
.ctrl-group{display:flex;align-items:center;gap:0.6rem;background:var(--bg2);border:1px solid rgba(201,149,106,0.15);border-radius:8px;padding:0.5rem 1rem}
.ctrl-group label{font-size:0.8rem;color:var(--muted);letter-spacing:0.08em;white-space:nowrap;font-family:'JetBrains Mono',monospace}
.ctrl-group input[type=range]{-webkit-appearance:none;width:180px;height:4px;background:linear-gradient(90deg,var(--cool),var(--accent));border-radius:2px;outline:none;cursor:pointer}
.ctrl-group input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;width:16px;height:16px;border-radius:50%;background:var(--fg);border:2px solid var(--accent);cursor:grab}
.ctrl-group .val{font-family:'JetBrains Mono',monospace;font-size:0.85rem;color:var(--hud);min-width:48px;text-align:right}
button.ctrl-btn{background:transparent;border:1px solid var(--muted);color:var(--fg);padding:0.45rem 1rem;border-radius:6px;cursor:pointer;font-family:'Rajdhani',sans-serif;font-size:0.85rem;font-weight:500;letter-spacing:0.06em;transition:all .25s}
button.ctrl-btn:hover{border-color:var(--accent);color:var(--accent);box-shadow:0 0 12px rgba(255,107,43,0.2)}
button.ctrl-btn.active{border-color:var(--danger);color:var(--danger);box-shadow:0 0 12px rgba(255,34,68,0.3)}
.toast{position:fixed;top:1.5rem;right:1.5rem;background:var(--bg2);border:1px solid var(--danger);color:var(--danger);padding:0.7rem 1.2rem;border-radius:8px;font-size:0.85rem;font-family:'JetBrains Mono',monospace;opacity:0;transform:translateY(-10px);transition:all .3s;pointer-events:none;z-index:99}
.toast.show{opacity:1;transform:translateY(0)}
@keyframes pulseGlow{0%,100%{opacity:0.6}50%{opacity:1}}
@keyframes dashFlow{to{stroke-dashoffset:-20}}
@media(max-width:768px){
  header h1{font-size:1.4rem}
  .ctrl-group input[type=range]{width:120px}
  #controls{gap:0.5rem}
}
</style>
</head>
<body>
<header>
  <h1>毛细血管式微元浮力块</h1>
  <p>分布式浮力 &middot; 消灭应力集中 &middot; 无限冗余 &mdash; IFR 理想解原理动画</p>
</header>

<div id="scene-wrap">
<svg id="scene" viewBox="0 0 1400 820" xmlns="http://www.w3.org/2000/svg">
  <defs>
    <!-- 天空渐变(动态修改) -->
    <linearGradient id="skyGrad" x1="0" y1="0" x2="0" y2="1">
      <stop id="skyStop1" offset="0%" stop-color="#050a14"/>
      <stop id="skyStop2" offset="60%" stop-color="#0b1628"/>
      <stop id="skyStop3" offset="100%" stop-color="#111d33"/>
    </linearGradient>
    <!-- 太阳能薄膜渐变 -->
    <linearGradient id="solarGrad" x1="0" y1="0" x2="1" y2="0">
      <stop offset="0%" stop-color="#ffd700" stop-opacity="0.7"/>
      <stop offset="50%" stop-color="#ffaa00" stop-opacity="0.9"/>
      <stop offset="100%" stop-color="#ffd700" stop-opacity="0.7"/>
    </linearGradient>
    <!-- 舱壁渐变 -->
    <radialGradient id="cabinGlow" cx="50%" cy="50%" r="50%">
      <stop offset="0%" stop-color="#ff6b2b" stop-opacity="0.5"/>
      <stop offset="100%" stop-color="#ff6b2b" stop-opacity="0"/>
    </radialGradient>
    <radialGradient id="cabinCoolGlow" cx="50%" cy="50%" r="50%">
      <stop offset="0%" stop-color="#00d4ff" stop-opacity="0.4"/>
      <stop offset="100%" stop-color="#00d4ff" stop-opacity="0"/>
    </radialGradient>
    <!-- 蜂窝图案 -->
    <pattern id="hexPat" width="16" height="27.71" patternUnits="userSpaceOnUse" patternTransform="rotate(0)">
      <polygon points="8,0 16,4.62 16,13.86 8,18.48 0,13.86 0,4.62" fill="rgba(201,149,106,0.08)" stroke="rgba(201,149,106,0.3)" stroke-width="0.5"/>
      <polygon points="8,9.24 16,13.86 16,23.09 8,27.71 0,23.09 0,13.86" fill="rgba(201,149,106,0.08)" stroke="rgba(201,149,106,0.3)" stroke-width="0.5"/>
    </pattern>
    <!-- 放大区蜂窝 -->
    <pattern id="hexPatZoom" width="52" height="90.07" patternUnits="userSpaceOnUse">
      <polygon points="26,0 52,15 52,45 26,60 0,45 0,15" fill="rgba(201,149,106,0.06)" stroke="rgba(201,149,106,0.45)" stroke-width="1.2"/>
      <polygon points="26,30.02 52,45.02 52,75.02 26,90.02 0,75.02 0,45.02" fill="rgba(201,149,106,0.06)" stroke="rgba(201,149,106,0.45)" stroke-width="1.2"/>
    </pattern>
    <!-- 张拉索虚线 -->
    <filter id="glowFilter" x="-50%" y="-50%" width="200%" height="200%">
      <feGaussianBlur in="SourceGraphic" stdDeviation="3" result="blur"/>
      <feMerge><feMergeNode in="blur"/><feMergeNode in="SourceGraphic"/></feMerge>
    </filter>
    <filter id="softGlow" x="-50%" y="-50%" width="200%" height="200%">
      <feGaussianBlur in="SourceGraphic" stdDeviation="6" result="blur"/>
      <feMerge><feMergeNode in="blur"/><feMergeNode in="SourceGraphic"/></feMerge>
    </filter>
    <clipPath id="hullClip">
      <path d="M180,410 C180,310 280,230 480,230 C680,230 780,310 780,410 C780,510 680,590 480,590 C280,590 180,510 180,410 Z"/>
    </clipPath>
    <clipPath id="innerClip">
      <path d="M240,410 C240,335 320,275 480,275 C640,275 720,335 720,410 C720,485 640,545 480,545 C320,545 240,485 240,410 Z"/>
    </clipPath>
    <!-- 裁剪环壳区域 -->
    <clipPath id="shellClip">
      <path d="M180,410 C180,310 280,230 480,230 C680,230 780,310 780,410 C780,510 680,590 480,590 C280,590 180,510 180,410 Z M240,410 C240,335 320,275 480,275 C640,275 720,335 720,410 C720,485 640,545 480,545 C320,545 240,485 240,410 Z" fill-rule="evenodd"/>
    </clipPath>
  </defs>

  <!-- ====== 天空背景 ====== -->
  <rect id="skyRect" x="0" y="0" width="1400" height="820" fill="url(#skyGrad)"/>
  <!-- 星星 -->
  <g id="stars" opacity="0.8">
    <circle cx="120" cy="60" r="1.2" fill="#fff"/><circle cx="340" cy="40" r="0.8" fill="#fff"/>
    <circle cx="560" cy="80" r="1" fill="#fff"/><circle cx="780" cy="30" r="1.3" fill="#fff"/>
    <circle cx="1020" cy="70" r="0.9" fill="#fff"/><circle cx="1250" cy="50" r="1.1" fill="#fff"/>
    <circle cx="200" cy="130" r="0.7" fill="#fff"/><circle cx="900" cy="110" r="1" fill="#fff"/>
    <circle cx="1100" cy="140" r="0.8" fill="#fff"/><circle cx="50" cy="100" r="0.9" fill="#fff"/>
    <circle cx="650" cy="150" r="0.6" fill="#fff"/><circle cx="1350" cy="90" r="1" fill="#fff"/>
  </g>
  <!-- 太阳/月亮 -->
  <g id="celestial">
    <circle id="sun" cx="480" cy="100" r="40" fill="#ffd700" opacity="0" filter="url(#softGlow)"/>
    <circle id="moon" cx="480" cy="120" r="25" fill="#c0c8d8" opacity="0.6"/>
  </g>

  <!-- ====== 高度标尺 ====== -->
  <g id="altScale" transform="translate(60, 200)">
    <line x1="0" y1="0" x2="0" y2="500" stroke="rgba(201,149,106,0.2)" stroke-width="1"/>
    <text x="-5" y="5" fill="var(--muted)" font-size="10" font-family="JetBrains Mono" text-anchor="end">20km 平流层</text>
    <line x1="-8" y1="0" x2="8" y2="0" stroke="var(--muted)" stroke-width="0.8"/>
    <text x="-5" y="250" fill="var(--muted)" font-size="10" font-family="JetBrains Mono" text-anchor="end">10km 对流层顶</text>
    <line x1="-8" y1="250" x2="8" y2="250" stroke="var(--muted)" stroke-width="0.8"/>
    <text x="-5" y="500" fill="var(--muted)" font-size="10" font-family="JetBrains Mono" text-anchor="end">0km 地面</text>
    <line x1="-8" y1="500" x2="8" y2="500" stroke="var(--muted)" stroke-width="0.8"/>
    <!-- 高度指示三角 -->
    <polygon id="altPointer" points="-12,395 0,390 0,400" fill="var(--hud)" opacity="0.9"/>
  </g>

  <!-- ====== 飞艇主体 ====== -->
  <g id="airshipGroup">
    <!-- 外壳(蜂窝图案填充) -->
    <g clip-path="url(#shellClip)">
      <rect x="170" y="220" width="620" height="380" fill="url(#hexPat)"/>
      <!-- 受损区域遮罩 -->
      <g id="damageOverlay" opacity="0"></g>
    </g>
    <!-- 外壳轮廓 -->
    <path d="M180,410 C180,310 280,230 480,230 C680,230 780,310 780,410 C780,510 680,590 480,590 C280,590 180,510 180,410 Z" fill="none" stroke="rgba(201,149,106,0.5)" stroke-width="1.5"/>
    <!-- 内腔轮廓 -->
    <path d="M240,410 C240,335 320,275 480,275 C640,275 720,335 720,410 C720,485 640,545 480,545 C320,545 240,485 240,410 Z" fill="rgba(5,10,20,0.7)" stroke="rgba(201,149,106,0.2)" stroke-width="1" stroke-dasharray="6,4"/>

    <!-- 太阳能薄膜(顶部弧线) -->
    <path id="solarFilm" d="M200,395 C200,320 290,250 480,250 C670,250 760,320 760,395" fill="none" stroke="url(#solarGrad)" stroke-width="6" stroke-linecap="round" opacity="0.5"/>
    <!-- 太阳能光子粒子 -->
    <g id="solarParticles"></g>

    <!-- 内腔气体发光效果 -->
    <ellipse id="innerGlow" cx="480" cy="410" rx="220" ry="120" fill="url(#cabinGlow)" opacity="0.3"/>

    <!-- 热泵标识 -->
    <g id="heatPump" transform="translate(480,410)">
      <circle r="22" fill="rgba(5,10,20,0.8)" stroke="var(--accent)" stroke-width="1.5" opacity="0.7"/>
      <text y="1" text-anchor="middle" fill="var(--accent)" font-size="11" font-family="JetBrains Mono" font-weight="500">HP</text>
      <!-- 旋转指示环 -->
      <circle id="hpRing" r="28" fill="none" stroke="var(--accent)" stroke-width="1" stroke-dasharray="8,6" opacity="0.5">
        <animateTransform attributeName="transform" type="rotate" from="0" to="360" dur="4s" repeatCount="indefinite"/>
      </circle>
    </g>

    <!-- 能量流粒子(太阳能→热泵→舱壁) -->
    <g id="energyFlows"></g>

    <!-- 张拉索 -->
    <g id="tensionCables">
      <line x1="350" y1="540" x2="380" y2="660" stroke="var(--cable)" stroke-width="1.2" stroke-dasharray="8,4" opacity="0.6">
        <animate attributeName="stroke-dashoffset" from="0" to="-24" dur="1.5s" repeatCount="indefinite"/>
      </line>
      <line x1="420" y1="548" x2="430" y2="660" stroke="var(--cable)" stroke-width="1.2" stroke-dasharray="8,4" opacity="0.6">
        <animate attributeName="stroke-dashoffset" from="0" to="-24" dur="1.5s" repeatCount="indefinite"/>
      </line>
      <line x1="540" y1="548" x2="530" y2="660" stroke="var(--cable)" stroke-width="1.2" stroke-dasharray="8,4" opacity="0.6">
        <animate attributeName="stroke-dashoffset" from="0" to="-24" dur="1.5s" repeatCount="indefinite"/>
      </line>
      <line x1="610" y1="540" x2="580" y2="660" stroke="var(--cable)" stroke-width="1.2" stroke-dasharray="8,4" opacity="0.6">
        <animate attributeName="stroke-dashoffset" from="0" to="-24" dur="1.5s" repeatCount="indefinite"/>
      </line>
    </g>

    <!-- 核心甲板/货舱 -->
    <g id="cargoDeck">
      <rect x="360" y="660" width="240" height="40" rx="4" fill="rgba(20,30,50,0.9)" stroke="var(--cable)" stroke-width="1.5"/>
      <text x="480" y="685" text-anchor="middle" fill="var(--muted)" font-size="11" font-family="JetBrains Mono">CARGO DECK</text>
      <!-- 预紧力标注 -->
      <text x="330" y="670" fill="var(--cool)" font-size="9" font-family="JetBrains Mono" opacity="0.7">F=500kN</text>
    </g>
  </g>

  <!-- ====== 放大镜:微舱细节 ====== -->
  <g id="zoomInset" transform="translate(950, 60)">
    <!-- 背景框 -->
    <rect x="-10" y="-10" width="400" height="340" rx="12" fill="rgba(5,10,20,0.85)" stroke="rgba(201,149,106,0.25)" stroke-width="1"/>
    <text x="190" y="18" text-anchor="middle" fill="var(--aerogel)" font-size="13" font-family="Rajdhani" font-weight="700" letter-spacing="0.1em">微舱结构放大视图</text>
    <line x1="10" y1="28" x2="370" y2="28" stroke="rgba(201,149,106,0.15)" stroke-width="0.5"/>

    <!-- 7个六边形微舱 -->
    <g id="zoomCabins" transform="translate(190, 185)">
      <!-- 中心舱 -->
      <g class="zcabin" data-idx="0" transform="translate(0,0)">
        <polygon points="0,-44 38.1,-22 38.1,22 0,44 -38.1,22 -38.1,-22" fill="rgba(201,149,106,0.06)" stroke="rgba(201,149,106,0.6)" stroke-width="1.5"/>
        <circle class="zgas" r="18" fill="rgba(255,107,43,0.3)"/>
        <text y="3" text-anchor="middle" fill="var(--muted)" font-size="8" font-family="JetBrains Mono">H₂</text>
        <!-- 阀门 -->
        <rect class="zvalve" x="-3" y="-48" width="6" height="6" rx="1" fill="var(--cool)" opacity="0.5"/>
      </g>
      <!-- 上舱 -->
      <g class="zcabin" data-idx="1" transform="translate(0,-76.2)">
        <polygon points="0,-44 38.1,-22 38.1,22 0,44 -38.1,22 -38.1,-22" fill="rgba(201,149,106,0.06)" stroke="rgba(201,149,106,0.6)" stroke-width="1.5"/>
        <circle class="zgas" r="18" fill="rgba(255,107,43,0.3)"/>
        <text y="3" text-anchor="middle" fill="var(--muted)" font-size="8" font-family="JetBrains Mono">H₂</text>
        <rect class="zvalve" x="-3" y="-48" width="6" height="6" rx="1" fill="var(--cool)" opacity="0.5"/>
      </g>
      <!-- 右上舱 -->
      <g class="zcabin" data-idx="2" transform="translate(66,-38.1)">
        <polygon points="0,-44 38.1,-22 38.1,22 0,44 -38.1,22 -38.1,-22" fill="rgba(201,149,106,0.06)" stroke="rgba(201,149,106,0.6)" stroke-width="1.5"/>
        <circle class="zgas" r="18" fill="rgba(255,107,43,0.3)"/>
        <text y="3" text-anchor="middle" fill="var(--muted)" font-size="8" font-family="JetBrains Mono">H₂</text>
        <rect class="zvalve" x="-3" y="-48" width="6" height="6" rx="1" fill="var(--cool)" opacity="0.5"/>
      </g>
      <!-- 右下舱 -->
      <g class="zcabin" data-idx="3" transform="translate(66,38.1)">
        <polygon points="0,-44 38.1,-22 38.1,22 0,44 -38.1,22 -38.1,-22" fill="rgba(201,149,106,0.06)" stroke="rgba(201,149,106,0.6)" stroke-width="1.5"/>
        <circle class="zgas" r="18" fill="rgba(255,107,43,0.3)"/>
        <text y="3" text-anchor="middle" fill="var(--muted)" font-size="8" font-family="JetBrains Mono">H₂</text>
        <rect class="zvalve" x="-3" y="-48" width="6" height="6" rx="1" fill="var(--cool)" opacity="0.5"/>
      </g>
      <!-- 下舱 -->
      <g class="zcabin" data-idx="4" transform="translate(0,76.2)">
        <polygon points="0,-44 38.1,-22 38.1,22 0,44 -38.1,22 -38.1,-22" fill="rgba(201,149,106,0.06)" stroke="rgba(201,149,106,0.6)" stroke-width="1.5"/>
        <circle class="zgas" r="18" fill="rgba(255,107,43,0.3)"/>
        <text y="3" text-anchor="middle" fill="var(--muted)" font-size="8" font-family="JetBrains Mono">H₂</text>
        <rect class="zvalve" x="-3" y="-48" width="6" height="6" rx="1" fill="var(--cool)" opacity="0.5"/>
      </g>
      <!-- 左下舱 -->
      <g class="zcabin" data-idx="5" transform="translate(-66,38.1)">
        <polygon points="0,-44 38.1,-22 38.1,22 0,44 -38.1,22 -38.1,-22" fill="rgba(201,149,106,0.06)" stroke="rgba(201,149,106,0.6)" stroke-width="1.5"/>
        <circle class="zgas" r="18" fill="rgba(255,107,43,0.3)"/>
        <text y="3" text-anchor="middle" fill="var(--muted)" font-size="8" font-family="JetBrains Mono">H₂</text>
        <rect class="zvalve" x="-3" y="-48" width="6" height="6" rx="1" fill="var(--cool)" opacity="0.5"/>
      </g>
      <!-- 左上舱 -->
      <g class="zcabin" data-idx="6" transform="translate(-66,-38.1)">
        <polygon points="0,-44 38.1,-22 38.1,22 0,44 -38.1,22 -38.1,-22" fill="rgba(201,149,106,0.06)" stroke="rgba(201,149,106,0.6)" stroke-width="1.5"/>
        <circle class="zgas" r="18" fill="rgba(255,107,43,0.3)"/>
        <text y="3" text-anchor="middle" fill="var(--muted)" font-size="8" font-family="JetBrains Mono">H₂</text>
        <rect class="zvalve" x="-3" y="-48" width="6" height="6" rx="1" fill="var(--cool)" opacity="0.5"/>
      </g>
    </g>

    <!-- 参数标注 -->
    <g transform="translate(10, 290)" font-family="JetBrains Mono" font-size="10">
      <text fill="var(--muted)">舱壁厚度</text>
      <text x="100" fill="var(--hud)">0.5mm</text>
      <text x="170" fill="var(--muted)">抗压</text>
      <text x="220" fill="var(--hud)">1.2atm</text>
    </g>

    <!-- 连接线指向主图 -->
    <line x1="0" y1="170" x2="-90" y2="370" stroke="rgba(201,149,106,0.2)" stroke-width="0.8" stroke-dasharray="4,3"/>
  </g>

  <!-- ====== HUD 数据面板 ====== -->
  <g id="hudPanel" transform="translate(950, 430)">
    <rect x="-10" y="-10" width="400" height="310" rx="10" fill="rgba(5,10,20,0.8)" stroke="rgba(0,255,136,0.15)" stroke-width="1"/>
    <text x="190" y="16" text-anchor="middle" fill="var(--hud)" font-size="13" font-family="Rajdhani" font-weight="700" letter-spacing="0.12em" opacity="0.8">SYSTEM STATUS</text>
    <line x1="10" y1="26" x2="370" y2="26" stroke="rgba(0,255,136,0.1)" stroke-width="0.5"/>

    <!-- 数据行 -->
    <g font-family="JetBrains Mono" font-size="12">
      <text x="20" y="56" fill="var(--muted)">时间</text>
      <text id="hudTime" x="370" y="56" fill="var(--fg)" text-anchor="end">12:00</text>

      <text x="20" y="86" fill="var(--muted)">海拔高度</text>
      <text id="hudAlt" x="370" y="86" fill="var(--hud)" text-anchor="end">20,000 m</text>

      <text x="20" y="116" fill="var(--muted)">总浮力</text>
      <text id="hudBuoyancy" x="370" y="116" fill="var(--accent)" text-anchor="end">100.0%</text>

      <text x="20" y="146" fill="var(--muted)">微舱温度</text>
      <text id="hudTemp" x="370" y="146" fill="var(--solar)" text-anchor="end">+45°C</text>

      <text x="20" y="176" fill="var(--muted)">活跃微舱数</text>
      <text id="hudCabins" x="370" y="176" fill="var(--aerogel)" text-anchor="end">1.2×10⁹</text>

      <text x="20" y="206" fill="var(--muted)">张拉索预紧力</text>
      <text id="hudTension" x="370" y="206" fill="var(--cool)" text-anchor="end">500 kN</text>

      <text x="20" y="236" fill="var(--muted)">太阳能输出</text>
      <text id="hudSolar" x="370" y="236" fill="var(--solar)" text-anchor="end">850 kW</text>

      <text x="20" y="266" fill="var(--muted)">受损舱数</text>
      <text id="hudDamage" x="370" y="266" fill="var(--danger)" text-anchor="end">0</text>
    </g>

    <!-- 浮力条 -->
    <g transform="translate(20, 280)">
      <rect width="350" height="8" rx="4" fill="rgba(255,255,255,0.05)"/>
      <rect id="buoyancyBar" width="350" height="8" rx="4" fill="var(--accent)" opacity="0.7"/>
    </g>
  </g>

  <!-- ====== IFR 标注箭头与文字 ====== -->
  <g id="ifrAnnotations" font-family="Rajdhani" font-weight="500">
    <!-- 分布式浮力标注 -->
    <g transform="translate(110, 340)" opacity="0.7">
      <line x1="0" y1="0" x2="55" y2="0" stroke="var(--accent)" stroke-width="1" marker-end="none"/>
      <text x="0" y="-8" fill="var(--accent)" font-size="11" letter-spacing="0.05em">分布式浮力壳</text>
      <text x="0" y="16" fill="var(--muted)" font-size="9">无集中弯矩</text>
    </g>
    <!-- 太阳能标注 -->
    <g transform="translate(300, 220)" opacity="0.7">
      <line x1="0" y1="12" x2="0" y2="30" stroke="var(--solar)" stroke-width="1"/>
      <text x="5" y="8" fill="var(--solar)" font-size="11">柔性太阳能薄膜</text>
    </g>
    <!-- 热泵标注 -->
    <g transform="translate(520, 430)" opacity="0.7">
      <line x1="0" y1="-12" x2="0" y2="-28" stroke="var(--accent)" stroke-width="1"/>
      <text x="5" y="-18" fill="var(--accent)" font-size="11">热泵核心</text>
    </g>
  </g>

  <!-- 损伤模拟闪烁覆盖 -->
  <g id="damageFlash" opacity="0">
    <circle r="30" fill="var(--danger)" opacity="0.3" filter="url(#softGlow)"/>
  </g>
</svg>
</div>

<!-- 控制面板 -->
<div id="controls">
  <div class="ctrl-group">
    <label><i class="fas fa-clock"></i> 时刻</label>
    <input type="range" id="timeSlider" min="0" max="24" step="0.1" value="12"/>
    <span class="val" id="timeVal">12:00</span>
  </div>
  <div class="ctrl-group">
    <label><i class="fas fa-temperature-high"></i> 热泵功率</label>
    <input type="range" id="pumpSlider" min="0" max="100" step="1" value="70"/>
    <span class="val" id="pumpVal">70%</span>
  </div>
  <button class="ctrl-btn" id="btnDamage"><i class="fas fa-bolt"></i> 模拟损伤</button>
  <button class="ctrl-btn" id="btnAutoPlay"><i class="fas fa-play"></i> 自动循环</button>
  <button class="ctrl-btn" id="btnReset"><i class="fas fa-undo"></i> 重置</button>
</div>

<!-- 提示Toast -->
<div class="toast" id="toast"></div>

<script>
// ====== 状态管理 ======
const state = {
  time: 12,          // 0-24小时
  pumpPower: 70,     // 热泵功率 0-100%
  autoPlay: false,
  autoDir: 1,
  damaged: false,
  damageCabins: 0,   // 损伤微舱数
  damageX: 0,
  damageY: 0,
  animFrame: null,
  particles: [],     // 能量流粒子
  solarPhotons: [],  // 太阳能光子
  lastTime: 0
};

// ====== DOM 引用 ======
const $ = id => document.getElementById(id);
const svg = $('scene');
const timeSlider = $('timeSlider');
const pumpSlider = $('pumpSlider');
const timeVal = $('timeVal');
const pumpVal = $('pumpVal');
const btnDamage = $('btnDamage');
const btnAutoPlay = $('btnAutoPlay');
const btnReset = $('btnReset');
const toast = $('toast');

// ====== 工具函数 ======
function lerp(a, b, t) { return a + (b - a) * t; }
function clamp(v, min, max) { return Math.max(min, Math.min(max, v)); }
function lerpColor(c1, c2, t) {
  // c1, c2 为 [r,g,b]
  return `rgb(${Math.round(lerp(c1[0],c2[0],t))},${Math.round(lerp(c1[1],c2[1],t))},${Math.round(lerp(c1[2],c2[2],t))})`;
}

// ====== 日照与浮力计算 ======
function getSunFactor(t) {
  // 6时日出, 18时日落, 正弦曲线
  if (t < 5 || t > 19) return 0;
  return Math.max(0, Math.sin((t - 5) * Math.PI / 14));
}
function getAltitude(t, pump) {
  // 基于日照+热泵的综合高度 (0-1 归一化)
  const sun = getSunFactor(t);
  const heat = sun * 0.6 + (pump / 100) * 0.4;
  return clamp(heat, 0.05, 1);
}
function getGasTemp(t, pump) {
  // 气体温度 -20°C ~ +65°C
  const sun = getSunFactor(t);
  return lerp(-20, 65, sun * 0.5 + (pump / 100) * 0.5);
}

// ====== 天空渲染 ======
const skyStop1 = $('skyStop1');
const skyStop2 = $('skyStop2');
const skyStop3 = $('skyStop3');
const sunEl = $('sun');
const moonEl = $('moon');
const starsEl = $('stars');

const nightTop = [5, 10, 20];
const nightMid = [11, 22, 40];
const nightBot = [17, 29, 51];
const dayTop = [25, 55, 110];
const dayMid = [60, 120, 180];
const dayBot = [90, 150, 200];
const sunsetTop = [40, 20, 50];
const sunsetMid = [120, 50, 40];
const sunsetBot = [80, 80, 60];

function updateSky(t) {
  const sun = getSunFactor(t);
  // 判断是否在日出/日落时段
  const isSunrise = t >= 5 && t <= 7;
  const isSunset = t >= 17 && t <= 19;
  const twilight = isSunrise ? (t - 5) / 2 : isSunset ? (19 - t) / 2 : 0;

  let topC, midC, botC;
  if (twilight > 0 && sun < 0.5) {
    const tw = twilight;
    topC = dayTop.map((v, i) => lerp(nightTop[i], sunsetTop[i], tw));
    midC = dayMid.map((v, i) => lerp(nightMid[i], sunsetMid[i], tw));
    botC = dayBot.map((v, i) => lerp(nightBot[i], sunsetBot[i], tw));
  } else {
    topC = dayTop.map((v, i) => lerp(nightTop[i], v, sun));
    midC = dayMid.map((v, i) => lerp(nightMid[i], v, sun));
    botC = dayBot.map((v, i) => lerp(nightBot[i], v, sun));
  }

  skyStop1.setAttribute('stop-color', `rgb(${topC.map(Math.round).join(',')})`);
  skyStop2.setAttribute('stop-color', `rgb(${midC.map(Math.round).join(',')})`);
  skyStop3.setAttribute('stop-color', `rgb(${botC.map(Math.round).join(',')})`);

  // 太阳
  const sunAngle = (t - 6) * Math.PI / 12; // 6时升起, 18时落下
  const sunX = 480 + 300 * Math.cos(sunAngle - Math.PI / 2);
  const sunY = 100 - 80 * Math.sin(sunAngle);
  sunEl.setAttribute('cx', sunX);
  sunEl.setAttribute('cy', Math.max(40, sunY));
  sunEl.setAttribute('opacity', sun * 0.9);

  // 月亮
  moonEl.setAttribute('opacity', (1 - sun) * 0.6);
  const moonAngle = sunAngle + Math.PI;
  moonEl.setAttribute('cx', 480 + 250 * Math.cos(moonAngle - Math.PI / 2));
  moonEl.setAttribute('cy', Math.max(50, 100 - 60 * Math.sin(moonAngle)));

  // 星星
  starsEl.setAttribute('opacity', (1 - sun) * 0.8);
}

// ====== 飞艇位置与状态 ======
const airshipGroup = $('airshipGroup');
const solarFilm = $('solarFilm');
const innerGlow = $('innerGlow');
const hpRing = $('hpRing');

function updateAirship(t, pump) {
  const alt = getAltitude(t, pump);
  const sun = getSunFactor(t);

  // 飞艇垂直位置 (高度越高越靠上)
  // 基准位置y=0, 最大上移120px
  const yOffset = -alt * 120;
  airshipGroup.setAttribute('transform', `translate(0, ${yOffset})`);

  // 太阳能薄膜发光
  solarFilm.setAttribute('opacity', 0.3 + sun * 0.7);
  solarFilm.setAttribute('stroke-width', 4 + sun * 4);

  // 内腔发光 (加热时橙色, 冷却时蓝色)
  const temp = getGasTemp(t, pump);
  const heatFactor = clamp((temp + 20) / 85, 0, 1); // 0=冷 1=热
  const glowR = Math.round(lerp(0, 255, heatFactor));
  const glowG = Math.round(lerp(212, 107, heatFactor));
  const glowB = Math.round(lerp(255, 43, heatFactor));
  innerGlow.setAttribute('fill', `rgba(${glowR},${glowG},${glowB},${0.15 + heatFactor * 0.25})`);

  // 热泵环颜色
  const hpColor = heatFactor > 0.5 ? 'var(--accent)' : 'var(--cool)';
  hpRing.setAttribute('stroke', hpColor);

  // 高度指针
  const altPointer = $('altPointer');
  const pointerY = 500 - alt * 500; // 0km=500, 20km=0
  altPointer.setAttribute('transform', `translate(0, ${pointerY})`);

  return { alt, sun, heatFactor, temp, yOffset };
}

// ====== 放大区微舱更新 ======
const zoomCabins = document.querySelectorAll('.zcabin');
const zgasElements = document.querySelectorAll('.zgas');
const zvalveElements = document.querySelectorAll('.zvalve');

function updateZoomCabins(t, pump) {
  const sun = getSunFactor(t);
  const heatFactor = clamp((getGasTemp(t, pump) + 20) / 85, 0, 1);
  // 加热时气体膨胀(半径增大), 冷却时收缩
  const gasR = lerp(12, 24, heatFactor);
  // 颜色:冷蓝 → 暖橙
  const r = Math.round(lerp(0, 255, heatFactor));
  const g = Math.round(lerp(180, 107, heatFactor));
  const b = Math.round(lerp(255, 43, heatFactor));
  const a = lerp(0.2, 0.5, heatFactor);

  zgasElements.forEach((el, i) => {
    // 微小随机差异,让每个舱不完全同步
    const jitter = Math.sin(Date.now() * 0.002 + i * 1.3) * 0.08;
    const localHeat = clamp(heatFactor + jitter, 0, 1);
    const localR = lerp(12, 24, localHeat);
    const lr = Math.round(lerp(0, 255, localHeat));
    const lg = Math.round(lerp(180, 107, localHeat));
    const lb = Math.round(lerp(255, 43, localHeat));
    const la = lerp(0.2, 0.5, localHeat);
    el.setAttribute('r', localR);
    el.setAttribute('fill', `rgba(${lr},${lg},${lb},${la})`);
  });

  // 阀门:夜间/降温时打开(闪烁)
  const valveOpen = heatFactor < 0.35;
  zvalveElements.forEach((el, i) => {
    if (valveOpen) {
      const blink = Math.sin(Date.now() * 0.008 + i) > 0;
      el.setAttribute('opacity', blink ? 0.9 : 0.3);
      el.setAttribute('fill', 'var(--cool)');
    } else {
      el.setAttribute('opacity', 0.3);
      el.setAttribute('fill', 'var(--muted)');
    }
  });

  // 损伤舱变红
  if (state.damaged) {
    zoomCabins.forEach((cabin, i) => {
      if (i === 3 || i === 4) { // 模拟受损的2个舱
        const poly = cabin.querySelector('polygon');
        poly.setAttribute('fill', 'rgba(255,34,68,0.2)');
        poly.setAttribute('stroke', 'var(--danger)');
        const gas = cabin.querySelector('.zgas');
        gas.setAttribute('fill', 'rgba(255,34,68,0.1)');
        gas.setAttribute('r', 6);
        const valve = cabin.querySelector('.zvalve');
        valve.setAttribute('fill', 'var(--danger)');
        valve.setAttribute('opacity', 0.9);
      }
    });
  }
}

// ====== HUD 更新 ======
function updateHUD(t, pump) {
  const alt = getAltitude(t, pump);
  const temp = getGasTemp(t, pump);
  const sun = getSunFactor(t);
  const totalCabins = 1.2e9;
  const damagedCount = state.damaged ? state.damageCabins : 0;
  const buoyancyPct = ((totalCabins - damagedCount) / totalCabins * 100);
  const solarOutput = Math.round(sun * 1200);

  const hours = Math.floor(t);
  const mins = Math.floor((t - hours) * 60);
  $('hudTime').textContent = `${String(hours).padStart(2,'0')}:${String(mins).padStart(2,'0')}`;
  $('hudAlt').textContent = `${Math.round(alt * 20000).toLocaleString()} m`;
  $('hudBuoyancy').textContent = `${buoyancyPct.toFixed(4)}%`;
  $('hudTemp').textContent = `${temp > 0 ? '+' : ''}${Math.round(temp)}°C`;
  $('hudCabins').textContent = `${(totalCabins - damagedCount).toExponential(1)}`;
  $('hudTension').textContent = `${Math.round(500 * (0.9 + alt * 0.2))} kN`;
  $('hudSolar').textContent = solarOutput > 0 ? `${solarOutput} kW` : '0 kW';
  $('hudDamage').textContent = damagedCount.toLocaleString();
  $('hudDamage').setAttribute('fill', damagedCount > 0 ? 'var(--danger)' : 'var(--muted)');

  // 浮力条
  $('buoyancyBar').setAttribute('width', buoyancyPct * 3.5);
  $('buoyancyBar').setAttribute('fill', buoyancyPct > 99 ? 'var(--accent)' : 'var(--danger)');
}

// ====== 粒子系统 ======
const energyFlowsG = $('energyFlows');
const solarParticlesG = $('solarParticles');
const MAX_PARTICLES = 40;
const MAX_PHOTONS = 15;

function initParticles() {
  // 能量流粒子:热泵→舱壁
  for (let i = 0; i < MAX_PARTICLES; i++) {
    const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
    circle.setAttribute('r', '2');
    circle.setAttribute('fill', 'var(--accent)');
    circle.setAttribute('opacity', '0');
    energyFlowsG.appendChild(circle);
    state.particles.push({
      el: circle,
      angle: Math.random() * Math.PI * 2,
      speed: 0.3 + Math.random() * 0.5,
      dist: 0,
      maxDist: 60 + Math.random() * 140,
      life: 0,
      maxLife: 80 + Math.random() * 60
    });
  }
  // 太阳能光子
  for (let i = 0; i < MAX_PHOTONS; i++) {
    const line = document.createElementNS('http://www.w3.org/2000/svg', 'line');
    line.setAttribute('stroke', 'var(--solar)');
    line.setAttribute('stroke-width', '1.5');
    line.setAttribute('opacity', '0');
    line.setAttribute('stroke-linecap', 'round');
    solarParticlesG.appendChild(line);
    state.solarPhotons.push({
      el: line,
      x: 250 + Math.random() * 460,
      y: 0,
      speed: 1.5 + Math.random() * 2,
      life: 0,
      maxLife: 40 + Math.random() * 30,
      delay: Math.random() * 100
    });
  }
}

function updateParticles(t, pump) {
  const sun = getSunFactor(t);
  const heatFactor = clamp((getGasTemp(t, pump) + 20) / 85, 0, 1);

  // 能量流粒子
  state.particles.forEach(p => {
    p.life++;
    if (p.life > p.maxLife) {
      p.life = 0;
      p.angle = Math.random() * Math.PI * 2;
      p.dist = 0;
      p.speed = 0.3 + Math.random() * 0.5;
      p.maxDist = 60 + Math.random() * 140;
    }
    p.dist += p.speed * (heatFactor * 1.5);
    const progress = p.dist / p.maxDist;
    const opacity = progress < 0.1 ? progress * 10 : progress > 0.8 ? (1 - progress) * 5 : 1;

    const cx = 480 + Math.cos(p.angle) * p.dist;
    const cy = 410 + Math.sin(p.angle) * p.dist * 0.55;
    p.el.setAttribute('cx', cx);
    p.el.setAttribute('cy', cy);
    p.el.setAttribute('opacity', opacity * heatFactor * 0.7);
    p.el.setAttribute('fill', heatFactor > 0.5 ? '#ff6b2b' : '#00d4ff');
    p.el.setAttribute('r', 1.5 + heatFactor);
  });

  // 太阳能光子
  state.solarPhotons.forEach(p => {
    if (p.delay > 0) { p.delay--; return; }
    p.life++;
    if (p.life > p.maxLife) {
      p.life = 0;
      p.x = 250 + Math.random() * 460;
      p.y = 0;
      p.delay = Math.random() * 60;
    }
    p.y += p.speed;
    const progress = p.life / p.maxLife;
    const opacity = progress < 0.2 ? progress * 5 : progress > 0.7 ? (1 - progress) * 3.3 : 1;

    // 获取飞艇当前偏移
    const alt = getAltitude(t, pump);
    const yOffset = -alt * 120;
    const filmY = 395 + yOffset - (1 - progress) * 60;

    p.el.setAttribute('x1', p.x);
    p.el.setAttribute('y1', filmY - 15);
    p.el.setAttribute('x2', p.x);
    p.el.setAttribute('y2', filmY);
    p.el.setAttribute('opacity', opacity * sun * 0.6);
  });
}

// ====== 损伤模拟 ======
function simulateDamage() {
  if (state.damaged) {
    resetDamage();
    return;
  }
  state.damaged = true;
  state.damageCabins = 2400000; // 240万个微舱受损
  btnDamage.classList.add('active');
  btnDamage.innerHTML = '<i class="fas fa-undo"></i> 修复损伤';

  // 在外壳上显示损伤区域
  const overlay = $('damageOverlay');
  overlay.innerHTML = '';
  overlay.setAttribute('opacity', '1');

  // 创建损伤标记
  const dmgCx = 620, dmgCy = 360;
  for (let i = 0; i < 12; i++) {
    const angle = (i / 12) * Math.PI * 2;
    const r = 15 + Math.random() * 25;
    const cx = dmgCx + Math.cos(angle) * r;
    const cy = dmgCy + Math.sin(angle) * r * 0.6;
    const hex = document.createElementNS('http://www.w3.org/2000/svg', 'polygon');
    const s = 6 + Math.random() * 4;
    const pts = [];
    for (let j = 0; j < 6; j++) {
      const a = (j / 6) * Math.PI * 2 - Math.PI / 6;
      pts.push(`${cx + Math.cos(a) * s},${cy + Math.sin(a) * s}`);
    }
    hex.setAttribute('points', pts.join(' '));
    hex.setAttribute('fill', 'rgba(255,34,68,0.4)');
    hex.setAttribute('stroke', 'var(--danger)');
    hex.setAttribute('stroke-width', '1');
    overlay.appendChild(hex);
  }
  // 损伤中心闪光
  const flash = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
  flash.setAttribute('cx', dmgCx);
  flash.setAttribute('cy', dmgCy);
  flash.setAttribute('r', '35');
  flash.setAttribute('fill', 'rgba(255,34,68,0.15)');
  flash.setAttribute('filter', 'url(#softGlow)');
  overlay.appendChild(flash);

  // 损伤闪烁动画
  let flashCount = 0;
  const flashInterval = setInterval(() => {
    flash.setAttribute('opacity', flashCount % 2 === 0 ? '0.8' : '0.2');
    flashCount++;
    if (flashCount > 8) {
      clearInterval(flashInterval);
      flash.setAttribute('opacity', '0.4');
    }
  }, 150);

  showToast(`损伤模拟:${state.damageCabins.toLocaleString()} 个微舱失效,浮力仅丧失 ${((state.damageCabins / 1.2e9) * 100).toFixed(4)}%`);
}

function resetDamage() {
  state.damaged = false;
  state.damageCabins = 0;
  btnDamage.classList.remove('active');
  btnDamage.innerHTML = '<i class="fas fa-bolt"></i> 模拟损伤';
  $('damageOverlay').setAttribute('opacity', '0');
  $('damageOverlay').innerHTML = '';

  // 恢复放大区微舱颜色
  zoomCabins.forEach((cabin, i) => {
    const poly = cabin.querySelector('polygon');
    poly.setAttribute('fill', 'rgba(201,149,106,0.06)');
    poly.setAttribute('stroke', 'rgba(201,149,106,0.6)');
  });
}

// ====== Toast 提示 ======
function showToast(msg) {
  toast.textContent = msg;
  toast.classList.add('show');
  setTimeout(() => toast.classList.remove('show'), 4000);
}

// ====== 主动画循环 ======
function animate(timestamp) {
  if (!state.lastTime) state.lastTime = timestamp;
  const dt = timestamp - state.lastTime;
  state.lastTime = timestamp;

  // 自动播放
  if (state.autoPlay) {
    state.time += state.autoDir * dt * 0.001; // 每秒1小时
    if (state.time >= 24) { state.time = 24; state.autoDir = -1; }
    if (state.time <= 0) { state.time = 0; state.autoDir = 1; }
    timeSlider.value = state.time;
  }

  const t = state.time;
  const pump = state.pumpPower;

  updateSky(t);
  const airshipState = updateAirship(t, pump);
  updateZoomCabins(t, pump);
  updateParticles(t, pump);
  updateHUD(t, pump);

  // 更新时间显示
  const hours = Math.floor(t);
  const mins = Math.floor((t - hours) * 60);
  timeVal.textContent = `${String(hours).padStart(2,'0')}:${String(mins).padStart(2,'0')}`;

  state.animFrame = requestAnimationFrame(animate);
}

// ====== 事件绑定 ======
timeSlider.addEventListener('input', e => {
  state.time = parseFloat(e.target.value);
});

pumpSlider.addEventListener('input', e => {
  state.pumpPower = parseFloat(e.target.value);
  pumpVal.textContent = `${Math.round(state.pumpPower)}%`;
});

btnDamage.addEventListener('click', simulateDamage);

btnAutoPlay.addEventListener('click', () => {
  state.autoPlay = !state.autoPlay;
  btnAutoPlay.classList.toggle('active', state.autoPlay);
  btnAutoPlay.innerHTML = state.autoPlay
    ? '<i class="fas fa-pause"></i> 暂停'
    : '<i class="fas fa-play"></i> 自动循环';
});

btnReset.addEventListener('click', () => {
  state.time = 12;
  state.pumpPower = 70;
  state.autoPlay = false;
  timeSlider.value = 12;
  pumpSlider.value = 70;
  pumpVal.textContent = '70%';
  btnAutoPlay.classList.remove('active');
  btnAutoPlay.innerHTML = '<i class="fas fa-play"></i> 自动循环';
  resetDamage();
});

// ====== 初始化 ======
function init() {
  initParticles();
  state.animFrame = requestAnimationFrame(animate);
}

init();
</script>
</body>
</html>

实现说明

这个高保真 SVG 原理动画聚焦于 TRIZ 最终理想解 (IFR) 思想,直接展示"毛细血管式微元浮力块"在理想工作状态下的运作原理:

视觉设计

  • 深色技术蓝图风格:以深蓝黑色为基底,琥珀色(气凝胶结构)为主色调,橙色/青色分别表征热态/冷态,营造出活体工程图的美学
  • 蜂窝图案外壳:使用 SVG <pattern> 在飞艇壳体环域内铺设六角微舱纹理,直观传达"分布式浮力"概念
  • 7 舱放大视图:右侧放大的 7 个六边形微舱展示内部气体膨胀/收缩、阀门启闭等微观机理

IFR 核心表达

  1. 消除问题本身:动画不对比传统方案,直接展示"无骨架、无应力集中"的理想状态——壳体即是结构、即是浮力源
  2. 资源自利用:太阳能薄膜 → 热泵加热 → 气体膨胀 → 爬升,整条链路仅依赖日照这一"免费资源"
  3. 损伤冗余:点击"模拟损伤"后,240 万个微舱失效,浮力仅下降 0.2%,直观证明"无限冗余单元"如何消灭宏观失效风险

交互控制

控件 功能
时刻滑块 0~24 小时,控制日照/天空/浮力/高度全链路变化
热泵功率 调节加热强度,影响气体膨胀与升限
模拟损伤 在壳体制造局部破损,观察浮力几乎无变化
自动循环 自动播放完整日夜循环,展示"日升→平流层巡航→夜降→避风"时序
积分规则:第一轮对话扣减6分,后续每轮扣4分