分享图
动画工坊
引擎就绪
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>TRIZ 理想最终解 - 多段式柔性底盘</title>
    <style>
        @import url('https://fonts.googleapis.com/css2?family=Share+Tech+Mono&family=Syncopate:wght@400;700&display=swap');

        :root {
            --bg-color: #08080c;
            --grid-color: #1a1a24;
            --stair-fill: #151520;
            --stair-edge: #2dd4bf;
            --neon-cyan: #00f0ff;
            --neon-orange: #ff5500;
            --cabin-metal: #242430;
            --text-main: #e2e8f0;
            --text-muted: #64748b;
        }

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

        /* 核心动画容器 */
        #animation-container {
            position: relative;
            width: 100%;
            max-width: 1400px;
            height: 100%;
            max-height: 900px;
            display: flex;
            justify-content: center;
            align-items: center;
            box-shadow: inset 0 0 100px rgba(0, 240, 255, 0.05);
        }

        svg {
            width: 100%;
            height: 100%;
            /* 保证在各种屏幕比例下居中显示且不被裁剪 */
            object-fit: contain; 
        }

        /* UI 覆盖层 (HUD) */
        .hud-panel {
            position: absolute;
            background: rgba(10, 10, 15, 0.85);
            border: 1px solid rgba(0, 240, 255, 0.3);
            backdrop-filter: blur(10px);
            padding: 20px;
            border-radius: 4px;
            box-shadow: 0 4px 30px rgba(0, 0, 0, 0.5), inset 0 0 20px rgba(0, 240, 255, 0.1);
            pointer-events: auto;
        }

        #title-panel {
            top: 30px;
            left: 30px;
            max-width: 450px;
        }

        #title-panel h1 {
            font-family: 'Syncopate', sans-serif;
            font-size: 20px;
            color: var(--neon-cyan);
            margin: 0 0 10px 0;
            text-transform: uppercase;
            letter-spacing: 2px;
            text-shadow: 0 0 10px rgba(0, 240, 255, 0.5);
        }

        #title-panel h2 {
            font-size: 14px;
            color: var(--neon-orange);
            margin: 0 0 15px 0;
            text-transform: uppercase;
        }

        .desc-text {
            font-size: 13px;
            color: var(--text-muted);
            line-height: 1.6;
            margin-bottom: 10px;
        }

        .highlight-text {
            color: #fff;
            border-bottom: 1px solid var(--neon-cyan);
        }

        #control-panel {
            bottom: 30px;
            right: 30px;
            width: 300px;
        }

        .control-group {
            margin-bottom: 15px;
        }
        
        .control-group:last-child {
            margin-bottom: 0;
        }

        .control-label {
            display: flex;
            justify-content: space-between;
            font-size: 12px;
            color: var(--neon-cyan);
            margin-bottom: 8px;
            text-transform: uppercase;
        }

        input[type=range] {
            -webkit-appearance: none;
            width: 100%;
            background: transparent;
        }

        input[type=range]::-webkit-slider-thumb {
            -webkit-appearance: none;
            height: 16px;
            width: 8px;
            background: var(--neon-orange);
            cursor: pointer;
            margin-top: -6px;
            box-shadow: 0 0 10px var(--neon-orange);
        }

        input[type=range]::-webkit-slider-runnable-track {
            width: 100%;
            height: 4px;
            cursor: pointer;
            background: rgba(255,255,255,0.1);
            border-radius: 2px;
        }

        /* 实时数据监控面板 */
        #telemetry-panel {
            top: 30px;
            right: 30px;
            width: 250px;
        }

        .data-row {
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding: 8px 0;
            border-bottom: 1px dashed rgba(255,255,255,0.1);
        }

        .data-row:last-child { border-bottom: none; }
        
        .data-label { color: var(--text-muted); font-size: 12px; }
        .data-value { color: var(--neon-cyan); font-size: 16px; font-weight: bold; font-family: 'Share Tech Mono', monospace;}
        .data-value.warning { color: var(--neon-orange); }

        /* SVG 样式 */
        .track-base {
            fill: none;
            stroke: #111;
            stroke-width: 66;
            stroke-linecap: round;
            stroke-linejoin: round;
            filter: drop-shadow(0 10px 15px rgba(0,0,0,0.8));
        }

        .track-tread {
            fill: none;
            stroke: #2a2a35;
            stroke-width: 60;
            stroke-linecap: round;
            stroke-linejoin: round;
        }

        .track-grip {
            fill: none;
            stroke: var(--neon-orange);
            stroke-width: 64;
            stroke-linecap: round;
            stroke-linejoin: round;
            stroke-dasharray: 8 24;
            opacity: 0.9;
            /* Dash offset 会由 JS 动态控制以展示履带卷动 */
        }

        .stair-visual {
            fill: url(#hatch-pattern);
            stroke: var(--stair-edge);
            stroke-width: 3;
            filter: drop-shadow(0 0 8px rgba(45, 212, 191, 0.3));
        }
        
        /* 隐藏用于运动学计算的虚拟平滑路径 */
        .kinematic-path {
            fill: none;
            stroke: red;
            stroke-width: 2;
            opacity: 0; 
        }

        .vector-arrow {
            transition: opacity 0.2s ease;
        }
    </style>
</head>
<body>

<div id="animation-container">
    <!-- SVG 动画主体 -->
    <svg viewBox="0 0 1600 900" id="main-svg" preserveAspectRatio="xMidYMid meet">
        <defs>
            <!-- 背景网格 -->
            <pattern id="bg-grid" 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>
            
            <!-- 台阶填充纹理 -->
            <pattern id="hatch-pattern" width="10" height="10" patternTransform="rotate(45 0 0)" patternUnits="userSpaceOnUse">
                <rect width="10" height="10" fill="var(--stair-fill)"/>
                <line x1="0" y1="0" x2="0" y2="10" stroke="rgba(255,255,255,0.03)" stroke-width="2"/>
            </pattern>

            <!-- 发光滤镜 -->
            <filter id="neon-glow" x="-50%" y="-50%" width="200%" height="200%">
                <feGaussianBlur in="SourceGraphic" stdDeviation="8" result="blur1" />
                <feGaussianBlur in="SourceGraphic" stdDeviation="15" result="blur2" />
                <feMerge>
                    <feMergeNode in="blur2" />
                    <feMergeNode in="blur1" />
                    <feMergeNode in="SourceGraphic" />
                </feMerge>
            </filter>

            <!-- 机舱组件渐变 -->
            <linearGradient id="metal-grad" x1="0%" y1="0%" x2="0%" y2="100%">
                <stop offset="0%" stop-color="#3f3f5a"/>
                <stop offset="100%" stop-color="#1a1a24"/>
            </linearGradient>

            <!-- 箭头标记 -->
            <marker id="arrowhead" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
                <polygon points="0 0, 10 3.5, 0 7" fill="#00f0ff" />
            </marker>
        </defs>

        <!-- 渲染背景 -->
        <rect width="100%" height="100%" fill="url(#bg-grid)" />

        <g transform="translate(100, 150)">
            <!-- 视觉渲染用的真实不规则台阶轮廓 -->
            <!-- 包含了直角、锐利突起和不规则斜坡,以验证失效边界与理想状态 -->
            <path class="stair-visual" d="M -200 600 L 300 600 L 300 450 L 550 450 L 650 300 L 800 300 L 800 250 L 900 250 L 900 100 L 1600 100 L 1600 800 L -200 800 Z" />

            <!-- 用于运动学约束的隐藏平滑路径 (底盘中心轨迹) -->
            <!-- 轨迹相比台阶向左上偏移约 35px,并在拐角处做贝塞尔平滑,模拟柔性履带的包络特性 -->
            <path id="kinematic-path" class="kinematic-path" d="
                M -200 565 
                L 265 565 C 265 565, 265 565, 265 520
                L 265 485 C 265 415, 265 415, 335 415
                L 510 415 C 550 415, 550 415, 570 385
                L 615 315 C 635 280, 635 280, 670 265
                L 765 265 C 765 265, 765 265, 765 235
                L 765 215 C 765 215, 765 215, 800 215
                L 865 215 C 865 215, 865 215, 865 185
                L 865 135 C 865 65, 865 65, 935 65
                L 1600 65" />

            <!-- 车辆本体 Group -->
            <g id="vehicle-assembly">
                <!-- 履带底层阴影与主形状 -->
                <path id="track-base" class="track-base" d="" />
                <!-- 履带主体黑色橡胶 -->
                <path id="track-tread" class="track-tread" d="" />
                <!-- 履带外侧防滑纹 (橙色动态卷动) -->
                <path id="track-grip" class="track-grip" d="" />

                <!-- 内部独立驱动舱段 (3个) -->
                <!-- 舱段1 (前) -->
                <g id="cabin-1">
                    <rect x="-35" y="-18" width="70" height="36" rx="6" fill="url(#metal-grad)" stroke="#00f0ff" stroke-width="1.5" opacity="0.9"/>
                    <circle cx="0" cy="0" r="10" fill="none" stroke="#ff5500" stroke-width="2"/>
                    <line x1="-20" y1="0" x2="-10" y2="0" stroke="#00f0ff" stroke-width="2"/>
                    <line x1="20" y1="0" x2="10" y2="0" stroke="#00f0ff" stroke-width="2"/>
                    <text x="0" y="3" fill="#00f0ff" font-size="10" text-anchor="middle" font-family="sans-serif">M1</text>
                </g>
                <!-- 舱段2 (中) -->
                <g id="cabin-2">
                    <rect x="-35" y="-18" width="70" height="36" rx="6" fill="url(#metal-grad)" stroke="#00f0ff" stroke-width="1.5" opacity="0.9"/>
                    <circle cx="0" cy="0" r="10" fill="none" stroke="#ff5500" stroke-width="2"/>
                    <text x="0" y="3" fill="#00f0ff" font-size="10" text-anchor="middle" font-family="sans-serif">M2</text>
                </g>
                <!-- 舱段3 (后) -->
                <g id="cabin-3">
                    <rect x="-35" y="-18" width="70" height="36" rx="6" fill="url(#metal-grad)" stroke="#00f0ff" stroke-width="1.5" opacity="0.9"/>
                    <circle cx="0" cy="0" r="10" fill="none" stroke="#ff5500" stroke-width="2"/>
                    <line x1="-20" y1="0" x2="-10" y2="0" stroke="#00f0ff" stroke-width="2"/>
                    <line x1="20" y1="0" x2="10" y2="0" stroke="#00f0ff" stroke-width="2"/>
                    <text x="0" y="3" fill="#00f0ff" font-size="10" text-anchor="middle" font-family="sans-serif">M3</text>
                </g>

                <!-- 被动铰接关节与发光指示器 (资源利用重点) -->
                <!-- 铰链 12 -->
                <circle id="hinge-12-glow" cx="0" cy="0" r="25" fill="var(--neon-cyan)" filter="url(#neon-glow)" opacity="0" style="transition: opacity 0.1s; pointer-events:none;"/>
                <circle id="hinge-12" cx="0" cy="0" r="5" fill="#fff" stroke="#000" stroke-width="2"/>
                
                <!-- 铰链 23 -->
                <circle id="hinge-23-glow" cx="0" cy="0" r="25" fill="var(--neon-cyan)" filter="url(#neon-glow)" opacity="0" style="transition: opacity 0.1s; pointer-events:none;"/>
                <circle id="hinge-23" cx="0" cy="0" r="5" fill="#fff" stroke="#000" stroke-width="2"/>

                <!-- 动态推力矢量箭头 -->
                <g id="force-vector-front" class="vector-arrow" opacity="0">
                    <line x1="0" y1="-20" x2="0" y2="-60" stroke="#00f0ff" stroke-width="3" marker-end="url(#arrowhead)"/>
                    <text x="5" y="-65" fill="#00f0ff" font-size="12">贴合摩擦力</text>
                </g>
            </g>

            <!-- 场景标注 -->
            <g transform="translate(420, 630)">
                <line x1="0" y1="0" x2="-60" y2="-30" stroke="var(--text-muted)" stroke-dasharray="2 2"/>
                <text x="10" y="5" fill="var(--text-muted)" font-size="12">传统刚性底盘失效点:直角台阶</text>
            </g>
            <g transform="translate(680, 480)">
                <line x1="0" y1="0" x2="-40" y2="-40" stroke="var(--text-muted)" stroke-dasharray="2 2"/>
                <text x="10" y="5" fill="var(--text-muted)" font-size="12">不规则高度差</text>
            </g>
        </g>
    </svg>

    <!-- 文本信息区 -->
    <div id="title-panel" class="hud-panel">
        <h1>TRIZ Ideal Final Result</h1>
        <h2>多段式柔性自适应底盘</h2>
        <div class="desc-text">
            <span class="highlight-text">技术矛盾:</span>传统刚性底盘为保证稳定性,无法适应高度随机的不规则台阶,极易悬空打滑。
        </div>
        <div class="desc-text">
            <span class="highlight-text">解决方案 (化整为零):</span>放弃整体刚性大梁,由 3 个独立驱动舱段组成。利用<strong style="color:var(--neon-cyan)">被动弹簧铰链</strong>与<strong style="color:var(--neon-orange)">高弹性宽体橡胶履带</strong>串联。
        </div>
        <div class="desc-text">
            当车头触及台阶,前端受力向上被动弯折,犹如毛毛虫般“趴”在立面上,巧妙利用现有重力与推力资源完成被动贴合,无需复杂的传感控制。
        </div>
    </div>

    <!-- 遥测数据监控区 -->
    <div id="telemetry-panel" class="hud-panel">
        <h2 style="font-size: 14px; color: #fff; margin: 0 0 15px 0; border-bottom: 1px solid rgba(255,255,255,0.2); padding-bottom: 5px;">状态遥测 / TELEMETRY</h2>
        <div class="data-row">
            <span class="data-label">主推进速度</span>
            <span class="data-value" id="val-speed">1.0 m/s</span>
        </div>
        <div class="data-row">
            <span class="data-label">前部关节形变 (Max 45°)</span>
            <span class="data-value" id="val-angle-front">0.0°</span>
        </div>
        <div class="data-row">
            <span class="data-label">后部关节形变 (Max 45°)</span>
            <span class="data-value" id="val-angle-rear">0.0°</span>
        </div>
        <div class="data-row">
            <span class="data-label">底盘姿态状态</span>
            <span class="data-value" id="val-status">巡航 [刚性]</span>
        </div>
    </div>

    <!-- 控制面板 -->
    <div id="control-panel" class="hud-panel">
        <div class="control-group">
            <div class="control-label">
                <span>驱动电机转速 (Speed)</span>
                <span id="speed-display">1.5x</span>
            </div>
            <input type="range" id="speed-slider" min="0.5" max="3" step="0.1" value="1.5">
        </div>
        <div class="control-group" style="margin-top: 15px;">
            <div class="control-label">
                <span>履带弹性模量 (视觉演示)</span>
            </div>
            <input type="range" id="elastic-slider" min="0" max="100" value="80">
        </div>
    </div>
</div>

<script>
    // 页面加载完成后立即自动执行动画,不依赖用户交互
    window.addEventListener('DOMContentLoaded', () => {
        
        // --- 核心运动学与 DOM 元素获取 ---
        const path = document.getElementById('kinematic-path');
        const pathLength = path.getTotalLength();
        
        const trackBase = document.getElementById('track-base');
        const trackTread = document.getElementById('track-tread');
        const trackGrip = document.getElementById('track-grip');
        
        const cabin1 = document.getElementById('cabin-1');
        const cabin2 = document.getElementById('cabin-2');
        const cabin3 = document.getElementById('cabin-3');
        
        const hinge12 = document.getElementById('hinge-12');
        const hinge12Glow = document.getElementById('hinge-12-glow');
        const hinge23 = document.getElementById('hinge-23');
        const hinge23Glow = document.getElementById('hinge-23-glow');
        const forceVector = document.getElementById('force-vector-front');

        const valSpeed = document.getElementById('val-speed');
        const valAngleFront = document.getElementById('val-angle-front');
        const valAngleRear = document.getElementById('val-angle-rear');
        const valStatus = document.getElementById('val-status');

        const speedSlider = document.getElementById('speed-slider');
        const speedDisplay = document.getElementById('speed-display');
        const elasticSlider = document.getElementById('elastic-slider');

        // --- 物理与状态参数 ---
        let globalProgress = 20; // 动画初始位置,稍稍进入画面
        let baseSpeed = 1.8;
        const SEGMENT_LENGTH = 75; // 舱段间距约束 (近似为节点间的弧长距离)
        let lastTimestamp = 0;

        // UI 交互绑定
        speedSlider.addEventListener('input', (e) => {
            baseSpeed = parseFloat(e.target.value) * 1.2;
            speedDisplay.textContent = e.target.value + 'x';
            valSpeed.textContent = (parseFloat(e.target.value) * 0.8).toFixed(1) + ' m/s';
        });

        elasticSlider.addEventListener('input', (e) => {
            const val = e.target.value;
            // 调整履带的张力视觉感受
            const tensionColor = `rgba(255, ${85 + (100-val)*1.7}, 0, 0.9)`;
            trackGrip.style.stroke = tensionColor;
        });

        // 角度计算辅助函数 (将 atan2 转为角度)
        function getAngle(p1, p2) {
            return Math.atan2(p2.y - p1.y, p2.x - p1.x) * 180 / Math.PI;
        }

        // --- 主渲染循环 ---
        function animate(timestamp) {
            if (!lastTimestamp) lastTimestamp = timestamp;
            const dt = timestamp - lastTimestamp;
            lastTimestamp = timestamp;

            // 保持帧率独立的运动
            globalProgress += baseSpeed * (dt / 16.6); 

            // 循环播放逻辑 (重开即播,到达终点自动重置)
            if (globalProgress > pathLength - 100) {
                globalProgress = 20;
                // 瞬间消除过渡效果防止跳跃闪烁
                trackGrip.style.transition = 'none'; 
            } else {
                trackGrip.style.transition = '';
            }

            // 履带上的 4 个核心接触点 (包络 3 个舱段)
            // 分别位于:前沿、前铰链、后铰链、后沿
            const posF = path.getPointAtLength(globalProgress + SEGMENT_LENGTH * 1.5);
            const posM1 = path.getPointAtLength(globalProgress + SEGMENT_LENGTH * 0.5);
            const posM2 = path.getPointAtLength(globalProgress - SEGMENT_LENGTH * 0.5);
            const posR = path.getPointAtLength(globalProgress - SEGMENT_LENGTH * 1.5);

            // 1. 绘制高弹性履带包络线
            // 利用 SVG thick stroke 和 round linejoin 实现完美的胶囊状外壳
            const trackPathDef = `M ${posF.x} ${posF.y} L ${posM1.x} ${posM1.y} L ${posM2.x} ${posM2.y} L ${posR.x} ${posR.y}`;
            trackBase.setAttribute('d', trackPathDef);
            trackTread.setAttribute('d', trackPathDef);
            trackGrip.setAttribute('d', trackPathDef);

            // 卷动履带花纹
            trackGrip.setAttribute('stroke-dashoffset', -globalProgress * 1.5);

            // 2. 更新舱段姿态 (位移与旋转)
            const updateCabinTransform = (element, pStart, pEnd) => {
                const cx = (pStart.x + pEnd.x) / 2;
                const cy = (pStart.y + pEnd.y) / 2;
                const angle = getAngle(pEnd, pStart);
                element.setAttribute('transform', `translate(${cx}, ${cy}) rotate(${angle})`);
                return angle;
            };

            const angleCabin1 = updateCabinTransform(cabin1, posF, posM1);
            const angleCabin2 = updateCabinTransform(cabin2, posM1, posM2);
            const angleCabin3 = updateCabinTransform(cabin3, posM2, posR);

            // 3. 计算关节弯折并处理 IFR 视觉引导
            // 夹角差值计算,并处理越界 (如 +179 与 -179 的差)
            let diff1 = angleCabin1 - angleCabin2;
            if (diff1 > 180) diff1 -= 360; else if (diff1 < -180) diff1 += 360;
            
            let diff2 = angleCabin2 - angleCabin3;
            if (diff2 > 180) diff2 -= 360; else if (diff2 < -180) diff2 += 360;

            const absDiff1 = Math.abs(diff1);
            const absDiff2 = Math.abs(diff2);

            // 铰链位置更新
            hinge12.setAttribute('cx', posM1.x); hinge12.setAttribute('cy', posM1.y);
            hinge12Glow.setAttribute('cx', posM1.x); hinge12Glow.setAttribute('cy', posM1.y);
            
            hinge23.setAttribute('cx', posM2.x); hinge23.setAttribute('cy', posM2.y);
            hinge23Glow.setAttribute('cx', posM2.x); hinge23Glow.setAttribute('cy', posM2.y);

            // 视觉引导:当弯折发生时,点亮对应铰链,突出“被动适应”的核心原理
            const threshold = 5; // 超过 5 度视为进入形变状态
            hinge12Glow.style.opacity = absDiff1 > threshold ? Math.min(absDiff1 / 45, 1).toString() : '0';
            hinge23Glow.style.opacity = absDiff2 > threshold ? Math.min(absDiff2 / 45, 1).toString() : '0';

            // 更新数据面板
            valAngleFront.textContent = absDiff1.toFixed(1) + '°';
            valAngleFront.className = absDiff1 > 35 ? 'data-value warning' : 'data-value';
            
            valAngleRear.textContent = absDiff2.toFixed(1) + '°';
            valAngleRear.className = absDiff2 > 35 ? 'data-value warning' : 'data-value';

            if (absDiff1 > threshold || absDiff2 > threshold) {
                valStatus.textContent = '仿形蠕动 [柔性贴合]';
                valStatus.style.color = 'var(--neon-orange)';
            } else {
                valStatus.textContent = '平移巡航 [重力复原]';
                valStatus.style.color = 'var(--neon-cyan)';
            }

            // 视觉引导 2:当车头接触立面且向上翘起时,展示向上的摩擦牵引力
            if (angleCabin1 < -30 && angleCabin1 > -100) {
                forceVector.setAttribute('transform', `translate(${posF.x}, ${posF.y})`);
                forceVector.setAttribute('opacity', '1');
            } else {
                forceVector.setAttribute('opacity', '0');
            }

            requestAnimationFrame(animate);
        }

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