分享图
动画工坊
引擎就绪
<!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>
    <style>
        @import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;700&display=swap');

        :root {
            --bg-color: #07090f;
            --grid-color: rgba(23, 42, 69, 0.5);
            --accent-cyan: #00f3ff;
            --accent-orange: #ff5500;
            --text-main: #a9b7c6;
            --text-dim: #5c7080;
            --font-mono: 'JetBrains Mono', monospace;
        }

        body, html {
            margin: 0;
            padding: 0;
            width: 100vw;
            height: 100vh;
            background-color: var(--bg-color);
            background-image: 
                linear-gradient(var(--grid-color) 1px, transparent 1px),
                linear-gradient(90deg, var(--grid-color) 1px, transparent 1px);
            background-size: 40px 40px;
            font-family: var(--font-mono);
            overflow: hidden;
            color: var(--text-main);
            display: flex;
            justify-content: center;
            align-items: center;
        }

        /* 绝对避免遮挡中央画面的遥测面板 */
        .telemetry-panel {
            position: absolute;
            background: rgba(7, 9, 15, 0.85);
            border: 1px solid rgba(0, 243, 255, 0.3);
            box-shadow: 0 0 15px rgba(0, 243, 255, 0.05);
            backdrop-filter: blur(8px);
            padding: 12px 16px;
            z-index: 100;
            pointer-events: none;
            display: flex;
            flex-direction: column;
            gap: 8px;
        }

        .panel-top-left {
            top: 24px;
            left: 24px;
            border-left: 3px solid var(--accent-cyan);
        }

        .panel-bottom-right {
            bottom: 24px;
            right: 24px;
            border-right: 3px solid var(--accent-orange);
            align-items: flex-end;
            text-align: right;
        }

        .sys-title {
            font-size: 12px;
            font-weight: 700;
            color: var(--accent-cyan);
            letter-spacing: 1px;
            margin-bottom: 4px;
        }

        .data-row {
            font-size: 10px;
            display: flex;
            gap: 12px;
            justify-content: space-between;
            width: 180px;
        }
        
        .data-label { color: var(--text-dim); }
        .data-value { color: #fff; font-weight: 700; font-variant-numeric: tabular-nums;}
        .value-cyan { color: var(--accent-cyan); }
        .value-orange { color: var(--accent-orange); }

        .ifr-notice {
            position: absolute;
            bottom: 24px;
            left: 24px;
            font-size: 10px;
            color: var(--text-dim);
            max-width: 250px;
            line-height: 1.4;
            pointer-events: none;
        }

        .ifr-highlight {
            color: var(--accent-cyan);
            font-weight: bold;
        }

        /* 核心动画容器,占满屏幕但不超出 */
        #stage-container {
            width: 100%;
            height: 100%;
            display: flex;
            justify-content: center;
            align-items: center;
            position: relative;
        }

        svg {
            width: 100%;
            height: 100%;
            max-width: 1400px;
            max-height: 900px;
            filter: drop-shadow(0 0 20px rgba(0, 0, 0, 0.5));
        }

        /* 屏幕震动特效 */
        @keyframes shake {
            0%, 100% { transform: translate(0, 0); }
            20% { transform: translate(-3px, 4px) rotate(-0.5deg); }
            40% { transform: translate(4px, -2px) rotate(0.5deg); }
            60% { transform: translate(-4px, -4px) rotate(-0.5deg); }
            80% { transform: translate(2px, 3px) rotate(0.5deg); }
        }
        .shake-active {
            animation: shake 0.4s cubic-bezier(.36,.07,.19,.97) both;
        }

        /* 微小的元件内联文字说明 */
        .svg-label {
            font-family: var(--font-mono);
            font-size: 10px;
            fill: var(--text-dim);
            letter-spacing: 0.5px;
        }
        .svg-label.highlight { fill: var(--accent-cyan); }
        .svg-label.alert { fill: var(--accent-orange); }
    </style>
</head>
<body>

    <!-- 顶部左侧:系统状态概览 -->
    <div class="telemetry-panel panel-top-left">
        <div class="sys-title">AERO-RECOVERY SYS // TRIZ IFR</div>
        <div class="data-row"><span class="data-label">PHASE:</span> <span class="data-value value-cyan" id="ui-phase">AWAITING</span></div>
        <div class="data-row"><span class="data-label">ALTITUDE:</span> <span class="data-value" id="ui-alt">--- m</span></div>
        <div class="data-row"><span class="data-label">VELOCITY:</span> <span class="data-value" id="ui-vel">--- m/s</span></div>
    </div>

    <!-- 底部右侧:关键子系统遥测 -->
    <div class="telemetry-panel panel-bottom-right">
        <div class="sys-title">SUBSYSTEM TELEMETRY</div>
        <div class="data-row"><span class="data-label">PARAFOIL AREA:</span> <span class="data-value" id="ui-area">0 m²</span></div>
        <div class="data-row"><span class="data-label">AIRBAG PRESSURE:</span> <span class="data-value value-orange" id="ui-press">0 kPa</span></div>
        <div class="data-row"><span class="data-label">VALVE STATUS:</span> <span class="data-value" id="ui-valve">CLOSED</span></div>
    </div>

    <!-- 底部左侧:设计原理解释 -->
    <div class="ifr-notice">
        [IFR] 消除死重与额外燃料:<br>利用大气气动阻力作为减速<span class="ifr-highlight">资源</span>,通过1200㎡滑翔翼伞实现无动力定向巡航。<br>末端利用高压多室气囊及泄压阀吸收地表冲击动能。
    </div>

    <div id="stage-container">
        <!-- 核心SVG画布 -->
        <svg id="main-svg" viewBox="0 0 1200 800" preserveAspectRatio="xMidYMid meet">
            <defs>
                <!-- 发光滤镜 -->
                <filter id="glow" x="-20%" y="-20%" width="140%" height="140%">
                    <feGaussianBlur stdDeviation="4" result="blur" />
                    <feComposite in="SourceGraphic" in2="blur" operator="over" />
                </filter>
                <filter id="glow-strong" x="-50%" y="-50%" width="200%" height="200%">
                    <feGaussianBlur stdDeviation="8" result="blur" />
                    <feMerge>
                        <feMergeNode in="blur" />
                        <feMergeNode in="SourceGraphic" />
                    </feMerge>
                </filter>
                
                <!-- 渐变定义 -->
                <linearGradient id="rocket-body" x1="0%" y1="0%" x2="100%" y2="0%">
                    <stop offset="0%" stop-color="#2a3242" />
                    <stop offset="30%" stop-color="#4f5b72" />
                    <stop offset="70%" stop-color="#8090a8" />
                    <stop offset="100%" stop-color="#1f2533" />
                </linearGradient>

                <linearGradient id="parafoil-grad" x1="0%" y1="0%" x2="0%" y2="100%">
                    <stop offset="0%" stop-color="#00f3ff" stop-opacity="0.9" />
                    <stop offset="100%" stop-color="#0066ff" stop-opacity="0.4" />
                </linearGradient>

                <radialGradient id="airbag-grad" cx="50%" cy="50%" r="50%" fx="30%" fy="30%">
                    <stop offset="0%" stop-color="#ffaa00" stop-opacity="0.9" />
                    <stop offset="70%" stop-color="#ff5500" stop-opacity="0.8" />
                    <stop offset="100%" stop-color="#aa1100" stop-opacity="0.9" />
                </radialGradient>
            </defs>

            <!-- 场景元素 -->
            <!-- 运动轨迹线 -->
            <path id="trajectory" d="M1100,-100 Q800,200 600,750" fill="none" stroke="rgba(0, 243, 255, 0.15)" stroke-width="2" stroke-dasharray="8 8" />
            
            <!-- 地面 -->
            <g id="ground-group">
                <rect x="0" y="750" width="1200" height="50" fill="#04060a" />
                <line x1="0" y1="750" x2="1200" y2="750" stroke="var(--accent-cyan)" stroke-width="1" filter="url(#glow)" />
                <polygon points="500,750 520,770 680,770 700,750" fill="rgba(0, 243, 255, 0.05)" stroke="var(--accent-cyan)" stroke-width="0.5" />
                <text x="600" y="765" class="svg-label" text-anchor="middle">RECOVERY ZONE // NOMINAL</text>
            </g>

            <!-- 火箭主体组合(包含降落伞和气囊,整体随轨迹运动) -->
            <g id="vehicle-assembly">
                
                <!-- 主翼伞 (初始隐藏/缩小) -->
                <g id="parafoil-system" opacity="0" transform="scale(0.1) translate(0, -150)">
                    <!-- 伞缆 -->
                    <path d="M-120,-80 L-10,0 M-60,-100 L-5,0 M0,-110 L0,0 M60,-100 L5,0 M120,-80 L10,0" stroke="rgba(255,255,255,0.4)" stroke-width="1" />
                    <!-- 翼伞本体 (冲压结构截面) -->
                    <path d="M-150,-80 C-150,-110 -80,-130 0,-130 C80,-130 150,-110 150,-80 C150,-70 140,-65 120,-65 C100,-75 80,-75 60,-65 C40,-75 20,-75 0,-65 C-20,-75 -40,-75 -60,-65 C-80,-75 -100,-75 -120,-65 C-140,-65 -150,-70 -150,-80 Z" 
                          fill="url(#parafoil-grad)" stroke="#00f3ff" stroke-width="1.5" filter="url(#glow)" />
                    <!-- 微小原理文字 -->
                    <text x="0" y="-145" class="svg-label highlight" text-anchor="middle">1200m² RAM-AIR PARAFOIL [AERO-DRAG UTILIZED]</text>
                </g>

                <!-- 火箭箭体 (水平或小倾角姿态) -->
                <g id="rocket-body-group" transform="rotate(0)">
                    <!-- 喷管 (熄火状态) -->
                    <polygon points="-60,10 -80,18 -80,-18 -60,-10" fill="#111" stroke="#333" stroke-width="1" />
                    <!-- 箭身圆柱 (为了演示侧收横向姿态,此处箭体横置,X为长度向) -->
                    <path d="M-60,-15 L60,-15 C75,-15 85,-5 85,0 C85,5 75,15 60,15 L-60,15 Z" fill="url(#rocket-body)" stroke="#555" stroke-width="1" />
                    <!-- 栅格舵(收起) -->
                    <rect x="50" y="-18" width="10" height="4" fill="#333" />
                    <rect x="50" y="14" width="10" height="4" fill="#333" />
                    
                    <!-- 气囊组 (初始隐藏,触地前充气) -->
                    <g id="airbag-system" opacity="0">
                        <!-- 底部主气囊 -->
                        <ellipse cx="-50" cy="25" rx="20" ry="20" fill="url(#airbag-grad)" stroke="#ffaa00" stroke-width="1.5" />
                        <!-- 侧边缓冲气囊 -->
                        <ellipse cx="0" cy="25" rx="18" ry="18" fill="url(#airbag-grad)" stroke="#ffaa00" stroke-width="1.5" />
                        <ellipse cx="45" cy="25" rx="15" ry="15" fill="url(#airbag-grad)" stroke="#ffaa00" stroke-width="1.5" />
                        
                        <!-- 气压标签跟随 -->
                        <text x="0" y="55" class="svg-label alert" text-anchor="middle" id="svg-pressure-txt">45 kPa PRE-PRESSURIZED</text>
                    </g>

                    <!-- 泄压阀排气粒子特效 (触地瞬间触发) -->
                    <g id="exhaust-particles" opacity="0">
                        <!-- 向左上方排气 -->
                        <path d="M-60,10 L-100,-20 M-55,5 L-90,-10 M-65,15 L-110,-30" stroke="rgba(255,255,255,0.8)" stroke-width="2" stroke-linecap="round" />
                        <!-- 向右上方排气 -->
                        <path d="M50,10 L90,-20 M45,5 L80,-10 M55,15 L100,-30" stroke="rgba(255,255,255,0.8)" stroke-width="2" stroke-linecap="round" />
                        <text x="0" y="-30" class="svg-label alert" text-anchor="middle" id="svg-valve-txt">VALVE OPEN [80 kPa THRESHOLD]</text>
                    </g>
                </g>
            </g>

        </svg>
    </div>

    <script>
        document.addEventListener("DOMContentLoaded", () => {
            // 获取DOM元素
            const elVehicle = document.getElementById('vehicle-assembly');
            const elParafoil = document.getElementById('parafoil-system');
            const elRocket = document.getElementById('rocket-body-group');
            const elAirbags = document.getElementById('airbag-system');
            const elExhaust = document.getElementById('exhaust-particles');
            const container = document.getElementById('stage-container');

            // UI 元素
            const uiPhase = document.getElementById('ui-phase');
            const uiAlt = document.getElementById('ui-alt');
            const uiVel = document.getElementById('ui-vel');
            const uiArea = document.getElementById('ui-area');
            const uiPress = document.getElementById('ui-press');
            const uiValve = document.getElementById('ui-valve');
            
            // SVG 内部文字状态
            const svgPressTxt = document.getElementById('svg-pressure-txt');
            const svgValveTxt = document.getElementById('svg-valve-txt');

            // 动画参数设定 (时间轴循环总长: 14000 毫秒)
            const CYCLE_DURATION = 14000;
            let startTime = performance.now();
            
            // 轨迹关键点映射控制
            // 阶段: 
            // 0% - 15%: 自由落体姿态调整 (15km)
            // 15% - 25%: 主翼伞展开减速 (8km)
            // 25% - 75%: 稳定滑翔寻的 
            // 75% - 82%: 接近地面 (50m),气囊充气
            // 82% - 85%: 触地,气囊压缩,泄压
            // 85% - 92%: 翼伞脱落,静止展示
            // 92% - 100%: 准备重置

            function easeOutCubic(x) {
                return 1 - Math.pow(1 - x, 3);
            }
            function easeOutElastic(x) {
                const c4 = (2 * Math.PI) / 3;
                return x === 0 ? 0 : x === 1 ? 1 : Math.pow(2, -10 * x) * Math.sin((x * 10 - 0.75) * c4) + 1;
            }

            // 核心渲染循环
            function renderFrame(now) {
                // 计算当前周期内的进度 (0.0 到 1.0)
                let elapsed = (now - startTime) % CYCLE_DURATION;
                let t = elapsed / CYCLE_DURATION;

                // --- 物理状态变量 ---
                let x = 0, y = 0, rot = 0;
                let pScale = 0, pOpacity = 0; // 翼伞状态
                let aScaleX = 1, aScaleY = 1, aOpacity = 0; // 气囊状态
                let eOpacity = 0; // 泄压排气状态
                let pOffset = 0; // 翼伞脱落位移

                // --- 遥测数据变量 ---
                let alt = 0, vel = 0, phase = "", area = 0, press = 0, valve = "CLOSED";

                // --- 阶段逻辑计算 ---
                if (t < 0.15) {
                    // 自由落体 & 调姿阶段 (高速斜下)
                    let pt = t / 0.15; // 0~1
                    x = 1100 - pt * 200;
                    y = -100 + pt * 300;
                    rot = 45 - pt * 45; // 从45度转为0度(水平)
                    
                    phase = "ATTITUDE ADJUST";
                    alt = Math.round(15000 - pt * 7000);
                    vel = Math.round(2500 - pt * 1500);
                    
                } else if (t < 0.25) {
                    // 翼伞展开阶段
                    let pt = (t - 0.15) / 0.10;
                    x = 900 - pt * 100;
                    y = 200 + pt * 150;
                    rot = 0;
                    
                    // 翼伞展开特效
                    pOpacity = pt;
                    pScale = easeOutElastic(pt); 
                    
                    phase = "PARAFOIL DEPLOY";
                    alt = Math.round(8000 - pt * 4000);
                    vel = Math.round(1000 - easeOutCubic(pt) * 850);
                    area = Math.round(pt * 1200);
                    
                } else if (t < 0.75) {
                    // 稳定滑翔阶段
                    let pt = (t - 0.25) / 0.50;
                    x = 800 - pt * 200;
                    y = 350 + pt * 350;
                    rot = 0;
                    
                    pOpacity = 1;
                    pScale = 1;
                    
                    phase = "GLIDING TARGETING";
                    alt = Math.max(50, Math.round(4000 - pt * 3950));
                    vel = 150; // 恒定滑翔速度
                    area = 1200;
                    
                } else if (t < 0.82) {
                    // 气囊充气阶段 (接近地面50m)
                    let pt = (t - 0.75) / 0.07;
                    x = 600;
                    y = 700 + pt * 45; // 接近 745 (考虑火箭半径和地面位置)
                    
                    pOpacity = 1;
                    pScale = 1;
                    
                    // 气囊猛烈充气
                    aOpacity = 1;
                    aScaleX = aScaleY = easeOutElastic(pt);
                    
                    phase = "AIRBAG INFLATION";
                    alt = Math.max(5, Math.round(50 - pt * 45));
                    vel = Math.max(20, Math.round(150 - pt * 130)); // 终点末速
                    area = 1200;
                    press = Math.round(pt * 45); // 充至 45kPa
                    
                } else if (t < 0.85) {
                    // 触地压缩与泄压阶段
                    let pt = (t - 0.82) / 0.03;
                    x = 600;
                    y = 745 + pt * 3; // 几乎停止,产生微小下压
                    
                    pOpacity = 1;
                    pScale = 1;
                    aOpacity = 1;
                    
                    // 形变吸收冲击
                    aScaleY = 1 - pt * 0.5; // Y轴压扁
                    aScaleX = 1 + pt * 0.3; // X轴外扩
                    
                    // 开启泄压阀
                    eOpacity = pt > 0.1 ? (1 - pt) * 2 : 0; 
                    valve = "OPEN (EXHAUSTING)";
                    svgValveTxt.setAttribute('opacity', pt > 0.1 ? 1 : 0);
                    
                    // 触发屏幕震动 (仅在瞬间触发一次类)
                    if (pt > 0.1 && pt < 0.2 && !container.classList.contains('shake-active')) {
                        container.classList.add('shake-active');
                        setTimeout(() => container.classList.remove('shake-active'), 400);
                    }

                    phase = "TOUCHDOWN & ABSORB";
                    alt = 0;
                    vel = 0;
                    area = 1200;
                    press = Math.min(80, 45 + Math.round(pt * 35)); // 峰值 80kPa
                    
                } else {
                    // 翼伞脱落静止展示阶段
                    let pt = Math.min(1, (t - 0.85) / 0.07);
                    x = 600;
                    y = 748;
                    
                    // 翼伞脱落飘走
                    pOffset = pt * 100;
                    pOpacity = 1 - pt;
                    pScale = 1;
                    
                    aOpacity = 1;
                    // 泄压后气囊稍微回弹维持瘪态
                    aScaleY = 0.5 + pt * 0.1;
                    aScaleX = 1.3 - pt * 0.1;
                    
                    eOpacity = 0;
                    valve = "CLOSED (LOCKED)";
                    svgValveTxt.setAttribute('opacity', 0);

                    phase = "SYSTEM SECURED";
                    alt = 0;
                    vel = 0;
                    area = Math.max(0, 1200 - Math.round(pt * 1200));
                    press = Math.max(10, 80 - Math.round(pt * 70));
                }

                // --- 渲染到 DOM ---
                // 火箭总成位置
                elVehicle.setAttribute('transform', `translate(${x}, ${y})`);
                // 火箭姿态
                elRocket.setAttribute('transform', `rotate(${rot})`);
                
                // 翼伞
                elParafoil.setAttribute('opacity', pOpacity);
                elParafoil.setAttribute('transform', `translate(${pOffset}, ${-pOffset}) scale(${pScale}) translate(0, -130)`);
                
                // 气囊
                elAirbags.setAttribute('opacity', aOpacity);
                elAirbags.setAttribute('transform', `scale(${aScaleX}, ${aScaleY})`);
                
                // 泄压特效
                elExhaust.setAttribute('opacity', eOpacity);
                if (eOpacity > 0) {
                    // 动态缩放排气线产生喷射感
                    let exhaustScale = 1 + (Math.random() * 0.2);
                    elExhaust.setAttribute('transform', `scale(${exhaustScale})`);
                }

                // --- 更新遥测 UI ---
                uiPhase.innerText = phase;
                uiAlt.innerText = alt.toString().padStart(5, '0') + " m";
                uiVel.innerText = vel.toString().padStart(4, '0') + " m/s";
                uiArea.innerText = area + " m²";
                
                uiPress.innerText = press + " kPa";
                if(press > 70) uiPress.style.color = "#ff1100";
                else uiPress.style.color = "var(--accent-orange)";
                
                uiValve.innerText = valve;
                if(valve.includes("OPEN")) uiValve.style.color = "#ff1100";
                else uiValve.style.color = "var(--text-main)";

                svgPressTxt.textContent = press > 10 ? `${press} kPa PRESSURE` : "";

                // 循环请求下一帧
                requestAnimationFrame(renderFrame);
            }

            // 启动动画
            requestAnimationFrame(renderFrame);
        });
    </script>
</body>
</html>
积分规则:第一轮对话扣减8分,后续每轮扣6分