这是为您生成的一个展示蛇形机器人“弹性气囊能量回收”原理的高保真 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分
等待动画代码生成...
