分享图
动画工坊
引擎就绪
<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>TRIZ IFR - 零阻力降杆机构动画</title>
    <style>
        :root {
            --bg-color: #060913;
            --grid-color: #111a2f;
            --text-main: #64748b;
            --text-hl: #e2e8f0;
            --accent-cyan: #0ea5e9;
            --accent-green: #10b981;
            --accent-red: #ef4444;
            --accent-amber: #f59e0b;
            --mech-base: #1e293b;
            --mech-arm: #cbd5e1;
            --mech-dark: #0f172a;
        }

        body, html {
            margin: 0;
            padding: 0;
            width: 100%;
            height: 100%;
            background-color: var(--bg-color);
            display: flex;
            justify-content: center;
            align-items: center;
            font-family: 'SF Mono', Consolas, 'Courier New', monospace;
            overflow: hidden;
            color: var(--text-main);
        }

        /* 核心要求:容器居中,尺寸足够大 */
        .animation-container {
            position: relative;
            width: 100vw;
            height: 100vh;
            display: flex;
            justify-content: center;
            align-items: center;
        }

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

        /* 核心要求:极简、小字号说明文字,放置在边缘角落,绝对不遮挡中心动画 */
        .hud-text {
            font-size: 11px;
            fill: var(--text-main);
            letter-spacing: 1px;
            user-select: none;
        }
        
        .hud-title {
            font-size: 13px;
            fill: var(--text-hl);
            font-weight: bold;
            letter-spacing: 2px;
        }

        .hud-value {
            font-size: 12px;
            fill: var(--accent-cyan);
            font-weight: bold;
        }

        .hud-ifr-badge {
            font-size: 10px;
            fill: var(--accent-amber);
            font-weight: bold;
        }

        /* SVG 元素样式 */
        .glow-cyan { filter: url(#glow-cyan); }
        .glow-red { filter: url(#glow-red); }
        .glow-green { filter: url(#glow-green); }
        
        .mech-stroke {
            stroke: var(--mech-arm);
            stroke-width: 2;
            fill: none;
            stroke-linecap: round;
        }

        .dash-circle {
            stroke: rgba(255, 255, 255, 0.2);
            stroke-width: 1;
            fill: none;
            stroke-dasharray: 4 4;
        }
    </style>
</head>
<body>

<div class="animation-container">
    <svg viewBox="0 0 1200 800" preserveAspectRatio="xMidYMid meet" id="main-svg">
        <defs>
            <!-- 背景网格图案 -->
            <pattern id="grid" width="40" height="40" patternUnits="userSpaceOnUse">
                <path d="M 40 0 L 0 0 0 40" fill="none" stroke="var(--grid-color)" stroke-width="1"/>
                <circle cx="0" cy="0" r="1.5" fill="#1e293b"/>
            </pattern>

            <pattern id="micro-grid" width="10" height="10" patternUnits="userSpaceOnUse">
                <path d="M 10 0 L 0 0 0 10" fill="none" stroke="rgba(17, 26, 47, 0.4)" stroke-width="0.5"/>
            </pattern>

            <!-- 发光滤镜 -->
            <filter id="glow-cyan" x="-50%" y="-50%" width="200%" height="200%">
                <feGaussianBlur stdDeviation="6" result="blur" />
                <feComposite in="SourceGraphic" in2="blur" operator="over" />
            </filter>
            <filter id="glow-red" x="-50%" y="-50%" width="200%" height="200%">
                <feGaussianBlur stdDeviation="8" result="blur" />
                <feFlood flood-color="#ef4444" result="color"/>
                <feComposite in="color" in2="blur" operator="in" result="glow"/>
                <feComposite in="SourceGraphic" in2="glow" operator="over" />
            </filter>
            <filter id="glow-green" x="-50%" y="-50%" width="200%" height="200%">
                <feGaussianBlur stdDeviation="6" result="blur" />
                <feFlood flood-color="#10b981" result="color"/>
                <feComposite in="color" in2="blur" operator="in" result="glow"/>
                <feComposite in="SourceGraphic" in2="glow" operator="over" />
            </filter>

            <!-- 阴影滤镜 -->
            <filter id="shadow" x="-10%" y="-10%" width="120%" height="120%">
                <feDropShadow dx="2" dy="5" stdDeviation="4" flood-opacity="0.4"/>
            </filter>

            <!-- 金属渐变 -->
            <linearGradient id="metal-grad" x1="0%" y1="0%" x2="0%" y2="100%">
                <stop offset="0%" stop-color="#f8fafc" />
                <stop offset="50%" stop-color="#cbd5e1" />
                <stop offset="100%" stop-color="#94a3b8" />
            </linearGradient>
            
            <linearGradient id="dark-metal" x1="0%" y1="0%" x2="0%" y2="100%">
                <stop offset="0%" stop-color="#334155" />
                <stop offset="100%" stop-color="#0f172a" />
            </linearGradient>
        </defs>

        <!-- 背景 -->
        <rect width="100%" height="100%" fill="url(#micro-grid)"/>
        <rect width="100%" height="100%" fill="url(#grid)"/>
        
        <!-- 核心物理连线及辅助轨迹 -->
        <g id="guide-lines">
            <circle cx="300" cy="550" r="280" class="dash-circle"/>
            <path d="M 300 550 L 300 270" class="dash-circle"/>
            <path d="M 300 550 L 580 550" class="dash-circle"/>
            <!-- 理想解清空区域指示 -->
            <path id="ifr-zone" d="M 400 600 A 100 100 0 0 0 500 500 L 500 550 Z" fill="rgba(14, 165, 233, 0)" />
        </g>

        <!-- 系统部件组 (居中放大) -->
        
        <!-- 5. 液压缓冲器 -->
        <g id="buffer-assembly" transform="translate(680, 520)">
            <rect x="-20" y="0" width="40" height="80" fill="var(--mech-base)" rx="4"/>
            <rect id="buffer-piston" x="-10" y="-30" width="20" height="40" fill="url(#metal-grad)"/>
            <path d="M -25 -30 L 25 -30" stroke="var(--mech-arm)" stroke-width="4"/>
            <text x="35" y="10" class="hud-text">HYDRAULIC_BUFFER</text>
        </g>

        <!-- 2. 主回位拉簧 -->
        <g id="spring-assembly">
            <circle cx="500" cy="700" r="6" fill="var(--mech-base)" stroke="var(--text-main)" stroke-width="2"/>
            <path id="spring-path" d="" fill="none" stroke="var(--accent-amber)" stroke-width="6" stroke-linejoin="round"/>
            <circle id="spring-top-joint" cx="500" cy="550" r="5" fill="#fff"/>
        </g>

        <!-- 1. 电机与偏心凸轮 -->
        <g id="motor-assembly" transform="translate(420, 600)">
            <!-- 电机本体底座 -->
            <rect x="-40" y="-30" width="80" height="100" fill="url(#dark-metal)" rx="8" filter="url(#shadow)"/>
            <circle cx="0" cy="0" r="15" fill="#1e293b" stroke="var(--text-main)" stroke-width="2"/>
            
            <!-- 凸轮 -->
            <g id="cam-rotor" transform="rotate(0)">
                <!-- 水滴形凸轮 -->
                <path d="M -25 0 A 25 25 0 1 0 25 0 L 10 -90 A 10 10 0 0 0 -10 -90 Z" fill="var(--accent-cyan)" class="glow-cyan" opacity="0.9"/>
                <circle cx="0" cy="0" r="6" fill="#fff"/>
                <circle cx="0" cy="-75" r="4" fill="#0f172a"/>
            </g>
        </g>

        <!-- 3. 摇臂系统 -->
        <g id="rocker-assembly" transform="translate(300, 550)">
            <g id="rocker-arm" transform="rotate(0)">
                <!-- 摇臂主体 (总长500) -->
                <rect x="0" y="-20" width="480" height="40" fill="url(#metal-grad)" rx="20" filter="url(#shadow)"/>
                
                <!-- 锁扣槽 -->
                <rect x="50" y="-25" width="20" height="15" fill="var(--bg-color)"/>
                
                <!-- 弹簧连接点 -->
                <circle cx="200" cy="0" r="6" fill="var(--mech-dark)"/>
                
                <!-- 凸轮受力垫板 -->
                <rect x="90" y="15" width="60" height="10" fill="var(--accent-amber)" rx="2"/>
                
                <!-- 物理枢轴 -->
                <circle cx="0" cy="0" r="12" fill="url(#dark-metal)" stroke="#94a3b8" stroke-width="3"/>
                <circle cx="0" cy="0" r="4" fill="#fff"/>
            </g>
        </g>

        <!-- 4. 独立电磁锁扣 (位于摇臂 90度 也就是垂直时的左侧) -->
        <g id="latch-assembly" transform="translate(240, 240)">
            <!-- 电磁铁外壳 -->
            <rect x="-40" y="-20" width="40" height="40" fill="url(#dark-metal)" rx="4"/>
            <!-- 锁舌 -->
            <rect id="latch-pin" x="0" y="-6" width="30" height="12" fill="var(--mech-arm)" rx="2"/>
            <!-- 状态指示灯 -->
            <circle id="latch-light" cx="-20" cy="0" r="5" fill="var(--accent-amber)"/>
        </g>


        <!-- HUD 数据层 (布置在四角,绝不遮挡中央机构) -->
        <g id="hud-layer">
            <!-- 左上角:系统标题与原理说明 -->
            <g transform="translate(40, 40)">
                <text x="0" y="0" class="hud-title">SYSTEM_ARCHITECTURE // TRIZ IFR</text>
                <text x="0" y="25" class="hud-text">MISSION: ELIMINATE BACK-DRAG RESISTANCE</text>
                <text x="0" y="45" class="hud-text">SOLUTION: CAM LIFT + INDEPENDENT LATCH</text>
                <rect x="0" y="55" width="300" height="1" fill="var(--grid-color)"/>
                <text x="0" y="75" class="hud-ifr-badge">IFR STATE: PURE MECHANICAL FREE-FALL</text>
            </g>

            <!-- 右上角:实时遥测数据 -->
            <g transform="translate(980, 40)">
                <text x="180" y="0" class="hud-title" text-anchor="end">TELEMETRY_DATA</text>
                
                <text x="60" y="30" class="hud-text">PHASE:</text>
                <text x="180" y="30" class="hud-value" id="hud-phase" text-anchor="end">IDLE</text>
                
                <text x="60" y="50" class="hud-text">MOTOR_CAM:</text>
                <text x="180" y="50" class="hud-value" id="hud-cam-angle" text-anchor="end">0.0°</text>
                
                <text x="60" y="70" class="hud-text">ARM_ANGLE:</text>
                <text x="180" y="70" class="hud-value" id="hud-arm-angle" text-anchor="end">0.0°</text>
                
                <text x="60" y="90" class="hud-text">LATCH_SYS:</text>
                <text x="180" y="90" class="hud-value" id="hud-latch" text-anchor="end" fill="var(--accent-amber)">DISENGAGED</text>
            </g>

            <!-- 左下角:TRIZ 核心机制动态指示 -->
            <g transform="translate(40, 720)">
                <text x="0" y="0" class="hud-text" fill="var(--text-hl)">IFR MECHANISM INSIGHT:</text>
                <text x="0" y="20" class="hud-text" id="hud-insight" fill="var(--accent-cyan)">MOTOR PUSHING ARM, STORING POTENTIAL ENERGY</text>
                <rect x="0" y="35" width="10" height="10" fill="var(--accent-cyan)"/>
                <text x="18" y="44" class="hud-text">ENERGY INPUT (CAM)</text>
                <rect x="180" y="35" width="10" height="10" fill="var(--accent-amber)"/>
                <text x="198" y="44" class="hud-text">POTENTIAL (SPRING)</text>
            </g>
            
            <!-- 动态 IFR 清空区域指示箭头和文字 (由JS控制透明度) -->
            <g id="ifr-indicator" transform="translate(380, 650)" opacity="0">
                <path d="M 0 0 L 40 -40 M 40 -40 L 40 -20 M 40 -40 L 20 -40" stroke="var(--accent-green)" stroke-width="2" fill="none" class="glow-green"/>
                <text x="50" y="-35" class="hud-text" fill="var(--accent-green)" font-weight="bold">IFR: MOTOR CLEARED (ZERO DRAG)</text>
            </g>
        </g>
    </svg>
</div>

<script>
    // 动画状态与时间控制 (纯JS实现精确物理状态机)
    const duration = 10000; // 10秒一个完整周期
    
    // DOM 元素引用
    const elCam = document.getElementById('cam-rotor');
    const elArm = document.getElementById('rocker-arm');
    const elSpringPath = document.getElementById('spring-path');
    const elSpringJoint = document.getElementById('spring-top-joint');
    const elLatchPin = document.getElementById('latch-pin');
    const elLatchLight = document.getElementById('latch-light');
    const elBufferPiston = document.getElementById('buffer-piston');
    const elIfrIndicator = document.getElementById('ifr-indicator');
    const elIfrZone = document.getElementById('ifr-zone');
    
    // HUD 引用
    const hudPhase = document.getElementById('hud-phase');
    const hudCamAngle = document.getElementById('hud-cam-angle');
    const hudArmAngle = document.getElementById('hud-arm-angle');
    const hudLatch = document.getElementById('hud-latch');
    const hudInsight = document.getElementById('hud-insight');

    // 物理锚点坐标 (依据 SVG)
    const pivotX = 300;
    const pivotY = 550;
    const springGroundX = 500;
    const springGroundY = 700;
    const springLocalX = 200; // 弹簧连接点在摇臂上的局部 X
    
    // 缓动函数
    function easeInOutSine(x) { return -(Math.cos(Math.PI * x) - 1) / 2; }
    function easeInQuad(x) { return x * x; }
    function easeOutBounce(x) {
        const n1 = 7.5625; const d1 = 2.75;
        if (x < 1 / d1) { return n1 * x * x; } 
        else if (x < 2 / d1) { return n1 * (x -= 1.5 / d1) * x + 0.75; } 
        else if (x < 2.5 / d1) { return n1 * (x -= 2.25 / d1) * x + 0.9375; } 
        else { return n1 * (x -= 2.625 / d1) * x + 0.984375; }
    }

    // 绘制锯齿状弹簧
    function drawSpring(x1, y1, x2, y2, coils = 12) {
        const dx = x2 - x1;
        const dy = y2 - y1;
        const dist = Math.sqrt(dx*dx + dy*dy);
        const angle = Math.atan2(dy, dx);
        
        const width = 15;
        const step = dist / coils;
        
        let path = `M ${x1} ${y1} `;
        
        for (let i = 1; i <= coils; i++) {
            const curDist = i * step;
            const curDistPrev = (i - 0.5) * step;
            
            // 垂直于弹簧方向的偏移
            const perpX = Math.cos(angle + Math.PI/2) * (i%2===0 ? width : -width);
            const perpY = Math.sin(angle + Math.PI/2) * (i%2===0 ? width : -width);
            
            const px = x1 + Math.cos(angle) * curDistPrev + perpX;
            const py = y1 + Math.sin(angle) * curDistPrev + perpY;
            
            const nx = x1 + Math.cos(angle) * curDist;
            const ny = y1 + Math.sin(angle) * curDist;
            
            path += `L ${px} ${py} L ${nx} ${ny} `;
        }
        return path;
    }

    let startTime = performance.now();

    function renderFrame(time) {
        let elapsed = (time - startTime) % duration;
        let t = elapsed / duration; // 0.0 到 1.0

        let camAngle = 0;
        let armAngle = 0;
        let latchPos = 0; // 0: 缩回, 1: 伸出
        let latchColor = 'var(--accent-amber)';
        let bufferSquish = 0;
        
        let phaseName = "";
        let insightMsg = "";

        // 状态机逻辑
        // 0.00 - 0.05: 待机
        if (t < 0.05) {
            phaseName = "IDLE";
            insightMsg = "SYSTEM READY - WAITING FOR LIFT COMMAND";
            camAngle = 0;
            armAngle = 0;
        }
        // 0.05 - 0.35: 顶升阶段 (凸轮推摇臂至 -90度)
        else if (t < 0.35) {
            let p = (t - 0.05) / 0.30;
            p = easeInOutSine(p);
            phaseName = "LIFTING (ENERGY STORE)";
            insightMsg = "MOTOR DRIVES CAM, CONVERTING KINETIC TO POTENTIAL ENERGY";
            camAngle = p * 180;
            armAngle = p * -90;
        }
        // 0.35 - 0.40: 锁扣卡死
        else if (t < 0.40) {
            let p = (t - 0.35) / 0.05;
            phaseName = "LOCKING";
            insightMsg = "LATCH ENGAGES. POTENTIAL ENERGY IS SECURED.";
            camAngle = 180;
            armAngle = -90;
            latchPos = p;
            latchColor = 'var(--accent-green)';
        }
        // 0.40 - 0.65: 电机继续旋转至避让位 (IFR 核心)
        else if (t < 0.65) {
            let p = (t - 0.40) / 0.25;
            p = easeInOutSine(p);
            phaseName = "MOTOR CLEARANCE (IFR)";
            insightMsg = "CRITICAL: MOTOR MOVES AWAY. 0 DRAG PATH CLEARED.";
            camAngle = 180 + p * 180;
            armAngle = -90;
            latchPos = 1;
            latchColor = 'var(--accent-green)';
        }
        // 0.65 - 0.70: 准备释放
        else if (t < 0.70) {
            phaseName = "HOLDING / ARMED";
            insightMsg = "IFR STATE ACHIEVED. WAITING FOR DROP SIGNAL.";
            camAngle = 360;
            armAngle = -90;
            latchPos = 1;
            latchColor = 'var(--accent-green)';
        }
        // 0.70 - 0.72: 电磁铁失电松开
        else if (t < 0.72) {
            let p = (t - 0.70) / 0.02;
            phaseName = "RELEASE TRIGGERED";
            insightMsg = "LATCH RELEASES WITH MINIMAL CONTROL ENERGY.";
            camAngle = 360;
            armAngle = -90;
            latchPos = 1 - p;
            latchColor = 'var(--accent-red)';
        }
        // 0.72 - 0.76: 瞬间无阻力坠落
        else if (t < 0.76) {
            let p = (t - 0.72) / 0.04;
            p = easeInQuad(p);
            phaseName = "FREE-FALL (ZERO DRAG)";
            insightMsg = "PURE GRAVITY + SPRING FORCE. NO MOTOR FRICTION.";
            camAngle = 360;
            armAngle = -90 + p * 90;
            latchPos = 0;
            latchColor = 'var(--accent-red)';
        }
        // 0.76 - 0.85: 液压缓冲
        else if (t < 0.85) {
            let p = (t - 0.76) / 0.09;
            p = easeOutBounce(p);
            phaseName = "ENERGY ABSORPTION";
            insightMsg = "HYDRAULIC BUFFER ABSORBS KINETIC IMPACT.";
            camAngle = 360;
            // 模拟反弹
            let bounce = Math.sin(p * Math.PI) * (1-p) * -5;
            armAngle = bounce;
            bufferSquish = Math.sin(p * Math.PI) * (1-p) * 20;
            latchPos = 0;
            latchColor = 'var(--accent-amber)';
        }
        // 0.85 - 1.00: 稳定与重置
        else {
            phaseName = "CYCLE COMPLETE";
            insightMsg = "SYSTEM RESET TO INITIAL STATE.";
            camAngle = 360;
            armAngle = 0;
            latchPos = 0;
            latchColor = 'var(--accent-amber)';
        }

        // --- DOM 更新 ---
        
        // 1. 旋转更新
        elCam.setAttribute('transform', `rotate(${camAngle})`);
        elArm.setAttribute('transform', `rotate(${armAngle})`);
        
        // 2. 锁扣更新 (向右伸出 30px)
        elLatchPin.setAttribute('transform', `translate(${latchPos * 25}, 0)`);
        elLatchLight.setAttribute('fill', latchColor);
        
        // 3. 缓冲器更新
        elBufferPiston.setAttribute('y', -30 + bufferSquish);

        // 4. 计算弹簧连接点世界坐标并绘制弹簧
        // 摇臂初始水平时,连接点在相对于枢轴 dx = 200, dy = 0
        const armRad = armAngle * Math.PI / 180;
        const jx = pivotX + springLocalX * Math.cos(armRad);
        const jy = pivotY + springLocalX * Math.sin(armRad);
        
        elSpringJoint.setAttribute('cx', jx);
        elSpringJoint.setAttribute('cy', jy);
        elSpringPath.setAttribute('d', drawSpring(springGroundX, springGroundY, jx, jy));
        
        // 根据弹簧拉伸长度调整颜色亮度 (物理张力可视化)
        const currentLen = Math.sqrt((jx-springGroundX)**2 + (jy-springGroundY)**2);
        const minLen = Math.sqrt((pivotX+springLocalX-springGroundX)**2 + (pivotY-springGroundY)**2);
        const tension = Math.min(1, Math.max(0, (currentLen - minLen) / 250));
        elSpringPath.setAttribute('stroke', `rgb(245, ${158 - tension*100}, ${11 + tension*50})`);

        // 5. IFR 视觉引导特效 (当电机离开,突出展示下方无阻挡)
        if (t > 0.45 && t < 0.70) {
            elIfrIndicator.setAttribute('opacity', Math.min(1, (t - 0.45) * 5));
            elIfrZone.setAttribute('fill', 'rgba(16, 185, 129, 0.1)'); // 绿色幽光
        } else {
            elIfrIndicator.setAttribute('opacity', 0);
            elIfrZone.setAttribute('fill', 'rgba(14, 165, 233, 0)');
        }

        // 6. HUD 文字更新
        hudPhase.textContent = phaseName;
        hudInsight.textContent = insightMsg;
        hudCamAngle.textContent = (camAngle % 360).toFixed(1) + '°';
        hudArmAngle.textContent = armAngle.toFixed(1) + '°';
        
        if (latchPos === 1) {
            hudLatch.textContent = "LOCKED (SECURE)";
            hudLatch.setAttribute('fill', 'var(--accent-green)');
        } else if (latchPos === 0) {
            hudLatch.textContent = "DISENGAGED";
            hudLatch.setAttribute('fill', 'var(--accent-amber)');
        } else {
            hudLatch.textContent = "ACTUATING...";
            hudLatch.setAttribute('fill', 'var(--accent-red)');
        }

        requestAnimationFrame(renderFrame);
    }

    // 自动触发循环动画 (重开即播,符合要求)
    requestAnimationFrame(renderFrame);
</script>
</body>
</html>
积分规则:第一轮对话扣减8分,后续每轮扣6分