分享图
动画工坊
引擎就绪
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>火箭一级柔性网兜捕与倾倒缓冲回收系统原理</title>
  <style>
    :root {
      --bg-dark: #070a11;
      --panel-bg: rgba(13, 20, 36, 0.75);
      --panel-border: rgba(0, 240, 255, 0.2);
      --cyan: #00f0ff;
      --cyan-glow: rgba(0, 240, 255, 0.4);
      --orange: #ff9f1c;
      --orange-glow: rgba(255, 159, 28, 0.4);
      --red: #ff3366;
      --green: #06d6a0;
      --text-main: #e2e8f0;
      --text-muted: #64748b;
      --font-mono: "SF Mono", "Fira Code", "Cascadia Code", "Roboto Mono", Consolas, monospace;
      --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
    }

    * {
      box-sizing: border-box;
      margin: 0;
      padding: 0;
      user-select: none;
    }

    body {
      background-color: var(--bg-dark);
      color: var(--text-main);
      font-family: var(--font-sans);
      width: 100vw;
      height: 100vh;
      overflow: hidden;
      display: flex;
      flex-direction: column;
      background-image: 
        radial-gradient(circle at 50% 20%, rgba(0, 110, 255, 0.08) 0%, transparent 70%),
        linear-gradient(to bottom, #070a11 0%, #0a0f1d 100%);
    }

    /* 顶部极简标题与状态栏 - 紧凑不遮挡 */
    header {
      position: absolute;
      top: 0;
      left: 0;
      right: 0;
      height: 48px;
      padding: 0 20px;
      display: flex;
      align-items: center;
      justify-content: space-between;
      background: linear-gradient(180deg, rgba(7,10,17,0.9) 0%, rgba(7,10,17,0) 100%);
      z-index: 10;
      pointer-events: none;
    }

    .header-left {
      display: flex;
      align-items: center;
      gap: 12px;
      pointer-events: auto;
    }

    .badge {
      font-family: var(--font-mono);
      font-size: 11px;
      font-weight: 700;
      letter-spacing: 1px;
      padding: 3px 8px;
      border-radius: 4px;
      background: rgba(0, 240, 255, 0.12);
      color: var(--cyan);
      border: 1px solid rgba(0, 240, 255, 0.3);
      text-transform: uppercase;
    }

    .title {
      font-size: 14px;
      font-weight: 600;
      color: #f1f5f9;
      letter-spacing: 0.5px;
    }

    .header-right {
      display: flex;
      gap: 16px;
      font-family: var(--font-mono);
      font-size: 11px;
      color: var(--text-muted);
    }

    .header-right span b {
      color: var(--cyan);
      font-weight: 600;
    }

    /* 核心动画区域 - 占据全屏核心视觉 */
    .viewport {
      flex: 1;
      width: 100%;
      height: 100%;
      position: relative;
      display: flex;
      align-items: center;
      justify-content: center;
    }

    svg#schematic {
      width: 100%;
      height: 100%;
      display: block;
    }

    /* 遥测参数小部件 (右上悬浮, 精简尺寸) */
    .telemetry-overlay {
      position: absolute;
      top: 56px;
      right: 20px;
      width: 220px;
      background: var(--panel-bg);
      border: 1px solid var(--panel-border);
      border-radius: 6px;
      padding: 10px 12px;
      backdrop-filter: blur(8px);
      box-shadow: 0 8px 24px rgba(0,0,0,0.5);
      pointer-events: auto;
      z-index: 10;
    }

    .telemetry-title {
      font-size: 10px;
      font-family: var(--font-mono);
      color: var(--cyan);
      text-transform: uppercase;
      letter-spacing: 1px;
      margin-bottom: 8px;
      display: flex;
      justify-content: space-between;
      border-bottom: 1px solid rgba(0,240,255,0.15);
      padding-bottom: 4px;
    }

    .tele-grid {
      display: grid;
      grid-template-columns: 1fr 1fr;
      gap: 6px 8px;
      font-family: var(--font-mono);
      font-size: 11px;
    }

    .tele-item {
      display: flex;
      flex-direction: column;
    }

    .tele-label {
      font-size: 9px;
      color: var(--text-muted);
    }

    .tele-val {
      color: #fff;
      font-weight: 600;
    }

    .tele-val.accent-orange { color: var(--orange); }
    .tele-val.accent-cyan { color: var(--cyan); }
    .tele-val.accent-green { color: var(--green); }

    /* 底部控制中心 - 悬浮紧凑底栏 */
    .control-bar {
      position: absolute;
      bottom: 16px;
      left: 50%;
      transform: translateX(-50%);
      width: calc(100% - 40px);
      max-width: 980px;
      background: var(--panel-bg);
      border: 1px solid var(--panel-border);
      border-radius: 8px;
      padding: 8px 16px;
      backdrop-filter: blur(12px);
      display: flex;
      flex-direction: column;
      gap: 8px;
      z-index: 20;
      box-shadow: 0 10px 30px rgba(0,0,0,0.6);
    }

    .timeline-row {
      display: flex;
      align-items: center;
      gap: 12px;
    }

    .btn-play {
      background: rgba(0, 240, 255, 0.15);
      border: 1px solid var(--cyan);
      color: var(--cyan);
      width: 32px;
      height: 32px;
      border-radius: 4px;
      cursor: pointer;
      display: flex;
      align-items: center;
      justify-content: center;
      font-size: 13px;
      transition: all 0.2s ease;
      flex-shrink: 0;
    }

    .btn-play:hover {
      background: var(--cyan);
      color: #000;
      box-shadow: 0 0 12px var(--cyan-glow);
    }

    .scrubber-container {
      flex: 1;
      display: flex;
      flex-direction: column;
      gap: 3px;
    }

    .time-slider {
      -webkit-appearance: none;
      width: 100%;
      height: 5px;
      border-radius: 3px;
      background: rgba(255, 255, 255, 0.1);
      outline: none;
      cursor: pointer;
    }

    .time-slider::-webkit-slider-thumb {
      -webkit-appearance: none;
      width: 13px;
      height: 13px;
      border-radius: 50%;
      background: var(--cyan);
      box-shadow: 0 0 8px var(--cyan);
      cursor: pointer;
      transition: transform 0.1s;
    }

    .time-slider::-webkit-slider-thumb:hover {
      transform: scale(1.3);
    }

    /* 阶段状态指示器 */
    .stages-row {
      display: flex;
      justify-content: space-between;
      gap: 6px;
    }

    .stage-pill {
      flex: 1;
      padding: 4px 6px;
      border-radius: 4px;
      background: rgba(255, 255, 255, 0.03);
      border: 1px solid rgba(255, 255, 255, 0.08);
      color: var(--text-muted);
      font-size: 10px;
      font-family: var(--font-sans);
      text-align: center;
      cursor: pointer;
      white-space: nowrap;
      transition: all 0.2s;
    }

    .stage-pill.active {
      background: rgba(0, 240, 255, 0.15);
      border-color: var(--cyan);
      color: #fff;
      font-weight: 500;
      box-shadow: 0 0 8px rgba(0,240,255,0.2);
    }

    /* 辅助微标签 */
    .legend-tag {
      font-family: var(--font-mono);
      font-size: 9px;
      fill: var(--text-muted);
    }

    .callout-box {
      background: rgba(10, 15, 28, 0.85);
      border-left: 2px solid var(--orange);
      padding: 4px 8px;
      border-radius: 0 4px 4px 0;
      font-size: 10px;
      color: #cbd5e1;
      pointer-events: none;
    }

    @media (max-width: 768px) {
      .telemetry-overlay { display: none; }
      .header-right { display: none; }
      .title { font-size: 12px; }
      .control-bar { width: calc(100% - 20px); bottom: 10px; padding: 6px 10px; }
      .stage-pill { font-size: 9px; padding: 3px 2px; }
    }
  </style>
</head>
<body>

  <!-- 顶部轻量状态栏 -->
  <header>
    <div class="header-left">
      <span class="badge">IFR 原理仿真</span>
      <span class="title">火箭一级柔性网兜捕与倾倒缓冲回收系统</span>
    </div>
    <div class="header-right">
      <span>落速标准: <b>≤10 m/s</b></span>
      <span>吸能量级: <b>5~10 MJ</b></span>
      <span>倾倒铰轴: <b>≥75°</b></span>
    </div>
  </header>

  <!-- 核心交互式原理动画视窗 -->
  <main class="viewport">
    <svg id="schematic" viewBox="0 0 1200 680" preserveAspectRatio="xMidYMid meet">
      <defs>
        <!-- 网格背景图案 -->
        <pattern id="gridPattern" width="40" height="40" patternUnits="userSpaceOnUse">
          <path d="M 40 0 L 0 0 0 40" fill="none" stroke="rgba(255,255,255,0.03)" stroke-width="1"/>
        </pattern>
        
        <!-- 线性渐变 -->
        <linearGradient id="rocketBodyGrad" x1="0%" y1="0%" x2="100%" y2="0%">
          <stop offset="0%" stop-color="#cbd5e1"/>
          <stop offset="50%" stop-color="#f8fafc"/>
          <stop offset="100%" stop-color="#94a3b8"/>
        </linearGradient>

        <linearGradient id="towerGrad" x1="0%" y1="0%" x2="100%" y2="0%">
          <stop offset="0%" stop-color="#1e293b"/>
          <stop offset="50%" stop-color="#334155"/>
          <stop offset="100%" stop-color="#0f172a"/>
        </linearGradient>

        <linearGradient id="slideGrad" x1="0%" y1="0%" x2="100%" y2="100%">
          <stop offset="0%" stop-color="#1e293b"/>
          <stop offset="100%" stop-color="#0b1120"/>
        </linearGradient>

        <linearGradient id="sandBedGrad" x1="0%" y1="0%" x2="100%" y2="0%">
          <stop offset="0%" stop-color="#d97706" stop-opacity="0.3"/>
          <stop offset="50%" stop-color="#f59e0b" stop-opacity="0.6"/>
          <stop offset="100%" stop-color="#b45309" stop-opacity="0.4"/>
        </linearGradient>

        <!-- 发光滤镜 -->
        <filter id="glow-cyan" x="-20%" y="-20%" width="140%" height="140%">
          <feGaussianBlur stdDeviation="3" result="blur"/>
          <feComposite in="SourceGraphic" in2="blur" operator="over"/>
        </filter>
        <filter id="glow-orange" x="-20%" y="-20%" width="140%" height="140%">
          <feGaussianBlur stdDeviation="4" result="blur"/>
          <feComposite in="SourceGraphic" in2="blur" operator="over"/>
        </filter>

        <!-- 箭头标记 -->
        <marker id="arrow-cyan" viewBox="0 0 10 10" refX="5" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
          <path d="M 0 1.5 L 8 5 L 0 8.5 z" fill="#00f0ff"/>
        </marker>
        <marker id="arrow-orange" viewBox="0 0 10 10" refX="5" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
          <path d="M 0 1.5 L 8 5 L 0 8.5 z" fill="#ff9f1c"/>
        </marker>
      </defs>

      <!-- 坐标网格背景 -->
      <rect width="100%" height="100%" fill="url(#gridPattern)"/>

      <!-- ==================== 静态场景与地面工程结构 ==================== -->
      <g id="ground-environment">
        <!-- 基础地平线 -->
        <line x1="20" y1="580" x2="1180" y2="580" stroke="#334155" stroke-width="2"/>
        
        <!-- 30° 缓坡滑道与 1.5m 缓冲沙床 (向右倾斜下延) -->
        <!-- 铰轴点位于 (580, 580) -->
        <path d="M 580 580 L 880 580 L 1120 620 L 1120 645 L 580 645 Z" fill="url(#slideGrad)" stroke="#475569" stroke-width="1.5"/>
        
        <!-- 缓冲沙床填充 -->
        <path d="M 740 580 L 1120 620 L 1120 635 L 740 595 Z" fill="url(#sandBedGrad)" stroke="#f59e0b" stroke-dasharray="4,2" stroke-width="1.5"/>
        <text x="910" y="605" fill="#f59e0b" font-size="10" font-family="var(--font-mono)" letter-spacing="1">1.5m 缓冲沙床 & 30°滑道</text>

        <!-- 55m 高门式格构立柱 (前侧主立柱支承结构示意) -->
        <g id="gantry-tower" opacity="0.85">
          <!-- 主立柱 (左侧支承) -->
          <path d="M 440 580 L 455 120 L 475 120 L 490 580 Z" fill="url(#towerGrad)" stroke="#475569" stroke-width="1.5"/>
          <!-- 桁架斜撑 -->
          <line x1="445" y1="520" x2="485" y2="460" stroke="#334155" stroke-width="1.2"/>
          <line x1="485" y1="460" x2="450" y2="400" stroke="#334155" stroke-width="1.2"/>
          <line x1="450" y1="400" x2="480" y2="340" stroke="#334155" stroke-width="1.2"/>
          <line x1="480" y1="340" x2="455" y2="280" stroke="#334155" stroke-width="1.2"/>
          <line x1="455" y1="280" x2="475" y2="220" stroke="#334155" stroke-width="1.2"/>
          <line x1="475" y1="220" x2="460" y2="160" stroke="#334155" stroke-width="1.2"/>
          <line x1="460" y1="160" x2="470" y2="120" stroke="#334155" stroke-width="1.2"/>
          
          <text x="400" y="115" fill="#94a3b8" font-size="10" font-family="var(--font-mono)">55m 格构立柱阵</text>
        </g>

        <!-- 顶部导向定滑轮组 -->
        <circle cx="465" cy="120" r="10" fill="#1e293b" stroke="#00f0ff" stroke-width="2"/>
        <circle cx="465" cy="120" r="3" fill="#00f0ff"/>

        <!-- 地面恒张力摩擦制动绞盘装置 (吸能核心) -->
        <g id="winch-station" transform="translate(330, 530)">
          <rect x="-15" y="0" width="65" height="50" rx="4" fill="#0f172a" stroke="#00f0ff" stroke-width="1.5"/>
          <!-- 绞盘滚筒 -->
          <circle id="winch-drum" cx="18" cy="24" r="16" fill="#1e293b" stroke="#ff9f1c" stroke-width="2.5"/>
          <line id="winch-spoke1" x1="18" y1="8" x2="18" y2="40" stroke="#ff9f1c" stroke-width="1.5"/>
          <line id="winch-spoke2" x1="2" y1="24" x2="34" y2="24" stroke="#ff9f1c" stroke-width="1.5"/>
          
          <!-- 液压缓冲气缸 -->
          <rect x="36" y="20" width="22" height="10" rx="2" fill="#334155" stroke="#64748b"/>
          <rect x="42" y="23" width="12" height="4" fill="#ff3366"/>

          <text x="-25" y="-8" fill="#00f0ff" font-size="10" font-family="var(--font-mono)">摩擦制动绞盘(250kN)</text>
          <text x="-15" y="62" fill="#64748b" font-size="9" font-family="var(--font-mono)">行程: 10m 恒张力放索</text>
        </g>

        <!-- 底轴可倾倒铰链机构 -->
        <g id="hinge-pivot" transform="translate(580, 580)">
          <path d="M -18 0 L 18 0 L 12 -16 L -12 -16 Z" fill="#334155" stroke="#64748b"/>
          <circle cx="0" cy="-14" r="9" fill="#0f172a" stroke="#00f0ff" stroke-width="2"/>
          <circle cx="0" cy="-14" r="3" fill="#00f0ff"/>
          <text x="-35" y="16" fill="#94a3b8" font-size="10" font-family="var(--font-mono)">底边解锁铰轴 (≥75°)</text>
        </g>
      </g>

      <!-- ==================== 动态运动构件群 ==================== -->
      
      <!-- 1. 牵引主索 (从绞盘 -> 经顶部滑轮 -> 连接网阵上边沿) -->
      <!-- 动态更新:path d 根据网阵位置变动 -->
      <path id="cable-to-winch" d="M 348 554 L 465 120" fill="none" stroke="#ff9f1c" stroke-width="3" stroke-dasharray="6,2"/>
      <path id="cable-main" d="M 465 120 L 580 140" fill="none" stroke="#00f0ff" stroke-width="3.5" filter="url(#glow-cyan)"/>

      <!-- 2. 倾倒式门式网阵框架与柔性兜网 (绕 (580, 566) 旋转) -->
      <!-- 网面由多条曲线构成以模拟动态鼓包兜住火箭 -->
      <g id="tilting-net-assembly">
        <!-- 网架刚性主梁 (从铰轴延伸至顶部) -->
        <line id="net-mast" x1="580" y1="566" x2="580" y2="140" stroke="#38bdf8" stroke-width="4" stroke-linecap="round"/>
        
        <!-- 柔性 UHMWPE 编织网面 (动态曲线插值) -->
        <g id="flexible-net-mesh" opacity="0.85">
          <!-- 纵向主经线 (多条贝塞尔曲线) -->
          <path id="net-curve-1" d="M 580 566 Q 580 350 580 140" fill="none" stroke="#00f0ff" stroke-width="2" stroke-dasharray="3,2"/>
          <path id="net-curve-2" d="M 580 566 Q 580 350 580 140" fill="none" stroke="#38bdf8" stroke-width="2" stroke-dasharray="4,2"/>
          <path id="net-curve-3" d="M 580 566 Q 580 350 580 140" fill="none" stroke="#00f0ff" stroke-width="2.5"/>
          
          <!-- 横向纬线索 -->
          <line id="lat-wire-1" x1="575" y1="200" x2="585" y2="200" stroke="#00f0ff" stroke-width="1.5"/>
          <line id="lat-wire-2" x1="570" y1="280" x2="590" y2="280" stroke="#00f0ff" stroke-width="1.5"/>
          <line id="lat-wire-3" x1="565" y1="360" x2="595" y2="360" stroke="#00f0ff" stroke-width="1.5"/>
          <line id="lat-wire-4" x1="570" y1="440" x2="590" y2="440" stroke="#00f0ff" stroke-width="1.5"/>
          <line id="lat-wire-5" x1="575" y1="510" x2="585" y2="510" stroke="#00f0ff" stroke-width="1.5"/>
        </g>
      </g>

      <!-- 3. 火箭一级箭体总成 (大端朝下姿态 + 群伞) -->
      <g id="rocket-stage" transform="translate(580, -200)">
        
        <!-- 减速群伞 (3具主伞示意) -->
        <g id="parachute-system" opacity="0.9">
          <!-- 伞绳索 -->
          <line x1="0" y1="-120" x2="-60" y2="-210" stroke="#94a3b8" stroke-width="1" stroke-dasharray="2,2"/>
          <line x1="0" y1="-120" x2="-20" y2="-210" stroke="#94a3b8" stroke-width="1" stroke-dasharray="2,2"/>
          <line x1="0" y1="-120" x2="20" y2="-210" stroke="#94a3b8" stroke-width="1" stroke-dasharray="2,2"/>
          <line x1="0" y1="-120" x2="60" y2="-210" stroke="#94a3b8" stroke-width="1" stroke-dasharray="2,2"/>
          
          <!-- 3 朵主降落伞冠 -->
          <path d="M -80 -210 C -80 -260 -20 -260 -20 -210 Z" fill="rgba(255, 159, 28, 0.4)" stroke="#ff9f1c" stroke-width="1.5"/>
          <path d="M -30 -215 C -30 -270 30 -270 30 -215 Z" fill="rgba(0, 240, 255, 0.4)" stroke="#00f0ff" stroke-width="1.5"/>
          <path d="M 20 -210 C 20 -260 80 -260 80 -210 Z" fill="rgba(255, 159, 28, 0.4)" stroke="#ff9f1c" stroke-width="1.5"/>
        </g>

        <!-- 箭体结构 (大端在下,顶部为级间段,底部为发动机主喷管及防撞缓冲裙) -->
        <g id="rocket-hull">
          <!-- 级间分离段 (顶端) -->
          <path d="M -10 -120 L 10 -120 L 12 -100 L -12 -100 Z" fill="#334155" stroke="#64748b"/>
          
          <!-- 栅格舵 (展开锁定姿态) -->
          <rect x="-24" y="-95" width="12" height="6" rx="1" fill="#475569" stroke="#00f0ff" stroke-width="1"/>
          <rect x="12" y="-95" width="12" height="6" rx="1" fill="#475569" stroke="#00f0ff" stroke-width="1"/>

          <!-- 箭体圆柱段 (长 120px) -->
          <rect x="-14" y="-100" width="28" height="120" rx="3" fill="url(#rocketBodyGrad)" stroke="#64748b" stroke-width="1.5"/>
          
          <!-- 涂装标识线 -->
          <line x1="-14" y1="-70" x2="14" y2="-70" stroke="#00f0ff" stroke-width="2"/>
          <line x1="-14" y1="-40" x2="14" y2="-40" stroke="#334155" stroke-width="1"/>
          <line x1="-14" y1="0" x2="14" y2="0" stroke="#ff9f1c" stroke-width="2"/>

          <!-- 底部发动机裙部 (大端朝下) -->
          <path d="M -16 20 L 16 20 L 22 42 L -22 42 Z" fill="#1e293b" stroke="#f59e0b" stroke-width="1.5"/>
          <!-- 主发动机喷管 -->
          <path d="M -10 42 L -6 54 L 6 54 L 10 42 Z" fill="#0f172a" stroke="#ff3366" stroke-width="1.5"/>
          <!-- 着陆腿机构 (折叠贴合) -->
          <line x1="-16" y1="10" x2="-20" y2="40" stroke="#475569" stroke-width="2.5"/>
          <line x1="16" y1="10" x2="20" y2="40" stroke="#475569" stroke-width="2.5"/>
        </g>

        <!-- 动能/受力实时矢量光晕 -->
        <circle id="impact-glow" cx="0" cy="0" r="36" fill="rgba(0, 240, 255, 0)" filter="url(#glow-cyan)"/>
      </g>

      <!-- ==================== 实时力学矢量与动态提示 ==================== -->
      <g id="force-overlay">
        <!-- 阻尼耗散力矢量 -->
        <g id="vector-braking" opacity="0">
          <line x1="465" y1="120" x2="400" y2="60" stroke="#ff9f1c" stroke-width="3" marker-end="url(#arrow-orange)" filter="url(#glow-orange)"/>
          <text x="350" y="50" fill="#ff9f1c" font-size="11" font-family="var(--font-mono)">F_索具制动 (250 kN)</text>
        </g>
        
        <!-- 倾倒旋转力矩箭头 -->
        <g id="vector-torque" opacity="0">
          <path d="M 640 400 A 180 180 0 0 1 720 480" fill="none" stroke="#00f0ff" stroke-width="2.5" stroke-dasharray="4,3" marker-end="url(#arrow-cyan)"/>
          <text x="730" y="470" fill="#00f0ff" font-size="11" font-family="var(--font-mono)">θ 绕轴受控顺转 (放平)</text>
        </g>
      </g>

      <!-- 底部刻度标尺 (米) -->
      <g opacity="0.3" transform="translate(0, 665)">
        <line x1="100" y1="0" x2="1100" y2="0" stroke="#94a3b8" stroke-width="1"/>
        <line x1="330" y1="-5" x2="330" y2="5" stroke="#94a3b8" stroke-width="1"/>
        <line x1="465" y1="-5" x2="465" y2="5" stroke="#94a3b8" stroke-width="1"/>
        <line x1="580" y1="-5" x2="580" y2="5" stroke="#94a3b8" stroke-width="1"/>
        <line x1="880" y1="-5" x2="880" y2="5" stroke="#94a3b8" stroke-width="1"/>
        <line x1="1120" y1="-5" x2="1120" y2="5" stroke="#94a3b8" stroke-width="1"/>
        <text x="330" y="-8" fill="#64748b" font-size="8" text-anchor="middle">绞盘基站</text>
        <text x="465" y="-8" fill="#64748b" font-size="8" text-anchor="middle">立柱顶部 55m</text>
        <text x="580" y="-8" fill="#64748b" font-size="8" text-anchor="middle">网底铰轴 0m</text>
        <text x="880" y="-8" fill="#64748b" font-size="8" text-anchor="middle">+30m 坡道</text>
        <text x="1120" y="-8" fill="#64748b" font-size="8" text-anchor="middle">+54m 沙床终端</text>
      </g>
    </svg>

    <!-- 右上角精简遥测 HUD 面板 -->
    <div class="telemetry-overlay">
      <div class="telemetry-title">
        <span>实时遥测仿真</span>
        <span id="tele-status">STANDBY</span>
      </div>
      <div class="tele-grid">
        <div class="tele-item">
          <span class="tele-label">进场落速 (Vz)</span>
          <span class="tele-val accent-cyan" id="tele-vz">9.8 m/s</span>
        </div>
        <div class="tele-item">
          <span class="tele-label">动能耗散 (Ek)</span>
          <span class="tele-val accent-orange" id="tele-ek">0.0 MJ</span>
        </div>
        <div class="tele-item">
          <span class="tele-label">放索行程 (L)</span>
          <span class="tele-val" id="tele-cable">0.0 m</span>
        </div>
        <div class="tele-item">
          <span class="tele-label">倾倒角度 (θ)</span>
          <span class="tele-val accent-green" id="tele-angle">0.0°</span>
        </div>
      </div>
    </div>
  </main>

  <!-- 底部微型控制栏与阶段导航 -->
  <footer class="control-bar">
    <div class="timeline-row">
      <button class="btn-play" id="btn-play-pause" title="播放/暂停">❚❚</button>
      <div class="scrubber-container">
        <input type="range" class="time-slider" id="scrubber" min="0" max="1000" value="0" step="1">
      </div>
      <span id="time-display" style="font-family: var(--font-mono); font-size: 11px; color: var(--text-muted); min-width: 45px;">0.00s</span>
    </div>
    <div class="stages-row">
      <div class="stage-pill active" data-stage="0">1. 末端进场</div>
      <div class="stage-pill" data-stage="200">2. 撞网入兜</div>
      <div class="stage-pill" data-stage="400">3. 恒张力吸能</div>
      <div class="stage-pill" data-stage="650">4. 铰轴倾倒放平</div>
      <div class="stage-pill" data-stage="850">5. 缓冲沙床静止</div>
    </div>
  </footer>

  <!-- ==================== 高保真动画与物理动力学插值引擎 ==================== -->
  <script>
    // 系统物理参数与常数
    const TOTAL_DURATION = 8000; // 动画总循环周期 8.0 秒
    const HINGE_X = 580;
    const HINGE_Y = 566;
    const MAST_LENGTH = 426; // 网面原高 (566 - 140)

    // DOM 元素引用
    const rocket = document.getElementById('rocket-stage');
    const parachute = document.getElementById('parachute-system');
    const netMast = document.getElementById('net-mast');
    const netCurve1 = document.getElementById('net-curve-1');
    const netCurve2 = document.getElementById('net-curve-2');
    const netCurve3 = document.getElementById('net-curve-3');
    const latWire1 = document.getElementById('lat-wire-1');
    const latWire2 = document.getElementById('lat-wire-2');
    const latWire3 = document.getElementById('lat-wire-3');
    const latWire4 = document.getElementById('lat-wire-4');
    const latWire5 = document.getElementById('lat-wire-5');
    const cableToWinch = document.getElementById('cable-to-winch');
    const cableMain = document.getElementById('cable-main');
    const winchDrum = document.getElementById('winch-drum');
    const winchSpoke1 = document.getElementById('winch-spoke1');
    const winchSpoke2 = document.getElementById('winch-spoke2');
    const vectorBraking = document.getElementById('vector-braking');
    const vectorTorque = document.getElementById('vector-torque');
    const impactGlow = document.getElementById('impact-glow');

    // 遥测数据指示
    const teleStatus = document.getElementById('tele-status');
    const teleVz = document.getElementById('tele-vz');
    const teleEk = document.getElementById('tele-ek');
    const teleCable = document.getElementById('tele-cable');
    const teleAngle = document.getElementById('tele-angle');

    // 控制器
    const scrubber = document.getElementById('scrubber');
    const btnPlayPause = document.getElementById('btn-play-pause');
    const timeDisplay = document.getElementById('time-display');
    const stagePills = document.querySelectorAll('.stage-pill');

    let isPlaying = true;
    let startTime = null;
    let pausedProgress = 0; // 0 ~ 1000
    let lastTimestamp = performance.now();

    // 辅助插值函数
    function lerp(a, b, t) { return a + (b - a) * t; }
    function easeOutQuad(t) { return t * (2 - t); }
    function easeInOutQuad(t) { return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t; }
    function clamp(val, min, max) { return Math.max(min, Math.min(max, val)); }

    // 主渲染帧更新 (progress: 0 ~ 1000)
    function renderFrame(progress) {
      const p = progress / 1000; // 0.0 ~ 1.0
      const simTimeSec = (p * (TOTAL_DURATION / 1000)).toFixed(2);
      timeDisplay.textContent = simTimeSec + 's';
      scrubber.value = progress;

      // 阶段高亮与遥测更新
      updateStageUI(progress);

      let rX = 580, rY = -150, rAngle = 0;
      let netAngle = 0; // 倾倒角度 (度)
      let bulge = 0;    // 网面受冲击横向鼓出位移 (像素)
      let winchRotate = 0;
      let parachuteOpacity = 1;
      let vz = 9.8, ek = 0.0, cableL = 0.0;

      // ---------------- 5大运动学阶段精细时序 ----------------
      if (p < 0.20) {
        // 【阶段 1: 末端进场 0 ~ 0.20】
        // 火箭大端朝下从空中匀速 9.8 m/s 下降,三伞稳定受阻
        const t1 = p / 0.20;
        rX = 580;
        rY = lerp(-120, 240, t1);
        rAngle = 0;
        netAngle = 0;
        bulge = 0;
        winchRotate = 0;
        parachuteOpacity = 1;

        vz = 9.8;
        ek = 0.0;
        cableL = 0.0;
        teleStatus.textContent = "APPROACHING";
        teleStatus.style.color = "var(--cyan)";
        vectorBraking.style.opacity = 0;
        vectorTorque.style.opacity = 0;
        impactGlow.setAttribute('fill', 'rgba(0,240,255,0)');

      } else if (p < 0.40) {
        // 【阶段 2: 撞网入兜 0.20 ~ 0.40】
        // 瞬间接触网面,网面大幅鼓出形成兜袋,四角牵引主索拽出
        const t2 = (p - 0.20) / 0.20;
        const e2 = easeOutQuad(t2);
        
        rX = lerp(580, 625, e2);
        rY = lerp(240, 360, e2);
        rAngle = lerp(0, 8, e2); // 略微前倾倾角
        
        bulge = lerp(0, 75, e2); // 网面最大鼓出 75px
        netAngle = 0;
        winchRotate = lerp(0, 180, e2);

        vz = lerp(9.8, 5.2, e2);
        ek = lerp(0.0, 4.2, e2);
        cableL = lerp(0.0, 3.8, e2);

        parachuteOpacity = lerp(1, 0.6, e2);
        teleStatus.textContent = "NET CAPTURED";
        teleStatus.style.color = "var(--orange)";
        
        vectorBraking.style.opacity = e2;
        vectorTorque.style.opacity = 0;
        impactGlow.setAttribute('fill', `rgba(255, 159, 28, ${0.4 * (1 - e2)})`);

      } else if (p < 0.65) {
        // 【阶段 3: 恒张力吸能 0.40 ~ 0.65】
        // 摩擦片恒张力放索 10m,耗散 8MJ 动能,速度平缓降至接近 0
        const t3 = (p - 0.40) / 0.25;
        const e3 = easeInOutQuad(t3);

        rX = lerp(625, 645, e3);
        rY = lerp(360, 420, e3);
        rAngle = lerp(8, 15, e3);

        bulge = lerp(75, 45, e3); // 随着放索鼓包微调
        netAngle = lerp(0, 12, e3); // 网底铰链受拉力解锁开始微动
        winchRotate = lerp(180, 720, e3); // 绞盘持续放索旋转

        vz = lerp(5.2, 0.8, e3);
        ek = lerp(4.2, 8.5, e3);
        cableL = lerp(3.8, 10.0, e3);

        parachuteOpacity = lerp(0.6, 0.2, e3);
        teleStatus.textContent = "WINCH BRAKING";
        teleStatus.style.color = "var(--orange)";

        vectorBraking.style.opacity = 1 - e3 * 0.5;
        vectorTorque.style.opacity = e3;

      } else if (p < 0.85) {
        // 【阶段 4: 铰轴倾倒放平 0.65 ~ 0.85】
        // 整片网连同火箭绕底轴缓慢向后方顺畅倒下(0° -> 78°),伞系彻底脱离
        const t4 = (p - 0.65) / 0.20;
        const e4 = easeInOutQuad(t4);

        netAngle = lerp(12, 78, e4); // 倾倒达到 78°
        bulge = lerp(45, 15, e4);

        // 火箭依附在网架上随之倾倒
        // 旋转几何变换:绕 (580, 566)
        const rad = (netAngle * Math.PI) / 180;
        const armLength = 220; // 箭体质心距铰链距离
        rX = HINGE_X + Math.sin(rad) * armLength + 10;
        rY = HINGE_Y - Math.cos(rad) * armLength;
        rAngle = netAngle;

        winchRotate = 720 + lerp(0, 120, e4);
        vz = lerp(0.8, 0.1, e4);
        ek = 8.5;
        cableL = 10.0;

        parachuteOpacity = 0;
        teleStatus.textContent = "TILTING DOWN";
        teleStatus.style.color = "var(--green)";

        vectorBraking.style.opacity = 0;
        vectorTorque.style.opacity = 1 - e4;

      } else {
        // 【阶段 5: 滑入缓冲沙床静止 0.85 ~ 1.0】
        // 火箭从倾倒网面滑脱,沿 30° 缓坡滑入 1.5m 缓冲沙床静止
        const t5 = (p - 0.85) / 0.15;
        const e5 = easeOutQuad(t5);

        netAngle = 78;
        bulge = 10;

        // 火箭顺着坡道向右下轻微滑行缓冲一段距离后稳稳停住
        const startSlideX = HINGE_X + Math.sin(78 * Math.PI / 180) * 220 + 10;
        const startSlideY = HINGE_Y - Math.cos(78 * Math.PI / 180) * 220;
        
        rX = lerp(startSlideX, startSlideX + 90, e5);
        rY = lerp(startSlideY, 570, e5);
        rAngle = lerp(78, 85, e5);

        winchRotate = 840;
        vz = 0.0;
        ek = 8.5;
        cableL = 10.0;

        parachuteOpacity = 0;
        teleStatus.textContent = "SAFE ARRESTED";
        teleStatus.style.color = "var(--green)";

        vectorBraking.style.opacity = 0;
        vectorTorque.style.opacity = 0;
      }

      // ---------------- 渲染 SVG 图元位置与几何曲线 ----------------

      // 1. 火箭位置与旋转
      rocket.setAttribute('transform', `translate(${rX}, ${rY}) rotate(${rAngle})`);
      parachute.style.opacity = parachuteOpacity;

      // 2. 网阵倾倒与主梁更新
      // 主梁顶端坐标 (TopX, TopY)
      const radNet = (netAngle * Math.PI) / 180;
      const topX = HINGE_X + Math.sin(radNet) * MAST_LENGTH;
      const topY = HINGE_Y - Math.cos(radNet) * MAST_LENGTH;
      
      netMast.setAttribute('x1', HINGE_X);
      netMast.setAttribute('y1', HINGE_Y);
      netMast.setAttribute('x2', topX);
      netMast.setAttribute('y2', topY);

      // 3. 动态柔性网面贝塞尔曲线 (模拟撞击时的鼓包形变)
      // 计算中段鼓出控制点
      const midX = (HINGE_X + topX) / 2 + Math.cos(radNet) * bulge;
      const midY = (HINGE_Y + topY) / 2 + Math.sin(radNet) * bulge;

      netCurve1.setAttribute('d', `M ${HINGE_X} ${HINGE_Y} Q ${midX - 10} ${midY} ${topX} ${topY}`);
      netCurve2.setAttribute('d', `M ${HINGE_X} ${HINGE_Y} Q ${midX} ${midY} ${topX} ${topY}`);
      netCurve3.setAttribute('d', `M ${HINGE_X} ${HINGE_Y} Q ${midX + 15} ${midY} ${topX} ${topY}`);

      // 横向纬线索跟随网架位移
      for (let i = 1; i <= 5; i++) {
        const wireRatio = i / 6;
        const wx = lerp(HINGE_X, topX, wireRatio) + Math.cos(radNet) * (bulge * Math.sin(wireRatio * Math.PI));
        const wy = lerp(HINGE_Y, topY, wireRatio) + Math.sin(radNet) * (bulge * Math.sin(wireRatio * Math.PI));
        const wireEl = document.getElementById(`lat-wire-${i}`);
        if (wireEl) {
          wireEl.setAttribute('x1', wx - 15 * Math.cos(radNet + Math.PI/2));
          wireEl.setAttribute('y1', wy - 15 * Math.sin(radNet + Math.PI/2));
          wireEl.setAttribute('x2', wx + 15 * Math.cos(radNet + Math.PI/2));
          wireEl.setAttribute('y2', wy + 15 * Math.sin(radNet + Math.PI/2));
        }
      }

      // 4. 牵引主索连接至网架顶端 (465, 120) -> (topX, topY)
      cableMain.setAttribute('x1', '465');
      cableMain.setAttribute('y1', '120');
      cableMain.setAttribute('x2', topX);
      cableMain.setAttribute('y2', topY);

      // 5. 绞盘滚筒转动
      winchDrum.setAttribute('transform', `rotate(${winchRotate} 18 24)`);
      winchSpoke1.setAttribute('transform', `rotate(${winchRotate} 18 24)`);
      winchSpoke2.setAttribute('transform', `rotate(${winchRotate} 18 24)`);

      // 6. 更新 HUD 遥测数值
      teleVz.textContent = vz.toFixed(1) + ' m/s';
      teleEk.textContent = ek.toFixed(1) + ' MJ';
      teleCable.textContent = cableL.toFixed(1) + ' m';
      teleAngle.textContent = netAngle.toFixed(1) + '°';
    }

    // 更新底部阶段药丸按钮状态
    function updateStageUI(progress) {
      stagePills.forEach(pill => {
        const stageVal = parseInt(pill.getAttribute('data-stage'));
        let nextStageVal = 1000;
        if (stageVal === 0) nextStageVal = 200;
        else if (stageVal === 200) nextStageVal = 400;
        else if (stageVal === 400) nextStageVal = 650;
        else if (stageVal === 650) nextStageVal = 850;

        if (progress >= stageVal && progress < nextStageVal) {
          pill.classList.add('active');
        } else {
          pill.classList.remove('active');
        }
      });
    }

    // 主动画循环驱动器 (RAF)
    function animationLoop(timestamp) {
      if (!startTime) startTime = timestamp;

      if (isPlaying) {
        const elapsed = (timestamp - startTime) % TOTAL_DURATION;
        const progress = (elapsed / TOTAL_DURATION) * 1000;
        pausedProgress = progress;
        renderFrame(progress);
      }
      requestAnimationFrame(animationLoop);
    }

    // 交互事件监听
    btnPlayPause.addEventListener('click', () => {
      isPlaying = !isPlaying;
      btnPlayPause.textContent = isPlaying ? '❚❚' : '▶';
      if (isPlaying) {
        startTime = performance.now() - (pausedProgress / 1000) * TOTAL_DURATION;
      }
    });

    scrubber.addEventListener('input', (e) => {
      const val = parseFloat(e.target.value);
      pausedProgress = val;
      renderFrame(val);
      if (isPlaying) {
        startTime = performance.now() - (val / 1000) * TOTAL_DURATION;
      }
    });

    stagePills.forEach(pill => {
      pill.addEventListener('click', () => {
        const targetProgress = parseInt(pill.getAttribute('data-stage'));
        pausedProgress = targetProgress;
        renderFrame(targetProgress);
        if (isPlaying) {
          startTime = performance.now() - (targetProgress / 1000) * TOTAL_DURATION;
        }
      });
    });

    // 页面加载完成后自动触发动画
    window.addEventListener('DOMContentLoaded', () => {
      renderFrame(0);
      requestAnimationFrame(animationLoop);
    });
  </script>
</body>
</html>
积分规则:每轮对话扣10分