分享图
动画工坊
引擎就绪
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>EPM无人机吊装系统 - IFR理想最终解原理演示</title>
    <style>
        :root {
            --bg-color: #0b1120;
            --grid-color: #1e293b;
            --text-main: #f8fafc;
            --text-dim: #94a3b8;
            --accent-cyan: #06b6d4;
            --accent-glow: #22d3ee;
            --accent-green: #10b981;
            --accent-red: #ef4444;
            --metal-light: #cbd5e1;
            --metal-dark: #475569;
            --warning: #f59e0b;
        }

        body {
            margin: 0;
            padding: 0;
            background-color: var(--bg-color);
            color: var(--text-main);
            font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            overflow: hidden;
            background-image: 
                linear-gradient(rgba(30, 41, 59, 0.3) 1px, transparent 1px),
                linear-gradient(90deg, rgba(30, 41, 59, 0.3) 1px, transparent 1px);
            background-size: 40px 40px;
        }

        #animation-container {
            width: 100%;
            height: 100%;
            max-width: 1600px;
            max-height: 900px;
            position: relative;
            box-shadow: 0 0 100px rgba(6, 182, 212, 0.05) inset;
        }

        svg {
            width: 100%;
            height: 100%;
        }

        .hud-text {
            font-family: 'Courier New', Courier, monospace;
            font-size: 14px;
            fill: var(--text-dim);
            letter-spacing: 1px;
        }

        .hud-value {
            font-family: 'Courier New', Courier, monospace;
            font-size: 16px;
            fill: var(--text-main);
            font-weight: bold;
            transition: fill 0.3s;
        }

        .hud-title {
            font-size: 24px;
            font-weight: 800;
            fill: var(--text-main);
            letter-spacing: 2px;
        }

        .hud-subtitle {
            font-size: 12px;
            fill: var(--accent-cyan);
            letter-spacing: 4px;
        }

        .triz-panel {
            fill: rgba(15, 23, 42, 0.8);
            stroke: var(--accent-cyan);
            stroke-width: 1;
            stroke-dasharray: 4 2;
        }

        /* Animations */
        @keyframes windBlow {
            0% { stroke-dashoffset: 100; opacity: 0; }
            20% { opacity: 0.6; }
            80% { opacity: 0.6; }
            100% { stroke-dashoffset: -100; opacity: 0; }
        }

        .wind-line {
            stroke: var(--text-dim);
            stroke-width: 2;
            stroke-dasharray: 20 80;
            animation: windBlow 3s linear infinite;
        }

        @keyframes pulseRing {
            0% { r: 20; opacity: 1; stroke-width: 10; }
            100% { r: 150; opacity: 0; stroke-width: 1; }
        }

        .pulse-effect {
            fill: none;
            stroke: var(--accent-cyan);
            opacity: 0;
        }

        .pulse-active {
            animation: pulseRing 0.5s ease-out;
        }

        @keyframes magFlow {
            0% { stroke-dashoffset: 40; }
            100% { stroke-dashoffset: 0; }
        }

        .mag-line {
            fill: none;
            stroke: var(--accent-green);
            stroke-width: 2;
            stroke-dasharray: 5 5;
            opacity: 0;
            transition: opacity 0.3s;
        }

        .mag-line.active {
            opacity: 0.8;
            animation: magFlow 1s linear infinite;
        }

        /* SVG Element Transitions */
        #hook-group {
            transition: transform 1.5s cubic-bezier(0.4, 0, 0.2, 1);
        }
        
        #cargo-group {
            transition: transform 1.5s cubic-bezier(0.4, 0, 0.2, 1);
        }

        .smooth-slide {
            transition: transform 0.6s cubic-bezier(0.34, 1.56, 0.64, 1) !important;
        }

        .epm-core {
            fill: var(--metal-dark);
            transition: fill 0.1s, filter 0.1s;
        }

        .epm-core.magnetized {
            fill: var(--accent-green);
            filter: drop-shadow(0 0 15px rgba(16, 185, 129, 0.8));
        }

        .force-arrow {
            opacity: 0;
            transition: opacity 0.3s;
        }

        .force-arrow.active {
            opacity: 1;
        }

        @keyframes flashText {
            0%, 100% { fill: var(--text-main); }
            50% { fill: var(--accent-red); }
        }
        .text-flash {
            animation: flashText 0.2s 3;
        }

    </style>
</head>
<body>

<div id="animation-container">
    <svg viewBox="0 0 1600 900" preserveAspectRatio="xMidYMid meet">
        <defs>
            <filter id="glow" x="-20%" y="-20%" width="140%" height="140%">
                <feGaussianBlur stdDeviation="5" result="blur" />
                <feComposite in="SourceGraphic" in2="blur" operator="over" />
            </filter>
            
            <linearGradient id="metal-grad" x1="0%" y1="0%" x2="100%" y2="0%">
                <stop offset="0%" stop-color="#475569" />
                <stop offset="50%" stop-color="#94a3b8" />
                <stop offset="100%" stop-color="#475569" />
            </linearGradient>

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

            <!-- Arrow Head -->
            <marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto">
                <path d="M 0 0 L 10 5 L 0 10 z" fill="var(--warning)" />
            </marker>
        </defs>

        <!-- Background Decor -->
        <g opacity="0.1">
            <circle cx="800" cy="450" r="300" fill="none" stroke="var(--accent-cyan)" stroke-width="1" stroke-dasharray="10 20"/>
            <circle cx="800" cy="450" r="400" fill="none" stroke="var(--accent-cyan)" stroke-width="1" />
            <line x1="800" y1="0" x2="800" y2="900" stroke="var(--accent-cyan)" stroke-width="1" />
            <line x1="0" y1="450" x2="1600" y2="450" stroke="var(--accent-cyan)" stroke-width="1" />
        </g>

        <!-- TRIZ & IFR Panel (Left) -->
        <g transform="translate(50, 50)">
            <rect width="380" height="260" rx="8" class="triz-panel" />
            <text x="20" y="40" class="hud-title" fill="var(--accent-cyan)">设计原理分析</text>
            <text x="20" y="60" class="hud-subtitle">TRIZ: IDEAL FINAL RESULT (IFR)</text>
            
            <line x1="20" y1="80" x2="360" y2="80" stroke="var(--grid-color)" stroke-width="2"/>
            
            <text x="20" y="110" class="hud-value" fill="#f8fafc">创新点 1: 被动形态对中</text>
            <text x="20" y="135" class="hud-text">利用喇叭形导向罩与圆台法兰的物理</text>
            <text x="20" y="155" class="hud-text">几何约束,通过重力下滑产生被动位移。</text>
            <text x="20" y="175" class="hud-text" fill="var(--warning)">→ 消除矛盾: 去除高精度对位驱动件</text>

            <text x="20" y="215" class="hud-value" fill="#f8fafc">创新点 2: 零功耗磁保持</text>
            <text x="20" y="240" class="hud-text">50ms脉冲电流切换电永磁(EPM)极性。</text>
            <text x="20" y="260" class="hud-text" fill="var(--accent-green)">→ 消除矛盾: 解决强磁吸力与持续耗电</text>
        </g>

        <!-- Telemetry Panel (Right) -->
        <g transform="translate(1170, 50)">
            <rect width="380" height="420" rx="8" class="triz-panel" />
            <text x="20" y="40" class="hud-title">遥测系统 (TELEMETRY)</text>
            <text x="20" y="60" class="hud-subtitle">实时传感器数据与状态监控</text>

            <line x1="20" y1="80" x2="360" y2="80" stroke="var(--grid-color)" stroke-width="2"/>

            <!-- Status Items -->
            <g transform="translate(20, 110)">
                <text x="0" y="0" class="hud-text">当前作业阶段:</text>
                <text x="140" y="0" class="hud-value" id="ui-stage" fill="var(--accent-cyan)">系统待机</text>

                <text x="0" y="40" class="hud-text">风扰动 (X轴):</text>
                <text x="140" y="40" class="hud-value" id="ui-wind" fill="var(--accent-red)">3级 侧风</text>

                <text x="0" y="80" class="hud-text">X轴 偏移误差:</text>
                <text x="140" y="80" class="hud-value" id="ui-offset" fill="var(--warning)">+80.0 mm</text>

                <text x="0" y="120" class="hud-text">导向接触开关:</text>
                <text x="140" y="120" class="hud-value" id="ui-contact">未触发</text>

                <text x="0" y="160" class="hud-text">线圈控制电流:</text>
                <text x="140" y="160" class="hud-value" id="ui-current">0.0 A</text>

                <text x="0" y="200" class="hud-text">电磁极性状态:</text>
                <text x="140" y="200" class="hud-value" id="ui-polarity">退磁 (释放)</text>

                <text x="0" y="240" class="hud-text">当前保持吸力:</text>
                <text x="140" y="240" class="hud-value" id="ui-suction">0 N</text>

                <text x="0" y="280" class="hud-text">吸力安全冗余:</text>
                <text x="140" y="280" class="hud-value" id="ui-redundancy">N/A</text>
            </g>

            <!-- Oscilloscope for Pulse -->
            <g transform="translate(20, 420)">
                <text x="0" y="0" class="hud-text">控制脉冲示波器 (50ms)</text>
                <rect x="0" y="10" width="340" height="60" fill="#0f172a" stroke="var(--grid-color)"/>
                <path id="pulse-graph" d="M 0 60 L 150 60 L 150 60 L 170 60 L 170 60 L 340 60" fill="none" stroke="var(--accent-cyan)" stroke-width="2"/>
                <text x="145" y="85" class="hud-subtitle" id="pulse-label" opacity="0">50ms 充磁脉冲</text>
            </g>
        </g>

        <!-- Wind Animation Elements -->
        <g id="wind-group" opacity="1">
            <path class="wind-line" d="M 300 300 L 700 300" />
            <path class="wind-line" d="M 250 400 L 650 400" style="animation-delay: 1s;" />
            <path class="wind-line" d="M 350 500 L 750 500" style="animation-delay: 0.5s;" />
            <path class="wind-line" d="M 400 600 L 800 600" style="animation-delay: 1.5s;" />
        </g>

        <!-- Main Animation Scene -->
        
        <!-- Cargo Base -->
        <g id="cargo-group" transform="translate(880, 650)">
            <!-- Cargo Box -->
            <rect x="-100" y="0" width="200" height="150" rx="5" fill="url(#cargo-grad)" stroke="var(--metal-dark)" stroke-width="3"/>
            <text x="0" y="80" text-anchor="middle" class="hud-title" fill="var(--text-dim)">PAYLOAD</text>
            <text x="0" y="105" text-anchor="middle" class="hud-text">10.0 KG</text>
            
            <!-- Standard Ferromagnetic Flange (Truncated Cone) -->
            <!-- Base width 100, Top width 40, Height 60 -->
            <path d="M -50 0 L 50 0 L 20 -60 L -20 -60 Z" fill="var(--metal-light)" stroke="#fff" stroke-width="1"/>
            <rect x="-20" y="-60" width="40" height="5" fill="var(--accent-cyan)" opacity="0.3"/> <!-- Interface -->
            
            <!-- Center Line -->
            <line x1="0" y1="-80" x2="0" y2="150" stroke="var(--text-dim)" stroke-dasharray="4 4" opacity="0.5"/>
            <text x="0" y="170" text-anchor="middle" class="hud-subtitle">货物几何中心</text>
        </g>

        <!-- Drone Hook Assembly -->
        <g id="hook-group" transform="translate(800, 100)">
            <!-- Drone Tether (Cable) -->
            <line x1="0" y1="-400" x2="0" y2="0" stroke="var(--metal-dark)" stroke-width="6" />
            <line x1="0" y1="-400" x2="0" y2="0" stroke="#fff" stroke-width="2" stroke-dasharray="10 10"/>

            <!-- Center Line (Hook) -->
            <line x1="0" y1="-50" x2="0" y2="150" stroke="var(--warning)" stroke-dasharray="4 4" opacity="0.8"/>
            <text x="0" y="-60" text-anchor="middle" class="hud-subtitle" fill="var(--warning)">吊索垂直轴</text>

            <!-- Force Vectors (Passive Centering) -->
            <g id="force-vectors" class="force-arrow">
                <path d="M 60 70 L 20 70" stroke="var(--warning)" stroke-width="4" marker-end="url(#arrow)"/>
                <text x="80" y="75" class="hud-value" fill="var(--warning)">滑移分力</text>
                <!-- Highlight collision area -->
                <circle cx="20" cy="50" r="15" fill="var(--warning)" opacity="0.4" filter="url(#glow)"/>
            </g>

            <!-- Magnetic Field Lines (Left & Right) -->
            <g id="magnetic-field">
                <!-- Left -->
                <path class="mag-line" d="M -15 30 C -80 30, -80 90, -15 90" />
                <path class="mag-line" d="M -10 20 C -120 20, -120 100, -10 100" />
                <!-- Right -->
                <path class="mag-line" d="M 15 30 C 80 30, 80 90, 15 90" />
                <path class="mag-line" d="M 10 20 C 120 20, 120 100, 10 100" />
            </g>

            <!-- Horn-shaped Guide Cover (Inverted shape, hollow inside) -->
            <!-- Top width 40, Bottom opening 140, Height 80 -->
            <path d="M -20 0 L 20 0 L 70 80 L 40 80 L 15 30 L -15 30 L -40 80 L -70 80 Z" fill="url(#metal-grad)" stroke="#fff" stroke-width="1.5"/>
            
            <!-- EPM Module (Center Cylinder) -->
            <rect x="-15" y="0" width="30" height="40" class="epm-core" id="epm-core" stroke="#fff" stroke-width="1"/>
            <!-- Coils representation -->
            <line x1="-15" y1="10" x2="15" y2="10" stroke="#f59e0b" stroke-width="2"/>
            <line x1="-15" y1="20" x2="15" y2="20" stroke="#f59e0b" stroke-width="2"/>
            <line x1="-15" y1="30" x2="15" y2="30" stroke="#f59e0b" stroke-width="2"/>

            <!-- Pulse Visual Effect -->
            <circle cx="0" cy="40" r="0" class="pulse-effect" id="pulse-ring"/>
        </g>

        <!-- Distance/Offset Dimension Line -->
        <g id="dimension-group" opacity="1">
            <line id="dim-line" x1="800" y1="200" x2="880" y2="200" stroke="var(--warning)" stroke-width="2"/>
            <line id="dim-left" x1="800" y1="190" x2="800" y2="210" stroke="var(--warning)" stroke-width="2"/>
            <line id="dim-right" x1="880" y1="190" x2="880" y2="210" stroke="var(--warning)" stroke-width="2"/>
            <text id="dim-text" x="840" y="185" text-anchor="middle" class="hud-value" fill="var(--warning)">ΔX: 80mm</text>
        </g>

    </svg>
</div>

<script>
    document.addEventListener("DOMContentLoaded", () => {
        // DOM Elements
        const hook = document.getElementById('hook-group');
        const cargo = document.getElementById('cargo-group');
        const epmCore = document.getElementById('epm-core');
        const pulseRing = document.getElementById('pulse-ring');
        const magLines = document.querySelectorAll('.mag-line');
        const forceVectors = document.getElementById('force-vectors');
        const windGroup = document.getElementById('wind-group');
        const pulseGraph = document.getElementById('pulse-graph');
        const pulseLabel = document.getElementById('pulse-label');
        
        // Dimension Elements
        const dimGroup = document.getElementById('dimension-group');
        const dimLine = document.getElementById('dim-line');
        const dimRight = document.getElementById('dim-right');
        const dimText = document.getElementById('dim-text');

        // UI Elements
        const uiStage = document.getElementById('ui-stage');
        const uiOffset = document.getElementById('ui-offset');
        const uiContact = document.getElementById('ui-contact');
        const uiCurrent = document.getElementById('ui-current');
        const uiPolarity = document.getElementById('ui-polarity');
        const uiSuction = document.getElementById('ui-suction');
        const uiRedundancy = document.getElementById('ui-redundancy');
        const uiWind = document.getElementById('ui-wind');

        // Initial Layout Coordinates
        const hookStartY = 150;
        const cargoStartY = 650;
        const hookDescendY = 560; // Y when touching flange
        const cargoLiftY = 350;
        const hookLiftY = cargoLiftY - 90; // Hook relative offset to cargo
        
        const hookStartX = 800; // Center of drone
        const cargoStartX = 880; // Misaligned by wind (+80)

        // Utility: Sleep
        const sleep = ms => new Promise(r => setTimeout(r, ms));

        // Utility: Set Transform
        const setTransform = (el, x, y, addClass = '') => {
            if (addClass) {
                el.classList.add(addClass);
            } else {
                el.classList.remove('smooth-slide');
            }
            el.style.transform = `translate(${x}px, ${y}px)`;
        };

        // Utility: Update Dimension Tracker visually
        const updateDimension = (hookX) => {
            const diff = cargoStartX - hookX;
            if (diff > 0) {
                dimGroup.style.opacity = 1;
                dimLine.setAttribute('x1', hookX);
                dimLine.setAttribute('x2', cargoStartX);
                const dimLeft = document.getElementById('dim-left');
                dimLeft.setAttribute('x1', hookX);
                dimLeft.setAttribute('x2', hookX);
                dimText.setAttribute('x', hookX + diff / 2);
                dimText.textContent = `ΔX: ${diff}mm`;
                uiOffset.textContent = `+${diff}.0 mm`;
                if(diff === 0) uiOffset.style.fill = "var(--accent-green)";
                else uiOffset.style.fill = "var(--warning)";
            } else {
                dimGroup.style.opacity = 0;
                uiOffset.textContent = `0.0 mm`;
                uiOffset.style.fill = "var(--accent-green)";
            }
        };

        // UI Update Function
        const updateUI = (el, text, color = null, flash = false) => {
            el.textContent = text;
            if (color) el.style.fill = color;
            if (flash) {
                el.classList.remove('text-flash');
                void el.offsetWidth; // trigger reflow
                el.classList.add('text-flash');
            }
        };

        // Action: 50ms Pulse Visualizer
        const triggerPulse = async (isMagnetizing) => {
            // Screen flash
            pulseRing.classList.remove('pulse-active');
            void pulseRing.offsetWidth;
            pulseRing.classList.add('pulse-active');

            // Oscilloscope animation
            pulseGraph.setAttribute('d', `M 0 60 L 150 60 L 150 10 L 170 10 L 170 60 L 340 60`);
            pulseLabel.style.opacity = 1;
            pulseLabel.textContent = isMagnetizing ? "50ms 充磁脉冲" : "50ms 退磁脉冲";
            pulseLabel.style.fill = isMagnetizing ? "var(--accent-green)" : "var(--accent-red)";
            
            updateUI(uiCurrent, "50.0 A (脉冲)", "var(--accent-cyan)", true);
            
            await sleep(50); // The 50ms literal wait
            
            pulseGraph.setAttribute('d', `M 0 60 L 340 60`);
            pulseLabel.style.opacity = 0;
            updateUI(uiCurrent, "0.0 A (零功耗)", "var(--accent-green)");
        };

        // Main Animation Loop
        async function runSequence() {
            while (true) {
                // --- STATE 0: RESET ---
                setTransform(hook, hookStartX, hookStartY);
                setTransform(cargo, cargoStartX, cargoStartY);
                epmCore.classList.remove('magnetized');
                magLines.forEach(l => l.classList.remove('active'));
                forceVectors.classList.remove('active');
                windGroup.style.opacity = 1;
                
                updateUI(uiStage, "系统待机 / 目标定位", "var(--accent-cyan)");
                updateUI(uiWind, "3级 侧风", "var(--accent-red)");
                updateUI(uiContact, "未接触", "var(--text-main)");
                updateUI(uiPolarity, "退磁 (释放)", "var(--text-dim)");
                updateUI(uiSuction, "0 N", "var(--text-dim)");
                updateUI(uiRedundancy, "N/A", "var(--text-dim)");
                
                // Track dimension manually since CSS transition handles the X later
                let currentHookX = hookStartX;
                updateDimension(currentHookX);

                await sleep(2000);

                // --- STATE 1: DESCEND ---
                updateUI(uiStage, "视觉下放吊索", "var(--warning)");
                setTransform(hook, hookStartX, hookDescendY - 60); // Lowering but not touching yet
                
                await sleep(1500);

                // --- STATE 2: PASSIVE CENTERING (IFR CORE) ---
                updateUI(uiStage, "物理被动滑移对中", "var(--accent-green)");
                updateUI(uiContact, "接触边缘", "var(--warning)");
                
                // Show force vectors to explain the TRIZ principle
                forceVectors.classList.add('active');
                
                // Hook slides to cargo center due to gravity/geometry
                setTransform(hook, cargoStartX, hookDescendY, 'smooth-slide');
                
                // Simulate dimensional update during transition
                let slideInterval = setInterval(() => {
                    currentHookX += (cargoStartX - currentHookX) * 0.1;
                    updateDimension(currentHookX);
                }, 30);

                await sleep(600);
                clearInterval(slideInterval);
                updateDimension(cargoStartX); // Lock to 0 offset
                
                forceVectors.classList.remove('active');
                updateUI(uiContact, "完美贴合", "var(--accent-green)");

                await sleep(500);

                // --- STATE 3: MAGNETIZATION (IFR CORE) ---
                updateUI(uiStage, "接触触发 - 充磁", "var(--accent-cyan)");
                await triggerPulse(true);

                // Apply magnetic state
                epmCore.classList.add('magnetized');
                magLines.forEach(l => l.classList.add('active'));
                
                updateUI(uiPolarity, "充磁 (保持中)", "var(--accent-green)");
                updateUI(uiSuction, "300 N", "var(--accent-green)", true);
                updateUI(uiRedundancy, "3.0 倍", "var(--accent-green)");

                await sleep(1500);

                // --- STATE 4: LIFTING ---
                updateUI(uiStage, "起吊飞行 (强磁保持)", "var(--accent-cyan)");
                windGroup.style.opacity = 0; // Less wind effect when flying high
                updateUI(uiWind, "1级 微风", "var(--accent-green)");

                // Lift both Hook and Cargo together
                setTransform(hook, cargoStartX, hookLiftY);
                setTransform(cargo, cargoStartX, cargoLiftY);

                await sleep(2500);

                // --- STATE 5: DROPPING (AT DESTINATION) ---
                updateUI(uiStage, "抵达目标地 - 放下", "var(--warning)");
                
                setTransform(hook, cargoStartX, hookDescendY);
                setTransform(cargo, cargoStartX, cargoStartY);

                await sleep(1500);

                // --- STATE 6: DEMAGNETIZATION ---
                updateUI(uiStage, "承重卸载 - 退磁", "var(--accent-red)");
                await triggerPulse(false);

                // Remove magnetic state
                epmCore.classList.remove('magnetized');
                magLines.forEach(l => l.classList.remove('active'));
                
                updateUI(uiPolarity, "退磁 (已释放)", "var(--text-dim)");
                updateUI(uiSuction, "0 N", "var(--text-dim)");
                updateUI(uiRedundancy, "N/A", "var(--text-dim)");

                await sleep(1000);

                // --- STATE 7: RETRACT ---
                updateUI(uiStage, "绞盘收起吊索", "var(--accent-cyan)");
                setTransform(hook, cargoStartX, hookStartY);
                
                await sleep(2000);
            }
        }

        // Start animation loop immediately
        runSequence();
    });
</script>
</body>
</html>
积分规则:第一轮对话扣减8分,后续每轮扣6分