独立渲染引擎就绪引擎就绪
以下是完全基于原生 HTML/CSS/SVG 和 JavaScript 构建的“杠杆尺蠖”爬楼小车原理动画代码。
该设计采用了**高科技蓝图与赛博工程(Technical Blueprint & Cyber Engineering)**的美学风格,通过深邃的暗色背景与高对比度的发光线条,将用户的视觉核心聚焦于机械运动的本质。设计完全遵循了 TRIZ 中的 “最终理想解 (IFR)” 思想:通过让阶梯“倒退”的相对运动巧妙实现无限循环,去除了杂乱的环境干扰,直观揭示了轮足协同、V形脚垫锁定和杠杆姿态补偿的精巧原理。
动画全自动播放,文字说明已被极其克制地缩小并安置在四角,确保中央机械运作区域占据绝对的视觉主导。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>杠杆尺蠖爬楼小车 - 最终理想解</title>
<style>
:root {
--bg-color: #050811;
--grid-color: rgba(0, 150, 255, 0.08);
--stair-stroke: #1b273d;
--stair-edge: #00f0ff;
--car-main: #2a3547;
--car-accent: #00f0ff;
--leg-sleeve: #475a7a;
--leg-rod: #e2f1ff;
--force-active: #ff3b00;
--text-color: #7b9abf;
--hud-bg: rgba(5, 8, 17, 0.6);
}
body, html {
margin: 0;
padding: 0;
width: 100vw;
height: 100vh;
background-color: var(--bg-color);
background-image:
linear-gradient(var(--grid-color) 1px, transparent 1px),
linear-gradient(90deg, var(--grid-color) 1px, transparent 1px);
background-size: 30px 30px;
background-position: center center;
overflow: hidden;
font-family: 'Consolas', 'Courier New', monospace;
display: flex;
justify-content: center;
align-items: center;
}
#animation-container {
position: relative;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
svg {
width: 100%;
height: 100%;
max-width: 1200px;
max-height: 900px;
overflow: visible;
}
/* 极简且不遮挡画面的 HUD 信息面板 */
.hud-panel {
position: absolute;
background: var(--hud-bg);
border: 1px solid rgba(0, 240, 255, 0.2);
padding: 12px 16px;
border-radius: 4px;
color: var(--text-color);
font-size: 12px;
line-height: 1.6;
backdrop-filter: blur(8px);
box-shadow: 0 4px 20px rgba(0,0,0,0.5);
pointer-events: none;
z-index: 10;
}
#hud-top-left { top: 30px; left: 30px; }
#hud-top-right { top: 30px; right: 30px; text-align: right; }
#hud-bottom-right { bottom: 30px; right: 30px; text-align: right; }
#hud-bottom-left { bottom: 30px; left: 30px; }
.hud-title {
color: var(--car-accent);
font-weight: bold;
font-size: 14px;
margin-bottom: 6px;
text-transform: uppercase;
letter-spacing: 1px;
}
.data-value {
color: #fff;
font-weight: bold;
text-shadow: 0 0 5px var(--car-accent);
}
.force-value {
color: var(--force-active);
text-shadow: 0 0 5px var(--force-active);
}
/* SVG 样式与滤镜 */
.glow { filter: drop-shadow(0 0 8px var(--car-accent)); }
.glow-red { filter: drop-shadow(0 0 8px var(--force-active)); }
.stair-line {
fill: none;
stroke: var(--stair-stroke);
stroke-width: 4;
stroke-linejoin: round;
}
.stair-edge-highlight {
fill: none;
stroke: var(--stair-edge);
stroke-width: 2;
stroke-dasharray: 4 4;
opacity: 0.5;
}
.mech-body {
fill: var(--car-main);
stroke: var(--car-accent);
stroke-width: 1.5;
}
.wheel {
fill: #111a28;
stroke: #475a7a;
stroke-width: 3;
}
.active-point {
fill: var(--bg-color);
stroke: var(--force-active);
stroke-width: 2;
transition: all 0.1s;
}
</style>
</head>
<body>
<div id="animation-container">
<!-- 信息面板 -->
<div class="hud-panel" id="hud-top-left">
<div class="hud-title">System.IFR_Status</div>
<div>系统模态: <span class="data-value" id="val-phase">初始化</span></div>
<div>爬升周期: <span class="data-value">1.5s / 级</span></div>
<div>支撑策略: <span class="data-value" id="val-support">轮足协同</span></div>
</div>
<div class="hud-panel" id="hud-top-right">
<div class="hud-title">Kinematics.Data</div>
<div>主梁俯仰角(Pitch): <span class="data-value" id="val-pitch">0.0°</span></div>
<div>平台姿态补偿: <span class="data-value" id="val-comp">0.0°</span></div>
<div>重心偏移(Y): <span class="data-value" id="val-cg">0 mm</span></div>
</div>
<div class="hud-panel" id="hud-bottom-left">
<div class="hud-title">TRIZ_Principle</div>
<div style="color: #fff">>> 最终理想解 (IFR)</div>
<div style="opacity: 0.8">消除问题后的绝对工作态<br>无多余步骤,直接展示核心杠杆解构</div>
<div style="color: #fff; margin-top: 5px;">>> 嵌套/杠杆/局部质量</div>
<div style="opacity: 0.8">平地用轮,楼梯用足杠杆支撑</div>
</div>
<div class="hud-panel" id="hud-bottom-right">
<div class="hud-title">Force.Feedback</div>
<div>前足拉升张力: <span class="force-value" id="val-force-f">0 N</span></div>
<div>后足支撑推力: <span class="force-value" id="val-force-r">0 N</span></div>
<div>锁定结构: <span class="data-value">V形倒钩接触副</span></div>
</div>
<!-- 核心 SVG 动画区 -->
<svg viewBox="-300 -350 600 700" preserveAspectRatio="xMidYMid meet">
<!-- 滤镜定义 -->
<defs>
<radialGradient id="hub-grad" cx="50%" cy="50%" r="50%">
<stop offset="0%" stop-color="#00f0ff" stop-opacity="0.8"/>
<stop offset="100%" stop-color="#00f0ff" stop-opacity="0"/>
</radialGradient>
<linearGradient id="cargo-grad" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" stop-color="#00f0ff" stop-opacity="0.3"/>
<stop offset="100%" stop-color="#00f0ff" stop-opacity="0.05"/>
</linearGradient>
</defs>
<!-- 场景容器:阶梯 -->
<g id="stairs-group">
<path id="stair-path" class="stair-line" d=""></path>
<path id="stair-highlight" class="stair-edge-highlight" d=""></path>
<!-- 动态生成的棱角高亮光圈 -->
<g id="edge-nodes"></g>
</g>
<!-- 车辆连杆机构与足 (世界坐标系下动态绘制,解耦层级防止旋转嵌套) -->
<g id="linkages">
<!-- 后腿 -->
<line id="r-leg-sleeve" stroke="var(--leg-sleeve)" stroke-width="14" stroke-linecap="round"/>
<line id="r-leg-rod" stroke="var(--leg-rod)" stroke-width="6" stroke-linecap="round"/>
<!-- 后脚垫 -->
<path id="r-pad" d="M -15,0 L 15,0" fill="none" stroke="var(--car-accent)" stroke-width="4" stroke-linecap="round"/>
<!-- 前腿 -->
<line id="f-leg-sleeve" stroke="var(--leg-sleeve)" stroke-width="14" stroke-linecap="round"/>
<line id="f-leg-rod" stroke="var(--leg-rod)" stroke-width="6" stroke-linecap="round"/>
<!-- 前脚垫 (V形倒钩) -->
<path id="f-pad" d="M -18,2 L 4,2 L 4,20" fill="none" stroke="var(--force-active)" stroke-width="4" stroke-linejoin="round" class="glow-red"/>
</g>
<!-- 车体组 (含中心补偿机构与货物) -->
<g id="car-group">
<!-- 中部轮组 (4轮,前后重叠展示为2大轮) -->
<g id="wheels" transform="translate(0, 30)">
<circle cx="-60" cy="0" r="30" class="wheel"/>
<circle cx="60" cy="0" r="30" class="wheel"/>
<circle cx="-60" cy="0" r="10" fill="#2a3547" stroke="#00f0ff" stroke-width="1"/>
<circle cx="60" cy="0" r="10" fill="#2a3547" stroke="#00f0ff" stroke-width="1"/>
</g>
<!-- 主梁梁体 -->
<path class="mech-body glow" d="M -150,-10 L 150,-10 L 160,10 L 130,30 L -130,30 L -160,10 Z"/>
<!-- 前后铰接点 -->
<circle cx="-130" cy="15" r="5" fill="#00f0ff"/>
<circle cx="130" cy="15" r="5" fill="#00f0ff"/>
<!-- 平行四边形悬挂补偿机构 (与主梁发生相对运动) -->
<g id="suspension-group">
<!-- 主支撑柱 -->
<rect id="center-pillar" x="-10" y="-140" width="20" height="130" fill="var(--car-main)" stroke="var(--car-accent)" stroke-width="1.5"/>
<!-- 平行连杆 -->
<line id="parallel-rod" x1="-25" y1="-10" x2="-25" y2="-130" stroke="var(--force-active)" stroke-width="3"/>
<!-- 旋转节点 -->
<circle cx="0" cy="-10" r="6" class="active-point"/>
<circle cx="-25" cy="-10" r="4" fill="#ff3b00"/>
<circle cx="0" cy="-140" r="6" class="active-point"/>
<circle cx="-25" cy="-130" r="4" fill="#ff3b00"/>
</g>
<!-- 货物平台 (始终保持水平绝对姿态,通过代码逆向旋转抵消主梁倾角) -->
<g id="cargo-platform">
<!-- 平台底座 -->
<rect x="-90" y="-155" width="180" height="15" rx="3" class="mech-body"/>
<!-- 抽象货物框 -->
<rect x="-80" y="-235" width="160" height="80" rx="4" fill="url(#cargo-grad)" stroke="#00f0ff" stroke-width="1" stroke-dasharray="8 4"/>
<text x="0" y="-190" fill="#00f0ff" font-size="14" text-anchor="middle" opacity="0.6" font-family="sans-serif">CARGO PAYLOAD</text>
</g>
</g>
<!-- 动力传递特效线 -->
<path id="force-line" fill="none" stroke="var(--force-active)" stroke-width="2" stroke-dasharray="4 8" opacity="0"/>
</svg>
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
// == 系统与几何参数配置 ==
const STAIR_W = 220; // 台阶深度
const STAIR_H = 160; // 台阶高度
const CAR_BASE_X = 110; // 车辆在画面中的恒定X轴参考点
const CAR_BASE_Y = -180; // 车辆的基础Y高度
// 小车主梁上前/后腿安装点 (相对车体中心)
const LEG_BASE_OFFSET = { front: {x: 130, y: 15}, rear: {x: -130, y: 15} };
// 动画周期配置 (毫秒)
const PHASE_PULL_DUR = 1800; // Phase 0: 杠杆拉升阶段 (占主体)
const PHASE_STEP_DUR = 1200; // Phase 1: 空中换步阶段
const TOTAL_DUR = PHASE_PULL_DUR + PHASE_STEP_DUR;
// DOM 元素引用
const els = {
stairsGrp: document.getElementById('stairs-group'),
stairPath: document.getElementById('stair-path'),
stairHi: document.getElementById('stair-highlight'),
edgeNodes: document.getElementById('edge-nodes'),
carGrp: document.getElementById('car-group'),
cargoPlat: document.getElementById('cargo-platform'),
centerPillar: document.getElementById('center-pillar'),
parallelRod: document.getElementById('parallel-rod'),
fSleeve: document.getElementById('f-leg-sleeve'),
fRod: document.getElementById('f-leg-rod'),
fPad: document.getElementById('f-pad'),
rSleeve: document.getElementById('r-leg-sleeve'),
rRod: document.getElementById('r-leg-rod'),
rPad: document.getElementById('r-pad'),
forceLine: document.getElementById('force-line'),
// HUD
valPhase: document.getElementById('val-phase'),
valSupport: document.getElementById('val-support'),
valPitch: document.getElementById('val-pitch'),
valComp: document.getElementById('val-comp'),
valCg: document.getElementById('val-cg'),
valForceF: document.getElementById('val-force-f'),
valForceR: document.getElementById('val-force-r')
};
// == 静态背景阶梯生成 (生成足够多的台阶以支持位移无缝衔接) ==
function initStairs() {
let d = '', dHi = '';
// 绘制 -3 到 +3 级台阶
for(let i = -3; i <= 3; i++) {
const x = i * STAIR_W;
const y = -i * STAIR_H;
if (i === -3) {
d += `M ${x},${y+STAIR_H} L ${x},${y} L ${x+STAIR_W},${y}`;
dHi += `M ${x},${y} L ${x+STAIR_W},${y}`;
} else {
d += ` L ${x},${y} L ${x+STAIR_W},${y}`;
dHi += ` M ${x},${y} L ${x+STAIR_W},${y}`;
}
// 添加棱角发光点
const circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
circle.setAttribute("cx", x);
circle.setAttribute("cy", y);
circle.setAttribute("r", 6);
circle.setAttribute("fill", "url(#hub-grad)");
els.edgeNodes.appendChild(circle);
}
els.stairPath.setAttribute("d", d + " L 900,1000 L -900,1000 Z"); // 闭合填充背景
els.stairPath.style.fill = "#0a0f1c";
els.stairHi.setAttribute("d", dHi);
}
initStairs();
// == 缓动函数 ==
const easeInOutQuad = t => t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
const easeOutBack = (t) => {
const c1 = 1.70158; const c3 = c1 + 1;
return 1 + c3 * Math.pow(t - 1, 3) + c1 * Math.pow(t - 1, 2);
};
// == 核心循环状态机 ==
let startTime = performance.now();
function renderFrame(now) {
// 时间归一化
let elapsed = (now - startTime) % TOTAL_DUR;
let phase = elapsed < PHASE_PULL_DUR ? 0 : 1;
let p = phase === 0
? elapsed / PHASE_PULL_DUR
: (elapsed - PHASE_PULL_DUR) / PHASE_STEP_DUR;
// 物理与几何变量
let stX = 0, stY = 0; // 阶梯整体平移
let carY = CAR_BASE_Y;
let carPitch = 0; // 主梁仰角 (度)
let fTipX, fTipY; // 前足端点世界坐标
let rTipX, rTipY; // 后足端点世界坐标
// 定义局部坐标系中的台阶基准点
// 台阶 0 位于 (0,0);台阶 1 位于 (STAIR_W, -STAIR_H)
const S_M1 = { x: -STAIR_W, y: STAIR_H };
const S_0 = { x: 0, y: 0 };
const S_1 = { x: STAIR_W, y: -STAIR_H };
if (phase === 0) {
// === 阶段 0: 杠杆拉升 (小车在原地,阶梯向左下后退) ===
let ep = easeInOutQuad(p);
stX = -STAIR_W * ep;
stY = STAIR_H * ep;
// 前足死锁在 S_1 的棱角
fTipX = S_1.x + stX;
fTipY = S_1.y + stY;
// 后足死锁在 S_0 的平面上
rTipX = S_0.x + stX;
rTipY = S_0.y + stY;
// 表现杠杆抬升:中间产生一个拱起的位移和仰角
// 模拟后腿顶起车尾,前腿拉拽
carPitch = -12 * Math.sin(ep * Math.PI); // 最大上仰 12 度
carY = CAR_BASE_Y - 45 * Math.sin(ep * Math.PI); // 重心最多抬高 45mm
// UI 更新
els.valPhase.textContent = "杠杆拉升 (Leverage Pull)";
els.valPhase.style.color = "var(--force-active)";
els.valSupport.textContent = "全足锁死跨越";
els.valForceF.textContent = Math.round(150 + 100 * Math.sin(ep * Math.PI)) + " N";
els.valForceR.textContent = Math.round(200 + 150 * Math.sin(ep * Math.PI)) + " N";
els.fPad.classList.add('glow-red');
els.forceLine.style.opacity = 0.6;
} else {
// === 阶段 1: 双足换步 (阶梯瞬间复位并静止,双腿在空中跨越) ===
let ep = easeInOutQuad(p);
// 瞬间复位带来的逻辑坐标变化:
// 原本的 S_1 变成了现在的 S_0。
// 前脚需要从 S_0 跨向新的 S_1。
// 后脚需要从 S_M1 跨向新的 S_0。
// 前腿跨步轨迹 (抛物线)
fTipX = S_0.x + (S_1.x - S_0.x) * ep;
fTipY = S_0.y + (S_1.y - S_0.y) * ep - 80 * Math.sin(ep * Math.PI);
// 后腿跨步轨迹
rTipX = S_M1.x + (S_0.x - S_M1.x) * ep;
rTipY = S_M1.y + (S_0.y - S_M1.y) * ep - 60 * Math.sin(ep * Math.PI);
carPitch = 0;
carY = CAR_BASE_Y;
// UI 更新
els.valPhase.textContent = "双足换步 (Step Reset)";
els.valPhase.style.color = "var(--car-accent)";
els.valSupport.textContent = "中部轮组过渡";
els.valForceF.textContent = "0 N";
els.valForceR.textContent = "0 N";
els.fPad.classList.remove('glow-red');
els.forceLine.style.opacity = 0;
}
// --- 渲染更新 ---
// 1. 更新台阶层容器平移
els.stairsGrp.setAttribute('transform', `translate(${stX}, ${stY})`);
// 2. 更新车体组的整体位移与旋转
// 由于车体中心是 CAR_BASE_X, carY,绕自身旋转
els.carGrp.setAttribute('transform', `translate(${CAR_BASE_X}, ${carY}) rotate(${carPitch})`);
// 3. 计算车体上安装点的绝对世界坐标
// 旋转矩阵公式: X' = x*cos - y*sin, Y' = x*sin + y*cos
const rad = carPitch * Math.PI / 180;
const cosP = Math.cos(rad);
const sinP = Math.sin(rad);
const getWldPos = (offset) => {
return {
x: CAR_BASE_X + offset.x * cosP - offset.y * sinP,
y: carY + offset.x * sinP + offset.y * cosP
};
};
let fBaseWld = getWldPos(LEG_BASE_OFFSET.front);
let rBaseWld = getWldPos(LEG_BASE_OFFSET.rear);
// 4. 绘制推杆 (腿部)
const updateLeg = (baseWld, tipX, tipY, elSleeve, elRod) => {
const dx = tipX - baseWld.x;
const dy = tipY - baseWld.y;
const dist = Math.hypot(dx, dy);
const dirX = dx / dist;
const dirY = dy / dist;
// 套筒固定长度 70
const sleeveLen = 70;
const midX = baseWld.x + dirX * sleeveLen;
const midY = baseWld.y + dirY * sleeveLen;
elSleeve.setAttribute('x1', baseWld.x);
elSleeve.setAttribute('y1', baseWld.y);
elSleeve.setAttribute('x2', midX);
elSleeve.setAttribute('y2', midY);
elRod.setAttribute('x1', midX);
elRod.setAttribute('y1', midY);
elRod.setAttribute('x2', tipX);
elRod.setAttribute('y2', tipY);
};
updateLeg(fBaseWld, fTipX, fTipY, els.fSleeve, els.fRod);
updateLeg(rBaseWld, rTipX, rTipY, els.rSleeve, els.rRod);
// 5. 更新脚垫位置 (V形倒钩和平面垫)
els.fPad.setAttribute('transform', `translate(${fTipX}, ${fTipY})`);
els.rPad.setAttribute('transform', `translate(${rTipX}, ${rTipY})`);
// 6. 悬挂补偿与货物平台反向旋转保持水平
// 平台位于 carGrp 内,它继承了 carPitch 旋转。
// 为了让它保持绝对水平,需要相对于它自身的中心进行负 carPitch 的旋转!
els.cargoPlat.setAttribute('transform', `rotate(${-carPitch}, 0, -150)`);
// 调整悬挂柱子 (中心柱保持刚性竖直方向)
// 表现形式:视觉补偿。立柱本身在 carGrp 下,反转它。
els.centerPillar.setAttribute('transform', `rotate(${-carPitch}, 0, -10)`);
// 平行副连杆也要反转
els.parallelRod.setAttribute('transform', `rotate(${-carPitch}, 0, -10)`);
// 绘制一根表示力矩传递的虚线 (从前脚钩连接到车体重心)
els.forceLine.setAttribute('d', `M ${fTipX},${fTipY} L ${CAR_BASE_X},${carY}`);
// 7. 更新 HUD 数据
els.valPitch.textContent = carPitch.toFixed(1) + "°";
els.valComp.textContent = (-carPitch).toFixed(1) + "°";
els.valCg.textContent = (carY - CAR_BASE_Y).toFixed(0) + " mm";
requestAnimationFrame(renderFrame);
}
// 启动动画循环
requestAnimationFrame(renderFrame);
});
</script>
</body>
</html>
积分规则:每轮对话扣10分
等待动画代码生成...
