独立渲染引擎就绪引擎就绪
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>气动软体蛇 - TRIZ IFR 演示</title>
<style>
:root {
--bg-base: #07090f;
--bg-grid: rgba(30, 41, 59, 0.5);
--silicone-core: #1e293b;
--silicone-edge: #334155;
--air-high: #00f0ff;
--air-low: #0f172a;
--text-main: #94a3b8;
--text-highlight: #e2e8f0;
--accent-orange: #ff5500;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
background-color: var(--bg-base);
color: var(--text-main);
font-family: 'SF Pro Display', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
height: 100vh;
width: 100vw;
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
background-image:
linear-gradient(var(--bg-grid) 1px, transparent 1px),
linear-gradient(90deg, var(--bg-grid) 1px, transparent 1px);
background-size: 40px 40px;
background-position: center center;
}
/* 纯净的动画主容器 */
#animation-container {
position: relative;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
z-index: 10;
}
svg {
width: 100%;
height: 100%;
max-width: 1200px;
max-height: 900px;
filter: drop-shadow(0 0 30px rgba(0, 240, 255, 0.05));
}
/* 绝对定位的极简文字说明层,严格遵守不遮挡核心动画的规则 */
.hud-overlay {
position: absolute;
pointer-events: none;
z-index: 20;
font-size: 12px;
line-height: 1.6;
letter-spacing: 0.5px;
}
.hud-top-left { top: 30px; left: 40px; }
.hud-top-right { top: 30px; right: 40px; text-align: right; }
.hud-bottom-left { bottom: 30px; left: 40px; }
.hud-bottom-right { bottom: 30px; right: 40px; text-align: right; }
.hud-title {
font-size: 14px;
font-weight: 600;
color: var(--text-highlight);
margin-bottom: 8px;
text-transform: uppercase;
letter-spacing: 2px;
}
.hud-data {
font-family: 'JetBrains Mono', 'Courier New', monospace;
color: var(--air-high);
font-weight: 500;
}
.hud-separator {
width: 20px;
height: 1px;
background: var(--text-main);
margin: 8px 0;
opacity: 0.3;
}
.hud-top-right .hud-separator, .hud-bottom-right .hud-separator {
margin-left: auto;
}
/* SVG 内部样式 */
.snake-spine {
fill: none;
stroke: var(--silicone-core);
stroke-width: 4;
stroke-linecap: round;
}
.snake-skin {
fill: rgba(30, 41, 59, 0.3);
stroke: var(--silicone-edge);
stroke-width: 1.5;
backdrop-filter: blur(4px);
}
.chamber {
transition: fill 0.1s;
}
.valve-line {
fill: none;
stroke: var(--silicone-edge);
stroke-width: 2;
stroke-dasharray: 4 4;
opacity: 0.5;
}
.flow-particle {
fill: var(--air-high);
filter: blur(1px);
}
/* 发光滤镜 */
.glow {
filter: drop-shadow(0 0 8px rgba(0, 240, 255, 0.6));
}
.glow-orange {
filter: drop-shadow(0 0 8px rgba(255, 85, 0, 0.6));
}
</style>
</head>
<body>
<!-- 极简文字说明层 (放置在角落) -->
<div class="hud-overlay hud-top-left">
<div class="hud-title">System IFR // 气动软体架构</div>
<div>消除机械铰接磨损,实现理想最终解</div>
<div class="hud-separator"></div>
<div>利用柔性体内部不对称气压分布</div>
<div>产生宏观形变,实现蠕动波驱动</div>
</div>
<div class="hud-overlay hud-top-right">
<div class="hud-title">Operational Data</div>
<div>PUMP_PRESSURE: <span class="hud-data">2.50 BAR</span></div>
<div>VALVE_FREQ: <span class="hud-data">20.0 HZ</span></div>
<div>STATUS: <span class="hud-data" style="color: #00ff66;">NOMINAL</span></div>
</div>
<div class="hud-overlay hud-bottom-left">
<div class="hud-title">Chamber Status</div>
<div>左气腔膨胀 → 躯干右弯</div>
<div>交替充排气产生纵向推力</div>
<div style="display: flex; gap: 8px; margin-top: 4px;">
<span style="display:inline-block;width:10px;height:10px;background:var(--air-high);border-radius:2px;box-shadow:0 0 5px var(--air-high);"></span>
<span>充气建压区</span>
<span style="display:inline-block;width:10px;height:10px;background:var(--air-low);border-radius:2px;border:1px solid #334155;"></span>
<span>排气释压区</span>
</div>
</div>
<div class="hud-overlay hud-bottom-right">
<div class="hud-title">Control Sequence</div>
<div id="seq-step-1">1. 微型泵建压</div>
<div id="seq-step-2">2. 阀岛开启 (交替)</div>
<div id="seq-step-3">3. 弯曲波传递</div>
<div id="seq-step-4">4. 持续蠕动</div>
</div>
<!-- 动画核心容器 -->
<div id="animation-container">
<svg id="visualizer" viewBox="0 0 1000 800" preserveAspectRatio="xMidYMid meet">
<defs>
<linearGradient id="pumpGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#1e293b" />
<stop offset="100%" stop-color="#0f172a" />
</linearGradient>
<radialGradient id="chamberGlow" cx="50%" cy="50%" r="50%">
<stop offset="0%" stop-color="#00f0ff" stop-opacity="0.8"/>
<stop offset="100%" stop-color="#00f0ff" stop-opacity="0"/>
</radialGradient>
</defs>
<!-- 底部基站:泵和电磁阀岛 -->
<g transform="translate(500, 720)">
<!-- 气泵 -->
<rect x="-80" y="-30" width="160" height="60" rx="6" fill="url(#pumpGrad)" stroke="#334155" stroke-width="2"/>
<text x="0" y="5" font-family="monospace" font-size="12" fill="#64748b" text-anchor="middle">MICRO-PUMP SYSTEM</text>
<!-- 泵工作指示灯 -->
<circle cx="-60" cy="0" r="4" fill="#00ff66" class="glow" id="pump-led"/>
<!-- 连接气管 -->
<path d="M -30, -30 C -30,-60 -20,-80 0,-100" class="valve-line"/>
<path d="M 30, -30 C 30,-60 20,-80 0,-100" class="valve-line"/>
<!-- 电磁阀 (L/R) -->
<rect x="-45" y="-30" width="30" height="10" fill="#0f172a" stroke="#475569"/>
<rect x="15" y="-30" width="30" height="10" fill="#0f172a" stroke="#475569"/>
<circle cx="-30" cy="-25" r="2" fill="#ff5500" id="valve-led-l"/>
<circle cx="30" cy="-25" r="2" fill="#ff5500" id="valve-led-r"/>
</g>
<!-- 软体蛇实体组 -->
<!-- 初始位于画面中央,由 JS 动态生成控制 -->
<g id="snake-body"></g>
</svg>
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
const svgNS = "http://www.w3.org/2000/svg";
const snakeContainer = document.getElementById('snake-body');
// --- 物理与几何参数 ---
const NUM_SEGMENTS = 24; // 节段数
const SEG_LENGTH = 20; // 每节长度
const BASE_WIDTH = 12; // 脊椎半宽
const CHAMBER_MAX_EXPAND = 22; // 气腔最大膨胀宽度
const WAVE_SPEED = 6.0; // 蠕动波速
const WAVE_FREQ = 0.45; // 空间频率
let time = 0;
let segments = [];
// --- 初始化 DOM 结构 ---
function initSnake() {
// 生成皮肤轮廓路径
const skinPath = document.createElementNS(svgNS, 'path');
skinPath.setAttribute('class', 'snake-skin');
skinPath.setAttribute('id', 'skin-outline');
snakeContainer.appendChild(skinPath);
// 生成中央脊椎线
const spinePath = document.createElementNS(svgNS, 'path');
spinePath.setAttribute('class', 'snake-spine');
spinePath.setAttribute('id', 'central-spine');
snakeContainer.appendChild(spinePath);
// 生成左右独立气腔
for (let i = 0; i < NUM_SEGMENTS; i++) {
const group = document.createElementNS(svgNS, 'g');
// 左气腔
const leftChamber = document.createElementNS(svgNS, 'path');
leftChamber.setAttribute('class', 'chamber');
leftChamber.setAttribute('stroke', '#334155');
leftChamber.setAttribute('stroke-width', '1');
// 右气腔
const rightChamber = document.createElementNS(svgNS, 'path');
rightChamber.setAttribute('class', 'chamber');
rightChamber.setAttribute('stroke', '#334155');
rightChamber.setAttribute('stroke-width', '1');
// 气腔内部分隔肋 (视觉细节)
const rib = document.createElementNS(svgNS, 'line');
rib.setAttribute('stroke', '#1e293b');
rib.setAttribute('stroke-width', '2');
group.appendChild(leftChamber);
group.appendChild(rightChamber);
group.appendChild(rib);
segments.push({
group: group,
left: leftChamber,
right: rightChamber,
rib: rib
});
snakeContainer.appendChild(group);
}
}
// --- 核心运动学计算与渲染 ---
function updateKinematics(t) {
// 整体位置偏移,保持在画面中央偏下
let cx = 500;
let cy = 620;
let baseAngle = 0;
let points = [];
points.push({x: cx, y: cy, angle: baseAngle, curvature: 0});
// 1. 计算脊椎Serpenoid曲线
for (let i = 1; i <= NUM_SEGMENTS; i++) {
// 构造行波方程
// 引入包络函数,使头尾摆幅较小,中间摆幅大,更符合真实物理约束
const envelope = Math.sin((i / NUM_SEGMENTS) * Math.PI);
const phase = i * WAVE_FREQ - t * WAVE_SPEED;
// 局部曲率(对应气压差)
const curvature = Math.sin(phase) * 0.45 * envelope;
baseAngle += curvature;
cx += Math.sin(baseAngle) * SEG_LENGTH;
cy -= Math.cos(baseAngle) * SEG_LENGTH; // 向上延伸
points.push({x: cx, y: cy, angle: baseAngle, curvature: curvature});
}
// 2. 更新 SVG 路径
let spineD = `M ${points[0].x} ${points[0].y}`;
let leftContour = [];
let rightContour = [];
for (let i = 0; i < NUM_SEGMENTS; i++) {
let p1 = points[i];
let p2 = points[i+1];
let cur = p2.curvature;
spineD += ` L ${p2.x} ${p2.y}`;
// 计算法向量
let nx = Math.cos(p1.angle);
let ny = Math.sin(p1.angle);
// 气腔膨胀逻辑:曲率为正->右弯->左侧膨胀;曲率为负->左弯->右侧膨胀
let leftPressure = Math.max(0, cur) * 2.5; // 标准化压力 0~1 左右
let rightPressure = Math.max(0, -cur) * 2.5;
let leftWidth = BASE_WIDTH + leftPressure * CHAMBER_MAX_EXPAND;
let rightWidth = BASE_WIDTH + rightPressure * CHAMBER_MAX_EXPAND;
// 计算轮廓顶点
let lpx = p1.x - nx * leftWidth;
let lpy = p1.y - ny * leftWidth;
let rpx = p1.x + nx * rightWidth;
let rpy = p1.y + ny * rightWidth;
leftContour.push({x: lpx, y: lpy});
rightContour.push({x: rpx, y: rpy});
// 渲染单个气腔形变
// 为了平滑,需要知道下一节的法向
let nx2 = Math.cos(p2.angle);
let ny2 = Math.sin(p2.angle);
let leftWidth2 = BASE_WIDTH + Math.max(0, points[i+1]?.curvature || 0) * 2.5 * CHAMBER_MAX_EXPAND;
let rightWidth2 = BASE_WIDTH + Math.max(0, -(points[i+1]?.curvature || 0)) * 2.5 * CHAMBER_MAX_EXPAND;
let lpx2 = p2.x - nx2 * leftWidth2;
let lpy2 = p2.y - ny2 * leftWidth2;
let rpx2 = p2.x + nx2 * rightWidth2;
let rpy2 = p2.y + ny2 * rightWidth2;
// 构造气腔的多边形路径
let leftD = `M ${p1.x} ${p1.y} L ${lpx} ${lpy} L ${lpx2} ${lpy2} L ${p2.x} ${p2.y} Z`;
let rightD = `M ${p1.x} ${p1.y} L ${rpx} ${rpy} L ${rpx2} ${rpy2} L ${p2.x} ${p2.y} Z`;
// 设置颜色(模拟气压)
let colHigh = [0, 240, 255];
let colLow = [15, 23, 42];
function blendColor(c1, c2, ratio) {
ratio = Math.min(1, Math.max(0, ratio));
return `rgb(${Math.round(c2[0] + (c1[0]-c2[0])*ratio)},
${Math.round(c2[1] + (c1[1]-c2[1])*ratio)},
${Math.round(c2[2] + (c1[2]-c2[2])*ratio)})`;
}
segments[i].left.setAttribute('d', leftD);
segments[i].left.setAttribute('fill', blendColor(colHigh, colLow, leftPressure));
if(leftPressure > 0.5) segments[i].left.setAttribute('class', 'chamber glow');
else segments[i].left.setAttribute('class', 'chamber');
segments[i].right.setAttribute('d', rightD);
segments[i].right.setAttribute('fill', blendColor(colHigh, colLow, rightPressure));
if(rightPressure > 0.5) segments[i].right.setAttribute('class', 'chamber glow');
else segments[i].right.setAttribute('class', 'chamber');
// 更新肋骨线
segments[i].rib.setAttribute('x1', lpx);
segments[i].rib.setAttribute('y1', lpy);
segments[i].rib.setAttribute('x2', rpx);
segments[i].rib.setAttribute('y2', rpy);
}
// 渲染脊椎和外皮
document.getElementById('central-spine').setAttribute('d', spineD);
let skinD = `M ${leftContour[0].x} ${leftContour[0].y}`;
for(let i=1; i<NUM_SEGMENTS; i++) skinD += ` L ${leftContour[i].x} ${leftContour[i].y}`;
// 蛇头圆滑过渡
let head = points[NUM_SEGMENTS];
skinD += ` C ${head.x - Math.cos(head.angle)*20} ${head.y - Math.sin(head.angle)*20},
${head.x + Math.cos(head.angle)*20} ${head.y + Math.sin(head.angle)*20},
${rightContour[NUM_SEGMENTS-1].x} ${rightContour[NUM_SEGMENTS-1].y}`;
for(let i=NUM_SEGMENTS-2; i>=0; i--) skinD += ` L ${rightContour[i].x} ${rightContour[i].y}`;
// 蛇尾闭合
skinD += ` Z`;
document.getElementById('skin-outline').setAttribute('d', skinD);
// --- 同步底部阀门状态指示灯 ---
// 取根部节段的压力状态来模拟阀门开闭
let baseCurvature = points[1].curvature;
const ledL = document.getElementById('valve-led-l');
const ledR = document.getElementById('valve-led-r');
if (baseCurvature > 0.1) {
ledL.setAttribute('fill', '#00f0ff');
ledL.setAttribute('class', 'glow');
ledR.setAttribute('fill', '#ff5500');
ledR.setAttribute('class', '');
} else if (baseCurvature < -0.1) {
ledL.setAttribute('fill', '#ff5500');
ledL.setAttribute('class', '');
ledR.setAttribute('fill', '#00f0ff');
ledR.setAttribute('class', 'glow');
}
}
// --- 粒子系统:模拟气流 ---
const particles = [];
function spawnParticle() {
if(Math.random() > 0.3) return; // 节流
const p = document.createElementNS(svgNS, 'circle');
p.setAttribute('r', '1.5');
p.setAttribute('class', 'flow-particle');
// 随机选择左管或右管
const isLeft = Math.random() > 0.5;
const startX = isLeft ? 470 : 530;
const startY = 690;
const endX = 500;
const endY = 620;
snakeContainer.appendChild(p);
particles.push({
el: p,
sx: startX, sy: startY,
ex: endX, ey: endY,
progress: 0,
speed: 0.02 + Math.random() * 0.02
});
}
function updateParticles() {
for(let i = particles.length - 1; i >= 0; i--) {
let p = particles[i];
p.progress += p.speed;
if(p.progress >= 1) {
p.el.remove();
particles.splice(i, 1);
continue;
}
// 简单的贝塞尔曲线轨迹
let t = p.progress;
let curX = (1-t)*(1-t)*p.sx + 2*(1-t)*t*p.sx + t*t*p.ex;
let curY = (1-t)*(1-t)*p.sy + 2*(1-t)*t*640 + t*t*p.ey; // 控制点y使其有弧度
p.el.setAttribute('cx', curX);
p.el.setAttribute('cy', curY);
p.el.setAttribute('opacity', 1 - t);
}
}
// --- 主渲染循环 ---
let lastTime = performance.now();
function render(currentTime) {
const dt = (currentTime - lastTime) / 1000; // 秒
lastTime = currentTime;
time += dt;
updateKinematics(time);
spawnParticle();
updateParticles();
// 细微闪烁泵指示灯模拟高频工作
document.getElementById('pump-led').style.opacity = 0.8 + Math.sin(currentTime * 0.05) * 0.2;
requestAnimationFrame(render);
}
// 初始化并自动启动
initSnake();
requestAnimationFrame(render);
});
</script>
</body>
</html>
积分规则:第一轮对话扣减8分,后续每轮扣6分
等待动画代码生成...
