分享图
A
动画渲染工坊
就绪

这是为您生成的一个展示蛇形机器人“弹性气囊能量回收”原理的高保真 SVG 动画网页源码,您可以直接在浏览器中运行和交互。

<!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>
    <link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;500;600;700;800;900&family=Share+Tech+Mono&family=Rajdhani:wght@300;400;500;600;700&display=swap" rel="stylesheet">
    <style>
        :root {
            --bg-deep: #080d14;
            --bg-panel: #111922;
            --text-primary: #dde4f0;
            --text-accent: #ffb347;
            --accent-energy: #ffc940;
            --accent-stf-liquid: #f08040;
            --accent-stf-solid: #ff2d4b;
            --accent-piezo: #00e5ff;
            --accent-glow: #00ffc8;
            --ground: #3a3028;
            --bump: #5c3a30;
            --body-metal: #7d8fa3;
            --body-dark: #3d4a58;
            --capillary: rgba(180, 200, 220, 0.7);
            --particle-gold: #ffe08a;
        }

        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            background: #060b10;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            min-height: 100vh;
            font-family: 'Rajdhani', 'Segoe UI', sans-serif;
            overflow-x: hidden;
            padding: 20px;
            background-image:
                radial-gradient(ellipse at 50% 30%, rgba(0, 180, 160, 0.04) 0%, transparent 70%),
                radial-gradient(ellipse at 30% 70%, rgba(255, 140, 50, 0.03) 0%, transparent 60%),
                radial-gradient(ellipse at 70% 60%, rgba(0, 200, 220, 0.03) 0%, transparent 60%);
        }

        .main-container {
            display: flex;
            flex-direction: column;
            align-items: center;
            gap: 18px;
            width: 100%;
            max-width: 960px;
        }

        .header {
            display: flex;
            align-items: center;
            gap: 16px;
            flex-wrap: wrap;
            justify-content: center;
        }
        .header .badge {
            background: rgba(0, 229, 255, 0.08);
            border: 1px solid rgba(0, 229, 255, 0.3);
            color: #00e5ff;
            font-family: 'Share Tech Mono', monospace;
            font-size: 0.7rem;
            letter-spacing: 0.15em;
            padding: 6px 14px;
            border-radius: 20px;
            text-transform: uppercase;
            white-space: nowrap;
        }
        .header h1 {
            font-family: 'Orbitron', sans-serif;
            font-weight: 700;
            font-size: 1.35rem;
            letter-spacing: 0.04em;
            color: #e8eef6;
            text-align: center;
            line-height: 1.3;
        }
        .header h1 span {
            color: #ffb347;
        }

        .svg-wrapper {
            position: relative;
            width: 100%;
            max-width: 920px;
            background: var(--bg-panel);
            border-radius: 16px;
            border: 1px solid rgba(255, 255, 255, 0.08);
            box-shadow:
                0 20px 60px rgba(0, 0, 0, 0.5),
                0 0 0 1px rgba(255, 255, 255, 0.03) inset,
                0 0 80px rgba(0, 200, 180, 0.04);
            overflow: hidden;
            aspect-ratio: 900/550;
            display: flex;
            align-items: center;
            justify-content: center;
        }
        .svg-wrapper svg {
            width: 100%;
            height: 100%;
            display: block;
        }

        .controls {
            display: flex;
            gap: 24px;
            align-items: center;
            flex-wrap: wrap;
            justify-content: center;
            background: var(--bg-panel);
            border-radius: 12px;
            padding: 14px 22px;
            border: 1px solid rgba(255, 255, 255, 0.06);
            box-shadow: 0 8px 30px rgba(0, 0, 0, 0.3);
        }
        .control-group {
            display: flex;
            align-items: center;
            gap: 10px;
        }
        .control-group label {
            font-family: 'Share Tech Mono', monospace;
            font-size: 0.72rem;
            letter-spacing: 0.06em;
            color: #8899b4;
            text-transform: uppercase;
            white-space: nowrap;
            min-width: 100px;
            text-align: right;
        }
        .control-group input[type="range"] {
            -webkit-appearance: none;
            width: 160px;
            height: 6px;
            border-radius: 3px;
            background: linear-gradient(90deg, #2a3545, #4a5568);
            outline: none;
            cursor: pointer;
        }
        .control-group input[type="range"]::-webkit-slider-thumb {
            -webkit-appearance: none;
            width: 22px;
            height: 22px;
            border-radius: 50%;
            background: #ffb347;
            border: 2px solid #1a2230;
            cursor: pointer;
            box-shadow: 0 0 14px rgba(255, 179, 71, 0.5);
            transition: box-shadow 0.2s;
        }
        .control-group input[type="range"]::-webkit-slider-thumb:hover {
            box-shadow: 0 0 24px rgba(255, 179, 71, 0.8);
        }
        .control-value {
            font-family: 'Share Tech Mono', monospace;
            font-size: 0.75rem;
            color: #ffb347;
            min-width: 40px;
            text-align: left;
        }
        .stf-indicator {
            display: flex;
            align-items: center;
            gap: 8px;
            font-family: 'Share Tech Mono', monospace;
            font-size: 0.7rem;
            letter-spacing: 0.05em;
            color: #8899b4;
            padding: 6px 12px;
            border-radius: 16px;
            background: rgba(255, 255, 255, 0.02);
            border: 1px solid rgba(255, 255, 255, 0.08);
            transition: all 0.3s;
        }
        .stf-indicator .dot {
            width: 10px;
            height: 10px;
            border-radius: 50%;
            background: #f08040;
            transition: background 0.2s, box-shadow 0.2s;
            box-shadow: 0 0 8px rgba(240, 128, 64, 0.5);
        }
        .stf-indicator.hardened .dot {
            background: #ff2d4b;
            box-shadow: 0 0 16px rgba(255, 45, 75, 0.9);
            animation: pulse-hard 0.4s ease-out;
        }
        .stf-indicator.hardened {
            color: #ff6b7a;
            border-color: rgba(255, 45, 75, 0.4);
            background: rgba(255, 45, 75, 0.06);
        }
        @keyframes pulse-hard {
            0%,
            100% {
                box-shadow: 0 0 16px rgba(255, 45, 75, 0.9);
            }
            50% {
                box-shadow: 0 0 32px rgba(255, 45, 75, 1.5);
            }
        }

        .legend-row {
            display: flex;
            gap: 16px;
            flex-wrap: wrap;
            justify-content: center;
            font-family: 'Share Tech Mono', monospace;
            font-size: 0.65rem;
            letter-spacing: 0.04em;
            color: #6b7d95;
        }
        .legend-row span {
            display: flex;
            align-items: center;
            gap: 5px;
        }
        .legend-dot {
            width: 8px;
            height: 8px;
            border-radius: 50%;
            display: inline-block;
            flex-shrink: 0;
        }
    </style>
</head>
<body>
    <div class="main-container">
        <!-- 头部 -->
        <div class="header">
            <span class="badge">TRIZ · IFR 最终理想解</span>
            <h1>弹性气囊 <span>能量回收</span> & 地形自适应</h1>
        </div>

        <!-- SVG 动画区域 -->
        <div class="svg-wrapper" id="svgWrapper">
            <svg id="mainSvg" viewBox="0 0 900 550" xmlns="http://www.w3.org/2000/svg">
                <defs>
                    <!-- 发光滤镜 -->
                    <filter id="glow-piezo" x="-80%" y="-80%" width="260%" height="260%">
                        <feGaussianBlur stdDeviation="3.5" result="blur" />
                        <feComposite in="SourceGraphic" in2="blur" operator="over" />
                    </filter>
                    <filter id="glow-particle" x="-200%" y="-200%" width="500%" height="500%">
                        <feGaussianBlur stdDeviation="2.5" result="blur" />
                        <feMerge>
                            <feMergeNode in="blur" />
                            <feMergeNode in="SourceGraphic" />
                        </feMerge>
                    </filter>
                    <filter id="glow-strong" x="-100%" y="-100%" width="300%" height="300%">
                        <feGaussianBlur stdDeviation="6" result="blur" />
                        <feMerge>
                            <feMergeNode in="blur" />
                            <feMergeNode in="blur" />
                            <feMergeNode in="SourceGraphic" />
                        </feMerge>
                    </filter>
                    <filter id="soft-shadow" x="-20%" y="-10%" width="140%" height="140%">
                        <feDropShadow dx="0" dy="3" stdDeviation="4" flood-color="#000000" flood-opacity="0.4" />
                    </filter>

                    <!-- 渐变 -->
                    <linearGradient id="grad-body" x1="0" y1="0" x2="0" y2="1">
                        <stop offset="0%" stop-color="#9aacbf" />
                        <stop offset="40%" stop-color="#6b7d93" />
                        <stop offset="100%" stop-color="#3d4c5c" />
                    </linearGradient>
                    <linearGradient id="grad-body-highlight" x1="0" y1="0" x2="0" y2="1">
                        <stop offset="0%" stop-color="#bcc8d6" />
                        <stop offset="50%" stop-color="#8a9bb0" />
                        <stop offset="100%" stop-color="#556372" />
                    </linearGradient>
                    <radialGradient id="grad-bladder" cx="50%" cy="30%" r="60%">
                        <stop offset="0%" stop-color="#f4985c" />
                        <stop offset="60%" stop-color="#e8703a" />
                        <stop offset="100%" stop-color="#c04a20" />
                    </radialGradient>
                    <radialGradient id="grad-bladder-hard" cx="50%" cy="30%" r="60%">
                        <stop offset="0%" stop-color="#ff5c6c" />
                        <stop offset="50%" stop-color="#ff2d4b" />
                        <stop offset="100%" stop-color="#c01030" />
                    </radialGradient>
                    <radialGradient id="grad-piezo-glow" cx="50%" cy="50%" r="50%">
                        <stop offset="0%" stop-color="#00ffcc" stop-opacity="0.9" />
                        <stop offset="40%" stop-color="#00e5ff" stop-opacity="0.5" />
                        <stop offset="100%" stop-color="#0088aa" stop-opacity="0" />
                    </radialGradient>
                    <linearGradient id="grad-ground" x1="0" y1="0" x2="0" y2="1">
                        <stop offset="0%" stop-color="#4a3d33" />
                        <stop offset="100%" stop-color="#2a2018" />
                    </linearGradient>
                    <linearGradient id="grad-bump" x1="0" y1="0" x2="0" y2="1">
                        <stop offset="0%" stop-color="#6b3d35" />
                        <stop offset="100%" stop-color="#3d1e18" />
                    </linearGradient>

                    <!-- 箭头标记 -->
                    <marker id="arrow-energy" markerWidth="8" markerHeight="6" refX="8" refY="3" orient="auto">
                        <polygon points="0,0 8,3 0,6" fill="#ffc940" />
                    </marker>

                    <!-- 噪声纹理 -->
                    <filter id="noise">
                        <feTurbulence type="fractalNoise" baseFrequency="0.65" numOctaves="3" stitchTiles="stitch" />
                        <feColorMatrix type="saturate" values="0" />
                        <feBlend in="SourceGraphic" mode="multiply" result="blend" />
                    </filter>
                </defs>

                <!-- 背景 -->
                <rect x="0" y="0" width="900" height="550" fill="#0b1018" />
                <rect x="0" y="0" width="900" height="550" fill="url(#noise)" opacity="0.06" />

                <!-- 背景网格 -->
                <g opacity="0.06" stroke="#4a6078" stroke-width="0.5">
                    <line x1="0" y1="100" x2="900" y2="100" />
                    <line x1="0" y1="200" x2="900" y2="200" />
                    <line x1="0" y1="300" x2="900" y2="300" />
                    <line x1="0" y1="400" x2="900" y2="400" />
                    <line x1="150" y1="0" x2="150" y2="550" />
                    <line x1="300" y1="0" x2="300" y2="550" />
                    <line x1="450" y1="0" x2="450" y2="550" />
                    <line x1="600" y1="0" x2="600" y2="550" />
                    <line x1="750" y1="0" x2="750" y2="550" />
                </g>

                <!-- 基准地面线(背景参考) -->
                <line x1="40" y1="408" x2="860" y2="408" stroke="#2a3038" stroke-width="0.8" stroke-dasharray="6,12"
                opacity="0.5" />

                <!-- 地面区域 -->
                <g id="groundGroup">
                    <!-- 主地面 -->
                    <path id="groundPath" d="M20,408 L900,408 L900,550 L20,550 Z" fill="url(#grad-ground)" />
                    <!-- 地面表面线 -->
                    <path id="groundSurface" d="M20,408 L900,408" stroke="#5a4a3d" stroke-width="2" fill="none" />
                    <!-- 凸起地形(动态更新) -->
                    <path id="bumpTerrain" d="" fill="url(#grad-bump)" opacity="0.9" />
                    <path id="bumpOutline" d="" stroke="#8b5040" stroke-width="2.5" fill="none" opacity="0.8" />
                    <!-- 凸起高亮标签区域 -->
                    <circle id="bumpHighlight" cx="450" cy="380" r="18" fill="none" stroke="#ff6b4a" stroke-width="1.5"
                    stroke-dasharray="4,3" opacity="0" />
                </g>

                <!-- 地形标注 -->
                <text id="bumpLabel" x="450" y="345" text-anchor="middle" font-family="'Share Tech Mono',monospace"
                font-size="11" fill="#ff8a6a" opacity="0" letter-spacing="0.05em">⚠ 尖锐凸起</text>

                <!-- 蛇身节段组(4个节段,由JS动态更新位置) -->
                <g id="segment1" class="segment-group">
                    <rect class="seg-body" x="-48" y="-20" width="96" height="40" rx="8" ry="8" fill="url(#grad-body)"
                    stroke="#5a6d80" stroke-width="1.2" filter="url(#soft-shadow)" />
                    <rect class="seg-top-highlight" x="-38" y="-16" width="76" height="6" rx="3" fill="rgba(255,255,255,0.12)" />
                    <!-- 压电片腔体 -->
                    <rect class="piezo-chamber" x="-14" y="-38" width="28" height="16" rx="4" fill="#1a2835"
                    stroke="#3a5068" stroke-width="1" />
                    <rect class="piezo-element" x="-10" y="-36" width="20" height="10" rx="2" fill="#1a3a40"
                    stroke="#008899" stroke-width="0.8" />
                    <!-- 压电发光 -->
                    <circle class="piezo-glow" cx="0" cy="-31" r="10" fill="url(#grad-piezo-glow)" opacity="0"
                    filter="url(#glow-piezo)" />
                    <!-- 毛细管 -->
                    <line class="capillary" x1="0" y1="-6" x2="0" y2="-22" stroke="rgba(170,200,220,0.55)"
                    stroke-width="1.5" stroke-dasharray="3,1.5" />
                    <!-- 气囊(半球形,底部) -->
                    <ellipse class="bladder" cx="0" cy="20" rx="34" ry="18" fill="url(#grad-bladder)"
                    stroke="#d06030" stroke-width="1.5" />
                    <ellipse class="bladder-highlight" cx="-8" cy="14" rx="10" ry="5" fill="rgba(255,255,255,0.13)"
                    opacity="0.7" />
                    <!-- 能量粒子容器 -->
                    <g class="particles-group"></g>
                </g>

                <g id="segment2" class="segment-group">
                    <rect class="seg-body" x="-48" y="-20" width="96" height="40" rx="8" ry="8" fill="url(#grad-body)"
                    stroke="#5a6d80" stroke-width="1.2" filter="url(#soft-shadow)" />
                    <rect class="seg-top-highlight" x="-38" y="-16" width="76" height="6" rx="3" fill="rgba(255,255,255,0.12)" />
                    <rect class="piezo-chamber" x="-14" y="-38" width="28" height="16" rx="4" fill="#1a2835"
                    stroke="#3a5068" stroke-width="1" />
                    <rect class="piezo-element" x="-10" y="-36" width="20" height="10" rx="2" fill="#1a3a40"
                    stroke="#008899" stroke-width="0.8" />
                    <circle class="piezo-glow" cx="0" cy="-31" r="10" fill="url(#grad-piezo-glow)" opacity="0"
                    filter="url(#glow-piezo)" />
                    <line class="capillary" x1="0" y1="-6" x2="0" y2="-22" stroke="rgba(170,200,220,0.55)"
                    stroke-width="1.5" stroke-dasharray="3,1.5" />
                    <ellipse class="bladder" cx="0" cy="20" rx="34" ry="18" fill="url(#grad-bladder)"
                    stroke="#d06030" stroke-width="1.5" />
                    <ellipse class="bladder-highlight" cx="-8" cy="14" rx="10" ry="5" fill="rgba(255,255,255,0.13)"
                    opacity="0.7" />
                    <g class="particles-group"></g>
                </g>

                <g id="segment3" class="segment-group">
                    <rect class="seg-body" x="-48" y="-20" width="96" height="40" rx="8" ry="8" fill="url(#grad-body)"
                    stroke="#5a6d80" stroke-width="1.2" filter="url(#soft-shadow)" />
                    <rect class="seg-top-highlight" x="-38" y="-16" width="76" height="6" rx="3" fill="rgba(255,255,255,0.12)" />
                    <rect class="piezo-chamber" x="-14" y="-38" width="28" height="16" rx="4" fill="#1a2835"
                    stroke="#3a5068" stroke-width="1" />
                    <rect class="piezo-element" x="-10" y="-36" width="20" height="10" rx="2" fill="#1a3a40"
                    stroke="#008899" stroke-width="0.8" />
                    <circle class="piezo-glow" cx="0" cy="-31" r="10" fill="url(#grad-piezo-glow)" opacity="0"
                    filter="url(#glow-piezo)" />
                    <line class="capillary" x1="0" y1="-6" x2="0" y2="-22" stroke="rgba(170,200,220,0.55)"
                    stroke-width="1.5" stroke-dasharray="3,1.5" />
                    <ellipse class="bladder" cx="0" cy="20" rx="34" ry="18" fill="url(#grad-bladder)"
                    stroke="#d06030" stroke-width="1.5" />
                    <ellipse class="bladder-highlight" cx="-8" cy="14" rx="10" ry="5" fill="rgba(255,255,255,0.13)"
                    opacity="0.7" />
                    <g class="particles-group"></g>
                </g>

                <g id="segment4" class="segment-group">
                    <rect class="seg-body" x="-48" y="-20" width="96" height="40" rx="8" ry="8" fill="url(#grad-body)"
                    stroke="#5a6d80" stroke-width="1.2" filter="url(#soft-shadow)" />
                    <rect class="seg-top-highlight" x="-38" y="-16" width="76" height="6" rx="3" fill="rgba(255,255,255,0.12)" />
                    <rect class="piezo-chamber" x="-14" y="-38" width="28" height="16" rx="4" fill="#1a2835"
                    stroke="#3a5068" stroke-width="1" />
                    <rect class="piezo-element" x="-10" y="-36" width="20" height="10" rx="2" fill="#1a3a40"
                    stroke="#008899" stroke-width="0.8" />
                    <circle class="piezo-glow" cx="0" cy="-31" r="10" fill="url(#grad-piezo-glow)" opacity="0"
                    filter="url(#glow-piezo)" />
                    <line class="capillary" x1="0" y1="-6" x2="0" y2="-22" stroke="rgba(170,200,220,0.55)"
                    stroke-width="1.5" stroke-dasharray="3,1.5" />
                    <ellipse class="bladder" cx="0" cy="20" rx="34" ry="18" fill="url(#grad-bladder)"
                    stroke="#d06030" stroke-width="1.5" />
                    <ellipse class="bladder-highlight" cx="-8" cy="14" rx="10" ry="5" fill="rgba(255,255,255,0.13)"
                    opacity="0.7" />
                    <g class="particles-group"></g>
                </g>

                <!-- 能量流动全局粒子(跨越气囊到压电片) -->
                <g id="globalParticles"></g>

                <!-- 图例区 -->
                <g transform="translate(610, 465)" opacity="0.85">
                    <rect x="0" y="0" width="260" height="70" rx="8" fill="rgba(10,16,24,0.85)" stroke="rgba(255,255,255,0.1)"
                    stroke-width="1" />
                    <text x="12" y="18" font-family="'Share Tech Mono',monospace" font-size="9" fill="#8899b4"
                    letter-spacing="0.04em">图例 LEGEND</text>
                    <circle cx="22" cy="34" r="5" fill="#f08040" />
                    <text x="34" y="37" font-family="'Rajdhani',sans-serif" font-size="10" fill="#bcc8d6">STF液态流动</text>
                    <circle cx="130" cy="34" r="5" fill="#ff2d4b" />
                    <text x="142" y="37" font-family="'Rajdhani',sans-serif" font-size="10" fill="#bcc8d6">STF硬化支撑</text>
                    <circle cx="22" cy="54" r="4" fill="#ffc940" filter="url(#glow-particle)" />
                    <text x="34" y="57" font-family="'Rajdhani',sans-serif" font-size="10" fill="#bcc8d6">能量回收粒子</text>
                    <circle cx="130" cy="54" r="4" fill="#00e5ff" filter="url(#glow-particle)" />
                    <text x="142" y="57" font-family="'Rajdhani',sans-serif" font-size="10" fill="#bcc8d6">压电发电脉冲</text>
                </g>

                <!-- 标题区 -->
                <text x="30" y="38" font-family="'Orbitron',sans-serif" font-weight="700" font-size="13" fill="#c8d6e0"
                letter-spacing="0.06em" opacity="0.7">蛇身节段 · 弹性气囊系统</text>
                <text x="30" y="56" font-family="'Share Tech Mono',monospace" font-size="8" fill="#556b80"
                letter-spacing="0.05em">SEGMENTED BLADDER ARRAY — STF CAPILLARY PIEZO</text>
            </svg>
        </div>

        <!-- 控制面板 -->
        <div class="controls" id="controlPanel">
            <div class="control-group">
                <label for="bumpSlider">地形凸起高度</label>
                <input type="range" id="bumpSlider" min="0" max="75" value="35" step="1">
                <span class="control-value" id="bumpValue">35 px</span>
            </div>
            <div class="control-group">
                <label for="freqSlider">波动频率</label>
                <input type="range" id="freqSlider" min="0.4" max="2.2" value="1.0" step="0.05">
                <span class="control-value" id="freqValue">1.0 Hz</span>
            </div>
            <div class="stf-indicator" id="stfIndicator">
                <span class="dot"></span>
                <span>STF 状态: 液态流动</span>
            </div>
        </div>

        <!-- 底部说明 -->
        <div class="legend-row">
            <span><span class="legend-dot" style="background:#f08040;"></span> 低剪切率 → STF液态 → 能量回收</span>
            <span><span class="legend-dot" style="background:#ff2d4b;"></span> 高剪切率 → STF硬化 → 结构支撑</span>
            <span><span class="legend-dot" style="background:#00e5ff;"></span> 压电发电 → 传感器回充</span>
            <span style="color:#ffb347;">⚡ 利用地面交互 · 零额外能耗</span>
        </div>
    </div>

    <script>
        (function() {
            // ─────────────────────────────────────
            // DOM 引用
            // ─────────────────────────────────────
            const svg = document.getElementById('mainSvg');
            const segments = [
                document.getElementById('segment1'),
                document.getElementById('segment2'),
                document.getElementById('segment3'),
                document.getElementById('segment4'),
            ];
            const globalParticlesGroup = document.getElementById('globalParticles');
            const bumpTerrain = document.getElementById('bumpTerrain');
            const bumpOutline = document.getElementById('bumpOutline');
            const bumpHighlight = document.getElementById('bumpHighlight');
            const bumpLabel = document.getElementById('bumpLabel');
            const stfIndicator = document.getElementById('stfIndicator');
            const bumpSlider = document.getElementById('bumpSlider');
            const freqSlider = document.getElementById('freqSlider');
            const bumpValueEl = document.getElementById('bumpValue');
            const freqValueEl = document.getElementById('freqValue');

            // ─────────────────────────────────────
            // 参数
            // ─────────────────────────────────────
            const SEG_COUNT = 4;
            const SEG_SPACING = 150; // 节段中心间距 (px in viewBox)
            const BASE_X_START = 155; // 第一个节段中心X
            const BASE_Y_CENTER = 280; // 节段波动中心Y
            const WAVE_AMPLITUDE = 38; // 波动幅度
            const GROUND_BASE_Y = 408; // 基准地面Y
            const BLADDER_BASE_RY = 18; // 气囊正常垂直半径
            const BLADDER_RX = 34; // 气囊水平半径
            const BLADDER_BOTTOM_OFFSET = 20; // 气囊底部相对节段中心的偏移(无压缩时)
            const BUMP_CENTER_X = 450; // 凸起中心X
            const BUMP_WIDTH = 180; // 凸起影响宽度
            const COMPRESSION_THRESHOLD_RATE = 2.8; // 压缩速率阈值(判断STF硬化)

            let bumpHeight = parseFloat(bumpSlider.value); // 凸起高度
            let waveFreq = parseFloat(freqSlider.value); // 波动频率(Hz)
            let time = 0;
            let lastFrameTime = performance.now();
            let prevCompression = [0, 0, 0, 0]; // 上一帧各节段压缩量
            let stfHardened = [false, false, false, false]; // STF硬化状态
            let hardenedTimers = [0, 0, 0, 0]; // 硬化状态持续时间
            const HARDENED_DURATION = 0.35; // 硬化持续秒数
            let energyParticles = []; // 全局能量粒子
            const MAX_PARTICLES = 40;

            // ─────────────────────────────────────
            // 地面凸起形状函数
            // ─────────────────────────────────────
            function getGroundY(x) {
                const dx = x - BUMP_CENTER_X;
                const sigma = BUMP_WIDTH / 3.5;
                const bumpContrib = bumpHeight * Math.exp(-(dx * dx) / (2 * sigma * sigma));
                // 使凸起更尖锐(使用更高阶高斯)
                const sharpFactor = 1 + 0.25 * Math.exp(-Math.abs(dx) / (sigma * 0.5));
                const finalBump = bumpContrib * Math.min(sharpFactor, 1.6);
                return GROUND_BASE_Y - finalBump;
            }

            function getGroundPathPoints() {
                const points = [];
                const step = 4;
                for (let x = 15; x <= 885; x += step) {
                    points.push({ x, y: getGroundY(x) });
                }
                return points;
            }

            function updateGroundShape() {
                const pts = getGroundPathPoints();
                // 凸起地形path(地面基准线以上的凸起部分)
                let bumpD = `M${pts[0].x},${GROUND_BASE_Y} `;
                for (const p of pts) {
                    if (p.y < GROUND_BASE_Y - 0.3) {
                        bumpD += `L${p.x},${p.y} `;
                    } else {
                        bumpD += `L${p.x},${GROUND_BASE_Y} `;
                    }
                }
                bumpD += `L${pts[pts.length-1].x},${GROUND_BASE_Y} L${pts[0].x},${GROUND_BASE_Y} Z`;
                bumpTerrain.setAttribute('d', bumpD);

                // 凸起轮廓线
                let outlineD = '';
                let drawing = false;
                for (const p of pts) {
                    if (p.y < GROUND_BASE_Y - 0.3) {
                        if (!drawing) {
                            outlineD += `M${p.x},${p.y} `;
                            drawing = true;
                        } else {
                            outlineD += `L${p.x},${p.y} `;
                        }
                    } else if (drawing) {
                        outlineD += `L${p.x},${GROUND_BASE_Y} `;
                        drawing = false;
                    }
                }
                bumpOutline.setAttribute('d', outlineD || 'M0,0');

                // 更新地面表面线
                const surfacePath = document.getElementById('groundSurface');
                let surfaceD = `M${pts[0].x},${pts[0].y}`;
                for (let i = 1; i < pts.length; i++) {
                    surfaceD += ` L${pts[i].x},${pts[i].y}`;
                }
                surfacePath.setAttribute('d', surfaceD);

                // 凸起高亮
                const peakY = getGroundY(BUMP_CENTER_X);
                bumpHighlight.setAttribute('cx', BUMP_CENTER_X);
                bumpHighlight.setAttribute('cy', peakY - 8);
                bumpHighlight.setAttribute('opacity', bumpHeight > 15 ? Math.min(1, (bumpHeight - 15) / 40) : 0);
                bumpLabel.setAttribute('y', peakY - 20);
                bumpLabel.setAttribute('opacity', bumpHeight > 20 ? Math.min(1, (bumpHeight - 20) / 35) : 0);
            }

            // ─────────────────────────────────────
            // 获取节段在世界坐标中的位置
            // ─────────────────────────────────────
            function getSegmentWorldY(segIndex, t) {
                const phase = segIndex * (Math.PI / 2); // 90度相位差
                return BASE_Y_CENTER + WAVE_AMPLITUDE * Math.sin(2 * Math.PI * waveFreq * t + phase);
            }

            function getBladderBottomY(segY, compression) {
                return segY + BLADDER_BOTTOM_OFFSET + BLADDER_BASE_RY - compression;
            }

            // ─────────────────────────────────────
            // 更新节段
            // ─────────────────────────────────────
            function updateSegments(t) {
                const segPositions = [];
                for (let i = 0; i < SEG_COUNT; i++) {
                    const cx = BASE_X_START + i * SEG_SPACING;
                    const cy = getSegmentWorldY(i, t);
                    segPositions.push({ cx, cy, index: i });
                    segments[i].setAttribute('transform', `translate(${cx}, ${cy})`);
                }
                return segPositions;
            }

            // ─────────────────────────────────────
            // 计算气囊压缩
            // ─────────────────────────────────────
            function computeCompression(segPositions) {
                const compressions = [];
                for (let i = 0; i < SEG_COUNT; i++) {
                    const { cx, cy } = segPositions[i];
                    const bladderBottomNominal = cy + BLADDER_BOTTOM_OFFSET + BLADDER_BASE_RY;
                    const groundY = getGroundY(cx);
                    const penetration = bladderBottomNominal - groundY;
                    const compression = Math.max(0, Math.min(penetration, BLADDER_BASE_RY * 0.85));
                    compressions.push(compression);
                }
                return compressions;
            }

            // ─────────────────────────────────────
            // 更新气囊外观
            // ─────────────────────────────────────
            function updateBladderAppearance(segIndex, compression, hardened) {
                const seg = segments[segIndex];
                const bladder = seg.querySelector('.bladder');
                const bladderHighlight = seg.querySelector('.bladder-highlight');
                const capillary = seg.querySelector('.capillary');
                const piezoGlow = seg.querySelector('.piezo-glow');
                const piezoElement = seg.querySelector('.piezo-element');

                const newRy = BLADDER_BASE_RY - compression * 0.7;
                const clampedRy = Math.max(4, newRy);
                bladder.setAttribute('ry', clampedRy);
                // 压缩时气囊变扁,cy略微下移
                const bladderCy = BLADDER_BOTTOM_OFFSET + (BLADDER_BASE_RY - clampedRy) * 0.5;
                bladder.setAttribute('cy', bladderCy);
                bladderHighlight.setAttribute('cy', bladderCy - 6);
                bladderHighlight.setAttribute('opacity', compression < 2 ? 0.7 : 0.3);

                // STF状态颜色
                if (hardened) {
                    bladder.setAttribute('fill', 'url(#grad-bladder-hard)');
                    bladder.setAttribute('stroke', '#ff4d5a');
                    bladder.setAttribute('stroke-width', '2.5');
                    capillary.setAttribute('stroke', 'rgba(255,80,90,0.7)');
                    capillary.setAttribute('stroke-width', '2');
                } else {
                    bladder.setAttribute('fill', 'url(#grad-bladder)');
                    bladder.setAttribute('stroke', '#d06030');
                    bladder.setAttribute('stroke-width', '1.5');
                    capillary.setAttribute('stroke', 'rgba(170,200,220,0.55)');
                    capillary.setAttribute('stroke-width', '1.5');
                }

                // 压电片发光(压缩时发光)
                const glowIntensity = compression / (BLADDER_BASE_RY * 0.85);
                piezoGlow.setAttribute('opacity', glowIntensity * 0.8);
                if (glowIntensity > 0.05) {
                    piezoElement.setAttribute('fill', '#1a5a60');
                    piezoElement.setAttribute('stroke', '#00ccdd');
                } else {
                    piezoElement.setAttribute('fill', '#1a3a40');
                    piezoElement.setAttribute('stroke', '#008899');
                }

                // 毛细管虚线动画(压缩时流动感)
                if (compression > 0.5 && !hardened) {
                    const dashOffset = (time * 40) % 9;
                    capillary.setAttribute('stroke-dashoffset', -dashOffset);
                } else if (hardened) {
                    capillary.setAttribute('stroke-dasharray', '6,3');
                    capillary.setAttribute('stroke-dashoffset', '0');
                } else {
                    capillary.setAttribute('stroke-dasharray', '3,1.5');
                    capillary.setAttribute('stroke-dashoffset', '0');
                }
            }

            // ─────────────────────────────────────
            // 能量粒子管理
            // ─────────────────────────────────────
            function spawnParticle(segIndex, segPos) {
                const { cx, cy } = segPos;
                const startY = cy + BLADDER_BOTTOM_OFFSET - 4;
                const endY = cy - 32;
                return {
                    segIndex,
                    startX: cx + (Math.random() - 0.5) * 14,
                    startY,
                    endX: cx + (Math.random() - 0.5) * 6,
                    endY,
                    progress: 0,
                    speed: 0.015 + Math.random() * 0.025,
                    size: 2 + Math.random() * 3,
                    alive: true,
                    element: null,
                };
            }

            function createParticleElement(particle) {
                const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
                circle.setAttribute('r', particle.size);
                circle.setAttribute('fill', '#ffc940');
                circle.setAttribute('filter', 'url(#glow-particle)');
                circle.setAttribute('opacity', '0.9');
                globalParticlesGroup.appendChild(circle);
                return circle;
            }

            function updateParticles(segPositions, compressions, hardenedStates, dt) {
                // 生成新粒子
                for (let i = 0; i < SEG_COUNT; i++) {
                    if (compressions[i] > 1.5 && !hardenedStates[i] && energyParticles.filter(p => p.segIndex ===
                            i && p.alive).length < 6) {
                        if (Math.random() < 0.5 && energyParticles.length < MAX_PARTICLES) {
                            const p = spawnParticle(i, segPositions[i]);
                            p.element = createParticleElement(p);
                            energyParticles.push(p);
                        }
                    }
                }

                // 更新现有粒子
                for (const p of energyParticles) {
                    if (!p.alive) continue;
                    p.progress += p.speed;
                    if (p.progress >= 1) {
                        p.alive = false;
                        if (p.element) {
                            p.element.remove();
                            p.element = null;
                        }
                        // 触发压电片闪光
                        const seg = segments[p.segIndex];
                        const piezoGlow = seg.querySelector('.piezo-glow');
                        piezoGlow.setAttribute('opacity', '1');
                        setTimeout(() => {
                            if (piezoGlow) piezoGlow.setAttribute('opacity', '0.3');
                        }, 120);
                        continue;
                    }
                    const cx = p.startX + (p.endX - p.startX) * p.progress;
                    const cy = p.startY + (p.endY - p.startY) * p.progress;
                    const wobble = Math.sin(p.progress * Math.PI * 8 + time * 30) * 2;
                    if (p.element) {
                        p.element.setAttribute('cx', cx + wobble * 0.5);
                        p.element.setAttribute('cy', cy + wobble);
                        p.element.setAttribute('opacity', 0.9 - p.progress * 0.5);
                        p.element.setAttribute('r', p.size * (1 - p.progress * 0.4));
                    }
                }

                // 清理死亡粒子
                energyParticles = energyParticles.filter(p => p.alive || (p.element && p.element.parentNode));
                // 彻底清理
                if (energyParticles.length > MAX_PARTICLES * 1.5) {
                    const toRemove = energyParticles.filter(p => !p.alive).slice(0, 20);
                    for (const p of toRemove) {
                        if (p.element && p.element.parentNode) p.element.remove();
                    }
                    energyParticles = energyParticles.filter(p => p.alive);
                }
            }

            // ─────────────────────────────────────
            // 更新STF状态指示器
            // ─────────────────────────────────────
            function updateSTFIndicator(anyHardened) {
                if (anyHardened) {
                    stfIndicator.classList.add('hardened');
                    stfIndicator.querySelector('span:last-child').textContent = 'STF 状态: 硬化支撑';
                } else {
                    stfIndicator.classList.remove('hardened');
                    stfIndicator.querySelector('span:last-child').textContent = 'STF 状态: 液态流动';
                }
            }

            // ─────────────────────────────────────
            // 主动画循环
            // ─────────────────────────────────────
            function animate(timestamp) {
                const dt = Math.min((timestamp - lastFrameTime) / 1000, 0.1);
                lastFrameTime = timestamp;
                time += dt;

                // 更新地面
                updateGroundShape();

                // 更新节段位置
                const segPositions = updateSegments(time);

                // 计算压缩
                const compressions = computeCompression(segPositions);

                // 判断STF硬化
                let anyHardened = false;
                for (let i = 0; i < SEG_COUNT; i++) {
                    const compressionRate = (compressions[i] - prevCompression[i]) / Math.max(dt, 0.001);
                    if (compressionRate > COMPRESSION_THRESHOLD_RATE && compressions[i] > 3) {
                        stfHardened[i] = true;
                        hardenedTimers[i] = HARDENED_DURATION;
                        anyHardened = true;
                    } else if (stfHardened[i]) {
                        hardenedTimers[i] -= dt;
                        if (hardenedTimers[i] <= 0) {
                            stfHardened[i] = false;
                        } else {
                            anyHardened = true;
                        }
                    }
                    prevCompression[i] = compressions[i];
                }
                updateSTFIndicator(anyHardened);

                // 更新气囊外观
                for (let i = 0; i < SEG_COUNT; i++) {
                    updateBladderAppearance(i, compressions[i], stfHardened[i]);
                }

                // 更新能量粒子
                updateParticles(segPositions, compressions, stfHardened, dt);

                requestAnimationFrame(animate);
            }

            // ─────────────────────────────────────
            // 滑块事件
            // ─────────────────────────────────────
            bumpSlider.addEventListener('input', () => {
                bumpHeight = parseFloat(bumpSlider.value);
                bumpValueEl.textContent = bumpHeight + ' px';
                updateGroundShape();
            });
            freqSlider.addEventListener('input', () => {
                waveFreq = parseFloat(freqSlider.value);
                freqValueEl.textContent = waveFreq.toFixed(2) + ' Hz';
            });

            // 初始化
            bumpHeight = parseFloat(bumpSlider.value);
            waveFreq = parseFloat(freqSlider.value);
            bumpValueEl.textContent = bumpHeight + ' px';
            freqValueEl.textContent = waveFreq.toFixed(2) + ' Hz';
            updateGroundShape();
            // 初始化prevCompression
            const initPos = [];
            for (let i = 0; i < SEG_COUNT; i++) {
                initPos.push({
                    cx: BASE_X_START + i * SEG_SPACING,
                    cy: getSegmentWorldY(i, 0),
                    index: i
                });
            }
            const initComp = computeCompression(initPos);
            for (let i = 0; i < SEG_COUNT; i++) prevCompression[i] = initComp[i];

            lastFrameTime = performance.now();
            requestAnimationFrame(animate);
        })();
    </script>
</body>
</html>

动画玩法与表现

这个动画聚焦TRIZ理论中的“最终理想解”(IFR),直观展示巧妙利用现有资源解决核心问题,并结合动作控制和参数调节来展现自适应效果。

  • 核心机制可视化:动画清晰展示了STF流体双重行为。当气囊被平稳压缩时(低剪切率),STF保持液态(橙色),能量粒子沿毛细管流向压电片发光,代表能量回收;当遇到尖锐凸起导致压缩速率骤增(高剪切率)时,STF瞬间硬化(红色),提供结构支撑,防止机身触底。

  • 交互与控制方式:页面底部提供了两个滑块。您可以调节“地形凸起高度”来改变地面障碍物的尖锐程度,或调节“波动频率”来控制蛇身的爬行节奏。右上角的“STF状态指示器”会实时反馈流体是处于液态流动还是硬化支撑状态。

  • 视觉风格与引导:整体采用深色科技感主题。关键部件(如气囊、压电片)使用高亮颜色和发光滤镜进行强调。能量回收过程通过流动的粒子实现,硬化状态则有明显的颜色变化和边框高亮,视觉引导清晰明确。

积分规则:第一轮对话扣减6分,后续每轮扣4分