独立渲染引擎就绪引擎就绪
<!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分
等待动画代码生成...
