分享图
动画工坊
引擎就绪

以下是完全基于原生 HTML/CSS/SVG 和 JavaScript 构建的“杠杆尺蠖”爬楼小车原理动画代码。

该设计采用了**高科技蓝图与赛博工程(Technical Blueprint & Cyber Engineering)**的美学风格,通过深邃的暗色背景与高对比度的发光线条,将用户的视觉核心聚焦于机械运动的本质。设计完全遵循了 TRIZ 中的 “最终理想解 (IFR)” 思想:通过让阶梯“倒退”的相对运动巧妙实现无限循环,去除了杂乱的环境干扰,直观揭示了轮足协同、V形脚垫锁定和杠杆姿态补偿的精巧原理。

动画全自动播放,文字说明已被极其克制地缩小并安置在四角,确保中央机械运作区域占据绝对的视觉主导。

<!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-color: #050811;
            --grid-color: rgba(0, 150, 255, 0.08);
            --stair-stroke: #1b273d;
            --stair-edge: #00f0ff;
            --car-main: #2a3547;
            --car-accent: #00f0ff;
            --leg-sleeve: #475a7a;
            --leg-rod: #e2f1ff;
            --force-active: #ff3b00;
            --text-color: #7b9abf;
            --hud-bg: rgba(5, 8, 17, 0.6);
        }

        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: 30px 30px;
            background-position: center center;
            overflow: hidden;
            font-family: 'Consolas', 'Courier New', monospace;
            display: flex;
            justify-content: center;
            align-items: center;
        }

        #animation-container {
            position: relative;
            width: 100%;
            height: 100%;
            display: flex;
            justify-content: center;
            align-items: center;
        }

        svg {
            width: 100%;
            height: 100%;
            max-width: 1200px;
            max-height: 900px;
            overflow: visible;
        }

        /* 极简且不遮挡画面的 HUD 信息面板 */
        .hud-panel {
            position: absolute;
            background: var(--hud-bg);
            border: 1px solid rgba(0, 240, 255, 0.2);
            padding: 12px 16px;
            border-radius: 4px;
            color: var(--text-color);
            font-size: 12px;
            line-height: 1.6;
            backdrop-filter: blur(8px);
            box-shadow: 0 4px 20px rgba(0,0,0,0.5);
            pointer-events: none;
            z-index: 10;
        }

        #hud-top-left { top: 30px; left: 30px; }
        #hud-top-right { top: 30px; right: 30px; text-align: right; }
        #hud-bottom-right { bottom: 30px; right: 30px; text-align: right; }
        #hud-bottom-left { bottom: 30px; left: 30px; }

        .hud-title {
            color: var(--car-accent);
            font-weight: bold;
            font-size: 14px;
            margin-bottom: 6px;
            text-transform: uppercase;
            letter-spacing: 1px;
        }

        .data-value {
            color: #fff;
            font-weight: bold;
            text-shadow: 0 0 5px var(--car-accent);
        }

        .force-value {
            color: var(--force-active);
            text-shadow: 0 0 5px var(--force-active);
        }

        /* SVG 样式与滤镜 */
        .glow { filter: drop-shadow(0 0 8px var(--car-accent)); }
        .glow-red { filter: drop-shadow(0 0 8px var(--force-active)); }
        
        .stair-line {
            fill: none;
            stroke: var(--stair-stroke);
            stroke-width: 4;
            stroke-linejoin: round;
        }
        
        .stair-edge-highlight {
            fill: none;
            stroke: var(--stair-edge);
            stroke-width: 2;
            stroke-dasharray: 4 4;
            opacity: 0.5;
        }

        .mech-body {
            fill: var(--car-main);
            stroke: var(--car-accent);
            stroke-width: 1.5;
        }

        .wheel {
            fill: #111a28;
            stroke: #475a7a;
            stroke-width: 3;
        }

        .active-point {
            fill: var(--bg-color);
            stroke: var(--force-active);
            stroke-width: 2;
            transition: all 0.1s;
        }
    </style>
</head>
<body>

    <div id="animation-container">
        <!-- 信息面板 -->
        <div class="hud-panel" id="hud-top-left">
            <div class="hud-title">System.IFR_Status</div>
            <div>系统模态: <span class="data-value" id="val-phase">初始化</span></div>
            <div>爬升周期: <span class="data-value">1.5s / 级</span></div>
            <div>支撑策略: <span class="data-value" id="val-support">轮足协同</span></div>
        </div>

        <div class="hud-panel" id="hud-top-right">
            <div class="hud-title">Kinematics.Data</div>
            <div>主梁俯仰角(Pitch): <span class="data-value" id="val-pitch">0.0°</span></div>
            <div>平台姿态补偿: <span class="data-value" id="val-comp">0.0°</span></div>
            <div>重心偏移(Y): <span class="data-value" id="val-cg">0 mm</span></div>
        </div>
        
        <div class="hud-panel" id="hud-bottom-left">
            <div class="hud-title">TRIZ_Principle</div>
            <div style="color: #fff">>> 最终理想解 (IFR)</div>
            <div style="opacity: 0.8">消除问题后的绝对工作态<br>无多余步骤,直接展示核心杠杆解构</div>
            <div style="color: #fff; margin-top: 5px;">>> 嵌套/杠杆/局部质量</div>
            <div style="opacity: 0.8">平地用轮,楼梯用足杠杆支撑</div>
        </div>

        <div class="hud-panel" id="hud-bottom-right">
            <div class="hud-title">Force.Feedback</div>
            <div>前足拉升张力: <span class="force-value" id="val-force-f">0 N</span></div>
            <div>后足支撑推力: <span class="force-value" id="val-force-r">0 N</span></div>
            <div>锁定结构: <span class="data-value">V形倒钩接触副</span></div>
        </div>

        <!-- 核心 SVG 动画区 -->
        <svg viewBox="-300 -350 600 700" preserveAspectRatio="xMidYMid meet">
            <!-- 滤镜定义 -->
            <defs>
                <radialGradient id="hub-grad" cx="50%" cy="50%" r="50%">
                    <stop offset="0%" stop-color="#00f0ff" stop-opacity="0.8"/>
                    <stop offset="100%" stop-color="#00f0ff" stop-opacity="0"/>
                </radialGradient>
                <linearGradient id="cargo-grad" x1="0%" y1="0%" x2="0%" y2="100%">
                    <stop offset="0%" stop-color="#00f0ff" stop-opacity="0.3"/>
                    <stop offset="100%" stop-color="#00f0ff" stop-opacity="0.05"/>
                </linearGradient>
            </defs>

            <!-- 场景容器:阶梯 -->
            <g id="stairs-group">
                <path id="stair-path" class="stair-line" d=""></path>
                <path id="stair-highlight" class="stair-edge-highlight" d=""></path>
                <!-- 动态生成的棱角高亮光圈 -->
                <g id="edge-nodes"></g>
            </g>

            <!-- 车辆连杆机构与足 (世界坐标系下动态绘制,解耦层级防止旋转嵌套) -->
            <g id="linkages">
                <!-- 后腿 -->
                <line id="r-leg-sleeve" stroke="var(--leg-sleeve)" stroke-width="14" stroke-linecap="round"/>
                <line id="r-leg-rod" stroke="var(--leg-rod)" stroke-width="6" stroke-linecap="round"/>
                <!-- 后脚垫 -->
                <path id="r-pad" d="M -15,0 L 15,0" fill="none" stroke="var(--car-accent)" stroke-width="4" stroke-linecap="round"/>
                
                <!-- 前腿 -->
                <line id="f-leg-sleeve" stroke="var(--leg-sleeve)" stroke-width="14" stroke-linecap="round"/>
                <line id="f-leg-rod" stroke="var(--leg-rod)" stroke-width="6" stroke-linecap="round"/>
                <!-- 前脚垫 (V形倒钩) -->
                <path id="f-pad" d="M -18,2 L 4,2 L 4,20" fill="none" stroke="var(--force-active)" stroke-width="4" stroke-linejoin="round" class="glow-red"/>
            </g>

            <!-- 车体组 (含中心补偿机构与货物) -->
            <g id="car-group">
                <!-- 中部轮组 (4轮,前后重叠展示为2大轮) -->
                <g id="wheels" transform="translate(0, 30)">
                    <circle cx="-60" cy="0" r="30" class="wheel"/>
                    <circle cx="60" cy="0" r="30" class="wheel"/>
                    <circle cx="-60" cy="0" r="10" fill="#2a3547" stroke="#00f0ff" stroke-width="1"/>
                    <circle cx="60" cy="0" r="10" fill="#2a3547" stroke="#00f0ff" stroke-width="1"/>
                </g>

                <!-- 主梁梁体 -->
                <path class="mech-body glow" d="M -150,-10 L 150,-10 L 160,10 L 130,30 L -130,30 L -160,10 Z"/>
                
                <!-- 前后铰接点 -->
                <circle cx="-130" cy="15" r="5" fill="#00f0ff"/>
                <circle cx="130" cy="15" r="5" fill="#00f0ff"/>

                <!-- 平行四边形悬挂补偿机构 (与主梁发生相对运动) -->
                <g id="suspension-group">
                    <!-- 主支撑柱 -->
                    <rect id="center-pillar" x="-10" y="-140" width="20" height="130" fill="var(--car-main)" stroke="var(--car-accent)" stroke-width="1.5"/>
                    <!-- 平行连杆 -->
                    <line id="parallel-rod" x1="-25" y1="-10" x2="-25" y2="-130" stroke="var(--force-active)" stroke-width="3"/>
                    <!-- 旋转节点 -->
                    <circle cx="0" cy="-10" r="6" class="active-point"/>
                    <circle cx="-25" cy="-10" r="4" fill="#ff3b00"/>
                    <circle cx="0" cy="-140" r="6" class="active-point"/>
                    <circle cx="-25" cy="-130" r="4" fill="#ff3b00"/>
                </g>
                
                <!-- 货物平台 (始终保持水平绝对姿态,通过代码逆向旋转抵消主梁倾角) -->
                <g id="cargo-platform">
                    <!-- 平台底座 -->
                    <rect x="-90" y="-155" width="180" height="15" rx="3" class="mech-body"/>
                    <!-- 抽象货物框 -->
                    <rect x="-80" y="-235" width="160" height="80" rx="4" fill="url(#cargo-grad)" stroke="#00f0ff" stroke-width="1" stroke-dasharray="8 4"/>
                    <text x="0" y="-190" fill="#00f0ff" font-size="14" text-anchor="middle" opacity="0.6" font-family="sans-serif">CARGO PAYLOAD</text>
                </g>
            </g>

            <!-- 动力传递特效线 -->
            <path id="force-line" fill="none" stroke="var(--force-active)" stroke-width="2" stroke-dasharray="4 8" opacity="0"/>
        </svg>
    </div>

    <script>
        document.addEventListener('DOMContentLoaded', () => {
            // == 系统与几何参数配置 ==
            const STAIR_W = 220;  // 台阶深度
            const STAIR_H = 160;  // 台阶高度
            const CAR_BASE_X = 110; // 车辆在画面中的恒定X轴参考点
            const CAR_BASE_Y = -180; // 车辆的基础Y高度
            
            // 小车主梁上前/后腿安装点 (相对车体中心)
            const LEG_BASE_OFFSET = { front: {x: 130, y: 15}, rear: {x: -130, y: 15} };
            
            // 动画周期配置 (毫秒)
            const PHASE_PULL_DUR = 1800; // Phase 0: 杠杆拉升阶段 (占主体)
            const PHASE_STEP_DUR = 1200; // Phase 1: 空中换步阶段
            const TOTAL_DUR = PHASE_PULL_DUR + PHASE_STEP_DUR;

            // DOM 元素引用
            const els = {
                stairsGrp: document.getElementById('stairs-group'),
                stairPath: document.getElementById('stair-path'),
                stairHi: document.getElementById('stair-highlight'),
                edgeNodes: document.getElementById('edge-nodes'),
                carGrp: document.getElementById('car-group'),
                cargoPlat: document.getElementById('cargo-platform'),
                centerPillar: document.getElementById('center-pillar'),
                parallelRod: document.getElementById('parallel-rod'),
                
                fSleeve: document.getElementById('f-leg-sleeve'),
                fRod: document.getElementById('f-leg-rod'),
                fPad: document.getElementById('f-pad'),
                
                rSleeve: document.getElementById('r-leg-sleeve'),
                rRod: document.getElementById('r-leg-rod'),
                rPad: document.getElementById('r-pad'),

                forceLine: document.getElementById('force-line'),
                
                // HUD
                valPhase: document.getElementById('val-phase'),
                valSupport: document.getElementById('val-support'),
                valPitch: document.getElementById('val-pitch'),
                valComp: document.getElementById('val-comp'),
                valCg: document.getElementById('val-cg'),
                valForceF: document.getElementById('val-force-f'),
                valForceR: document.getElementById('val-force-r')
            };

            // == 静态背景阶梯生成 (生成足够多的台阶以支持位移无缝衔接) ==
            function initStairs() {
                let d = '', dHi = '';
                // 绘制 -3 到 +3 级台阶
                for(let i = -3; i <= 3; i++) {
                    const x = i * STAIR_W;
                    const y = -i * STAIR_H;
                    if (i === -3) {
                        d += `M ${x},${y+STAIR_H} L ${x},${y} L ${x+STAIR_W},${y}`;
                        dHi += `M ${x},${y} L ${x+STAIR_W},${y}`;
                    } else {
                        d += ` L ${x},${y} L ${x+STAIR_W},${y}`;
                        dHi += ` M ${x},${y} L ${x+STAIR_W},${y}`;
                    }
                    
                    // 添加棱角发光点
                    const circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
                    circle.setAttribute("cx", x);
                    circle.setAttribute("cy", y);
                    circle.setAttribute("r", 6);
                    circle.setAttribute("fill", "url(#hub-grad)");
                    els.edgeNodes.appendChild(circle);
                }
                els.stairPath.setAttribute("d", d + " L 900,1000 L -900,1000 Z"); // 闭合填充背景
                els.stairPath.style.fill = "#0a0f1c";
                els.stairHi.setAttribute("d", dHi);
            }
            initStairs();

            // == 缓动函数 ==
            const easeInOutQuad = t => t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
            const easeOutBack = (t) => {
                const c1 = 1.70158; const c3 = c1 + 1;
                return 1 + c3 * Math.pow(t - 1, 3) + c1 * Math.pow(t - 1, 2);
            };

            // == 核心循环状态机 ==
            let startTime = performance.now();

            function renderFrame(now) {
                // 时间归一化
                let elapsed = (now - startTime) % TOTAL_DUR;
                let phase = elapsed < PHASE_PULL_DUR ? 0 : 1;
                let p = phase === 0 
                    ? elapsed / PHASE_PULL_DUR 
                    : (elapsed - PHASE_PULL_DUR) / PHASE_STEP_DUR;

                // 物理与几何变量
                let stX = 0, stY = 0; // 阶梯整体平移
                let carY = CAR_BASE_Y; 
                let carPitch = 0; // 主梁仰角 (度)
                
                let fTipX, fTipY; // 前足端点世界坐标
                let rTipX, rTipY; // 后足端点世界坐标

                // 定义局部坐标系中的台阶基准点
                // 台阶 0 位于 (0,0);台阶 1 位于 (STAIR_W, -STAIR_H)
                const S_M1 = { x: -STAIR_W, y: STAIR_H };
                const S_0  = { x: 0, y: 0 };
                const S_1  = { x: STAIR_W, y: -STAIR_H };

                if (phase === 0) {
                    // === 阶段 0: 杠杆拉升 (小车在原地,阶梯向左下后退) ===
                    let ep = easeInOutQuad(p);
                    
                    stX = -STAIR_W * ep;
                    stY = STAIR_H * ep;
                    
                    // 前足死锁在 S_1 的棱角
                    fTipX = S_1.x + stX;
                    fTipY = S_1.y + stY;
                    
                    // 后足死锁在 S_0 的平面上
                    rTipX = S_0.x + stX;
                    rTipY = S_0.y + stY;

                    // 表现杠杆抬升:中间产生一个拱起的位移和仰角
                    // 模拟后腿顶起车尾,前腿拉拽
                    carPitch = -12 * Math.sin(ep * Math.PI); // 最大上仰 12 度
                    carY = CAR_BASE_Y - 45 * Math.sin(ep * Math.PI); // 重心最多抬高 45mm
                    
                    // UI 更新
                    els.valPhase.textContent = "杠杆拉升 (Leverage Pull)";
                    els.valPhase.style.color = "var(--force-active)";
                    els.valSupport.textContent = "全足锁死跨越";
                    els.valForceF.textContent = Math.round(150 + 100 * Math.sin(ep * Math.PI)) + " N";
                    els.valForceR.textContent = Math.round(200 + 150 * Math.sin(ep * Math.PI)) + " N";
                    els.fPad.classList.add('glow-red');
                    els.forceLine.style.opacity = 0.6;
                    
                } else {
                    // === 阶段 1: 双足换步 (阶梯瞬间复位并静止,双腿在空中跨越) ===
                    let ep = easeInOutQuad(p);
                    
                    // 瞬间复位带来的逻辑坐标变化:
                    // 原本的 S_1 变成了现在的 S_0。
                    // 前脚需要从 S_0 跨向新的 S_1。
                    // 后脚需要从 S_M1 跨向新的 S_0。
                    
                    // 前腿跨步轨迹 (抛物线)
                    fTipX = S_0.x + (S_1.x - S_0.x) * ep;
                    fTipY = S_0.y + (S_1.y - S_0.y) * ep - 80 * Math.sin(ep * Math.PI);
                    
                    // 后腿跨步轨迹
                    rTipX = S_M1.x + (S_0.x - S_M1.x) * ep;
                    rTipY = S_M1.y + (S_0.y - S_M1.y) * ep - 60 * Math.sin(ep * Math.PI);

                    carPitch = 0;
                    carY = CAR_BASE_Y;

                    // UI 更新
                    els.valPhase.textContent = "双足换步 (Step Reset)";
                    els.valPhase.style.color = "var(--car-accent)";
                    els.valSupport.textContent = "中部轮组过渡";
                    els.valForceF.textContent = "0 N";
                    els.valForceR.textContent = "0 N";
                    els.fPad.classList.remove('glow-red');
                    els.forceLine.style.opacity = 0;
                }

                // --- 渲染更新 ---

                // 1. 更新台阶层容器平移
                els.stairsGrp.setAttribute('transform', `translate(${stX}, ${stY})`);

                // 2. 更新车体组的整体位移与旋转
                // 由于车体中心是 CAR_BASE_X, carY,绕自身旋转
                els.carGrp.setAttribute('transform', `translate(${CAR_BASE_X}, ${carY}) rotate(${carPitch})`);

                // 3. 计算车体上安装点的绝对世界坐标
                // 旋转矩阵公式: X' = x*cos - y*sin, Y' = x*sin + y*cos
                const rad = carPitch * Math.PI / 180;
                const cosP = Math.cos(rad);
                const sinP = Math.sin(rad);

                const getWldPos = (offset) => {
                    return {
                        x: CAR_BASE_X + offset.x * cosP - offset.y * sinP,
                        y: carY + offset.x * sinP + offset.y * cosP
                    };
                };

                let fBaseWld = getWldPos(LEG_BASE_OFFSET.front);
                let rBaseWld = getWldPos(LEG_BASE_OFFSET.rear);

                // 4. 绘制推杆 (腿部)
                const updateLeg = (baseWld, tipX, tipY, elSleeve, elRod) => {
                    const dx = tipX - baseWld.x;
                    const dy = tipY - baseWld.y;
                    const dist = Math.hypot(dx, dy);
                    const dirX = dx / dist;
                    const dirY = dy / dist;
                    
                    // 套筒固定长度 70
                    const sleeveLen = 70;
                    const midX = baseWld.x + dirX * sleeveLen;
                    const midY = baseWld.y + dirY * sleeveLen;

                    elSleeve.setAttribute('x1', baseWld.x);
                    elSleeve.setAttribute('y1', baseWld.y);
                    elSleeve.setAttribute('x2', midX);
                    elSleeve.setAttribute('y2', midY);

                    elRod.setAttribute('x1', midX);
                    elRod.setAttribute('y1', midY);
                    elRod.setAttribute('x2', tipX);
                    elRod.setAttribute('y2', tipY);
                };

                updateLeg(fBaseWld, fTipX, fTipY, els.fSleeve, els.fRod);
                updateLeg(rBaseWld, rTipX, rTipY, els.rSleeve, els.rRod);

                // 5. 更新脚垫位置 (V形倒钩和平面垫)
                els.fPad.setAttribute('transform', `translate(${fTipX}, ${fTipY})`);
                els.rPad.setAttribute('transform', `translate(${rTipX}, ${rTipY})`);

                // 6. 悬挂补偿与货物平台反向旋转保持水平
                // 平台位于 carGrp 内,它继承了 carPitch 旋转。
                // 为了让它保持绝对水平,需要相对于它自身的中心进行负 carPitch 的旋转!
                els.cargoPlat.setAttribute('transform', `rotate(${-carPitch}, 0, -150)`);
                
                // 调整悬挂柱子 (中心柱保持刚性竖直方向)
                // 表现形式:视觉补偿。立柱本身在 carGrp 下,反转它。
                els.centerPillar.setAttribute('transform', `rotate(${-carPitch}, 0, -10)`);
                // 平行副连杆也要反转
                els.parallelRod.setAttribute('transform', `rotate(${-carPitch}, 0, -10)`);

                // 绘制一根表示力矩传递的虚线 (从前脚钩连接到车体重心)
                els.forceLine.setAttribute('d', `M ${fTipX},${fTipY} L ${CAR_BASE_X},${carY}`);

                // 7. 更新 HUD 数据
                els.valPitch.textContent = carPitch.toFixed(1) + "°";
                els.valComp.textContent = (-carPitch).toFixed(1) + "°";
                els.valCg.textContent = (carY - CAR_BASE_Y).toFixed(0) + " mm";

                requestAnimationFrame(renderFrame);
            }

            // 启动动画循环
            requestAnimationFrame(renderFrame);
        });
    </script>
</body>
</html>
积分规则:每轮对话扣10分