独立渲染引擎就绪引擎就绪
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>行星轮越障 + 主动云台补偿 原理动画</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/gsap.min.js"></script>
<style>
*{margin:0;padding:0;box-sizing:border-box}
body{background:#0c1222;display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:100vh;font-family:-apple-system,'Segoe UI',sans-serif;color:#e2e8f0;padding:16px}
h1{font-size:1.25rem;margin-bottom:4px;color:#f1f5f9;text-align:center;letter-spacing:.5px}
.sub{font-size:.82rem;color:#94a3b8;margin-bottom:10px;text-align:center;max-width:680px;line-height:1.5}
.wrap{width:96vw;max-width:1060px}
svg{width:100%;height:auto;border-radius:10px;background:linear-gradient(180deg,#1e293b 0%,#0f172a 60%)}
.ctrls{margin-top:12px;display:flex;gap:10px;align-items:center;flex-wrap:wrap;justify-content:center}
button{padding:6px 18px;border:none;border-radius:6px;background:#3b82f6;color:#fff;cursor:pointer;font-size:.82rem;transition:background .2s}
button:hover{background:#2563eb}
.plbl{font-size:.9rem;color:#fbbf24;min-width:170px;text-align:center}
.legend{margin-top:8px;display:flex;gap:14px;flex-wrap:wrap;justify-content:center;font-size:.75rem;color:#94a3b8}
.li{display:flex;align-items:center;gap:4px}
.ld{width:11px;height:11px;border-radius:2px}
@keyframes gPulse{0%,100%{opacity:.55}50%{opacity:1}}
.gimbal-pulse{animation:gPulse .6s ease-in-out infinite}
</style>
</head>
<body>
<div class="wrap">
<h1>行星轮越障 + 主动云台补偿 原理动画</h1>
<p class="sub">底层行星轮组几何翻转强行跨越台阶,上层主动云台反向补偿切断颠簸传递路径——移动跨越与保持平衡解耦</p>
<svg id="scene" viewBox="0 0 1000 550">
<defs>
<radialGradient id="wg" cx="38%" cy="38%"><stop offset="0%" stop-color="#64748b"/><stop offset="100%" stop-color="#334155"/></radialGradient>
<linearGradient id="cg" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#475569"/><stop offset="100%" stop-color="#334155"/></linearGradient>
<linearGradient id="pg" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#22c55e"/><stop offset="100%" stop-color="#16a34a"/></linearGradient>
<linearGradient id="cag" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#fbbf24"/><stop offset="100%" stop-color="#f59e0b"/></linearGradient>
<pattern id="bk" width="22" height="14" patternUnits="userSpaceOnUse">
<rect width="22" height="14" fill="#78350f"/>
<rect width="10" height="6" x="0" y="0" fill="#92400e" rx="1"/>
<rect width="10" height="6" x="12" y="0" fill="#92400e" rx="1"/>
<rect width="10" height="6" x="5" y="8" fill="#92400e" rx="1"/>
<rect width="10" height="6" x="17" y="8" fill="#92400e" rx="1"/>
</pattern>
<filter id="gl"><feGaussianBlur stdDeviation="4" result="b"/><feMerge><feMergeNode in="b"/><feMergeNode in="SourceGraphic"/></feMerge></filter>
<filter id="gl2"><feGaussianBlur stdDeviation="2" result="b"/><feMerge><feMergeNode in="b"/><feMergeNode in="SourceGraphic"/></feMerge></filter>
</defs>
<!-- 地面 -->
<rect x="0" y="420" width="560" height="140" fill="#14532d"/>
<rect x="0" y="418" width="560" height="3" fill="#22c55e" rx="1" opacity=".7"/>
<!-- 地面纹理 -->
<line x1="100" y1="440" x2="130" y2="440" stroke="#166534" stroke-width="1" opacity=".4"/>
<line x1="250" y1="450" x2="290" y2="450" stroke="#166534" stroke-width="1" opacity=".4"/>
<line x1="400" y1="435" x2="420" y2="435" stroke="#166534" stroke-width="1" opacity=".4"/>
<!-- 台阶 -->
<rect x="560" y="340" width="440" height="220" fill="url(#bk)"/>
<rect x="560" y="338" width="440" height="3" fill="#22c55e" rx="1" opacity=".7"/>
<rect x="558" y="340" width="3" height="80" fill="#a16207" rx="1" opacity=".8"/>
<!-- 台阶高度标注 -->
<line x1="548" y1="340" x2="548" y2="420" stroke="#fbbf24" stroke-width="1" stroke-dasharray="3,2" opacity=".5"/>
<line x1="543" y1="340" x2="553" y2="340" stroke="#fbbf24" stroke-width="1" opacity=".5"/>
<line x1="543" y1="420" x2="553" y2="420" stroke="#fbbf24" stroke-width="1" opacity=".5"/>
<text x="540" y="384" fill="#fbbf24" font-size="8" text-anchor="end" opacity=".6" transform="rotate(-90,540,384)">臂长 = 台阶高</text>
<!-- 水平基准线 -->
<line x1="30" y1="270" x2="970" y2="270" stroke="#475569" stroke-width=".5" stroke-dasharray="6,5" opacity=".3"/>
<text x="975" y="273" fill="#475569" font-size="7" opacity=".4">水平基准</text>
<!-- ════════ 车辆 ════════ -->
<g id="vehicle">
<!-- ── 底盘倾斜组(绕后铰接点旋转) ── -->
<g transform="translate(-70,0)">
<g id="chassisTilt">
<g transform="translate(70,0)">
<!-- 底盘本体 -->
<rect x="-92" y="-26" width="184" height="27" rx="5" fill="url(#cg)" stroke="#64748b" stroke-width="1.2"/>
<line x1="-82" y1="-18" x2="82" y2="-18" stroke="#64748b" stroke-width=".4" opacity=".3"/>
<line x1="-82" y1="-10" x2="82" y2="-10" stroke="#64748b" stroke-width=".4" opacity=".2"/>
<!-- 底盘标签 -->
<text id="chassisLabel" x="0" y="-3" text-anchor="middle" fill="#94a3b8" font-size="8" opacity=".6">底 盘</text>
<!-- ── 前行星轮组 ── -->
<g transform="translate(70,0)">
<g id="frontBracket">
<circle r="6" fill="#64748b" stroke="#94a3b8" stroke-width="1.2"/>
<circle r="2.5" fill="#94a3b8"/>
<!-- 三条臂 -->
<line x1="0" y1="6" x2="0" y2="68" stroke="#94a3b8" stroke-width="3.2" stroke-linecap="round"/>
<line x1="-5.2" y1="-3" x2="-60.1" y2="-34.6" stroke="#94a3b8" stroke-width="3.2" stroke-linecap="round"/>
<line x1="5.2" y1="-3" x2="60.1" y2="-34.6" stroke="#94a3b8" stroke-width="3.2" stroke-linecap="round"/>
<!-- 三角框架 -->
<polygon points="0,80 -69.3,-40 69.3,-40" fill="none" stroke="#64748b" stroke-width=".8" opacity=".25"/>
<!-- 三个小轮 -->
<circle cx="0" cy="80" r="12" fill="url(#wg)" stroke="#94a3b8" stroke-width="1.2"/>
<circle cx="0" cy="80" r="3.5" fill="#475569" stroke="#64748b" stroke-width=".5"/>
<circle cx="-69.3" cy="-40" r="12" fill="url(#wg)" stroke="#94a3b8" stroke-width="1.2"/>
<circle cx="-69.3" cy="-40" r="3.5" fill="#475569" stroke="#64748b" stroke-width=".5"/>
<circle cx="69.3" cy="-40" r="12" fill="url(#wg)" stroke="#94a3b8" stroke-width="1.2"/>
<circle cx="69.3" cy="-40" r="3.5" fill="#475569" stroke="#64748b" stroke-width=".5"/>
</g>
</g>
<!-- ── 后行星轮组 ── -->
<g transform="translate(-70,0)">
<g id="rearBracket">
<circle r="6" fill="#64748b" stroke="#94a3b8" stroke-width="1.2"/>
<circle r="2.5" fill="#94a3b8"/>
<line x1="0" y1="6" x2="0" y2="68" stroke="#94a3b8" stroke-width="3.2" stroke-linecap="round"/>
<line x1="-5.2" y1="-3" x2="-60.1" y2="-34.6" stroke="#94a3b8" stroke-width="3.2" stroke-linecap="round"/>
<line x1="5.2" y1="-3" x2="60.1" y2="-34.6" stroke="#94a3b8" stroke-width="3.2" stroke-linecap="round"/>
<polygon points="0,80 -69.3,-40 69.3,-40" fill="none" stroke="#64748b" stroke-width=".8" opacity=".25"/>
<circle cx="0" cy="80" r="12" fill="url(#wg)" stroke="#94a3b8" stroke-width="1.2"/>
<circle cx="0" cy="80" r="3.5" fill="#475569" stroke="#64748b" stroke-width=".5"/>
<circle cx="-69.3" cy="-40" r="12" fill="url(#wg)" stroke="#94a3b8" stroke-width="1.2"/>
<circle cx="-69.3" cy="-40" r="3.5" fill="#475569" stroke="#64748b" stroke-width=".5"/>
<circle cx="69.3" cy="-40" r="12" fill="url(#wg)" stroke="#94a3b8" stroke-width="1.2"/>
<circle cx="69.3" cy="-40" r="3.5" fill="#475569" stroke="#64748b" stroke-width=".5"/>
</g>
</g>
<!-- 云台底座(随底盘倾斜) -->
<rect id="gimbalBase" x="-38" y="-34" width="76" height="8" rx="2" fill="#dc2626" opacity=".85"/>
<circle cx="0" cy="-30" r="3.5" fill="#ef4444" stroke="#fca5a5" stroke-width=".8"/>
<!-- 云台活塞(随底盘倾斜,用于显示与平台的分离) -->
<rect id="pistonL" x="-28" y="-56" width="5" height="22" rx="2" fill="#b91c1c" opacity=".7"/>
<rect id="pistonR" x="23" y="-56" width="5" height="22" rx="2" fill="#b91c1c" opacity=".7"/>
</g>
</g>
</g>
<!-- ── 载货平台组(绕云台铰接点反向补偿旋转) ── -->
<g transform="translate(0,-38)">
<g id="platformRotator">
<g transform="translate(0,38)">
<!-- 云台顶部连接 -->
<circle id="gimbalTop" cx="0" cy="-56" r="3.5" fill="#ef4444" stroke="#fca5a5" stroke-width=".8"/>
<!-- 载货平台 -->
<rect id="platform" x="-82" y="-70" width="164" height="13" rx="3" fill="url(#pg)" stroke="#4ade80" stroke-width="1"/>
<!-- 水平仪 -->
<rect id="lvlOuter" x="-18" y="-68" width="36" height="9" rx="4.5" fill="none" stroke="#bbf7d0" stroke-width=".7" opacity=".6"/>
<circle id="lvlBubble" cx="0" cy="-63.5" r="3.2" fill="#86efac" opacity=".8"/>
<!-- 货物箱 -->
<g id="cargo">
<rect x="-26" y="-100" width="52" height="30" rx="3" fill="url(#cag)" stroke="#fcd34d" stroke-width="1"/>
<text x="0" y="-81" text-anchor="middle" fill="#78350f" font-size="10" font-weight="bold">货物</text>
</g>
</g>
</g>
</g>
<!-- ── 动态标签(随车辆移动但保持水平) ── -->
<g id="dynLabels">
<g id="gimbalLabel" opacity="0">
<rect x="-48" y="-112" width="96" height="18" rx="4" fill="#dc2626" opacity=".15"/>
<text x="0" y="-99" text-anchor="middle" fill="#fca5a5" font-size="9.5" font-weight="bold">⚡ 云台补偿中</text>
</g>
<g id="bracketLabel" opacity="0">
<text x="120" y="-10" text-anchor="start" fill="#94a3b8" font-size="8.5" opacity=".7">← 行星轮翻转120°</text>
</g>
<g id="tiltArcGroup" opacity="0">
<path id="tiltArc" d="" fill="none" stroke="#fbbf24" stroke-width="1.5" opacity=".7"/>
<text id="tiltText" x="0" y="0" fill="#fbbf24" font-size="9" text-anchor="middle" font-weight="bold"></text>
</g>
</g>
</g>
<!-- 碰撞闪光 -->
<circle id="impactFlash" r="18" fill="#fbbf24" opacity="0" filter="url(#gl)"/>
<!-- 阶段文字 -->
<text id="phaseText" x="500" y="28" text-anchor="middle" fill="#fbbf24" font-size="14" font-weight="bold" opacity="0"/>
<!-- 右下角说明 -->
<g opacity=".45">
<text x="975" y="530" text-anchor="end" fill="#94a3b8" font-size="7.5">臂长200mm · 云台响应 <20ms</text>
<text x="975" y="542" text-anchor="end" fill="#94a3b8" font-size="7">台阶高度 ≤ 臂长 → 几何翻转可越障</text>
</g>
</svg>
<div class="ctrls">
<button id="btnReplay">重新播放</button>
<button id="btnPause">暂停 / 继续</button>
<span class="plbl" id="phaseLabel">准备中…</span>
</div>
<div class="legend">
<div class="li"><div class="ld" style="background:#475569"></div>底盘</div>
<div class="li"><div class="ld" style="background:#94a3b8"></div>行星轮组</div>
<div class="li"><div class="ld" style="background:#dc2626"></div>主动云台</div>
<div class="li"><div class="ld" style="background:#22c55e"></div>载货平台</div>
<div class="li"><div class="ld" style="background:#fbbf24"></div>货物</div>
</div>
</div>
<script>
/* ──────────────────────────────────────
行星轮越障 + 主动云台补偿 原理动画
────────────────────────────────────── */
// ─── DOM ───
const vehicle = document.getElementById("vehicle");
const chassisTilt = document.getElementById("chassisTilt");
const frontBracket = document.getElementById("frontBracket");
const rearBracket = document.getElementById("rearBracket");
const platRotator = document.getElementById("platformRotator");
const cargo = document.getElementById("cargo");
const phaseText = document.getElementById("phaseText");
const phaseLabel = document.getElementById("phaseLabel");
const impactFlash = document.getElementById("impactFlash");
const gimbalLabel = document.getElementById("gimbalLabel");
const bracketLabel = document.getElementById("bracketLabel");
const tiltArcGroup = document.getElementById("tiltArcGroup");
const tiltArc = document.getElementById("tiltArc");
const tiltText = document.getElementById("tiltText");
const gimbalBase = document.getElementById("gimbalBase");
// ─── 常量 ───
const GY = 420; // 地面 y
const STY = 340; // 台阶顶 y
const STX = 560; // 台阶立面 x
const FY = GY - 80 - 12; // 328 平地时底盘铰接点 y
const SY = STY - 80 - 12; // 248 台阶顶时底盘铰接点 y
const TILT = 35; // 底盘最大倾斜角(≈atan(80/140)≈35°)
// ─── 初始位置 ───
gsap.set(vehicle, { x: 160, y: FY });
gsap.set(chassisTilt, { rotation: 0, transformOrigin: "0px 0px" });
gsap.set(frontBracket, { rotation: 0, transformOrigin: "0px 0px" });
gsap.set(rearBracket, { rotation: 0, transformOrigin: "0px 0px" });
gsap.set(platRotator, { rotation: 0, transformOrigin: "0px 0px" });
gsap.set(cargo, { rotation: 0, transformOrigin: "0px -85px" });
gsap.set(impactFlash, { x: STX, y: GY - 35, transformOrigin: "50% 50%" });
gsap.set(phaseText, { opacity: 0 });
// ─── 辅助:绘制倾斜角弧线 ───
function updateTiltArc(angle) {
const cx = -70, cy = -30, r = 28;
const rad = angle * Math.PI / 180;
const sx = cx, sy = cy - r;
const ex = cx + r * Math.sin(rad), ey = cy - r * Math.cos(rad);
const large = Math.abs(angle) > 180 ? 1 : 0;
const sweep = angle > 0 ? 1 : 0;
tiltArc.setAttribute("d", `M${sx},${sy} A${r},${r} 0 ${large},${sweep} ${ex},${ey}`);
tiltText.setAttribute("x", cx - r - 14);
tiltText.setAttribute("y", cy - 4);
tiltText.textContent = Math.abs(Math.round(angle)) + "°";
}
// ─── 主时间轴 ───
const tl = gsap.timeline({
repeat: -1,
repeatDelay: 2.5,
defaults: { ease: "power1.inOut" }
});
/* ── 阶段1:平地行驶 ── */
tl.addLabel("phase1")
.to(phaseText, { opacity: 1, duration: 0.4 })
.call(() => { phaseText.textContent = "① 平地行驶"; phaseLabel.textContent = "平地行驶"; })
.to(vehicle, { x: 488, duration: 3.2, ease: "none" }, "phase1")
/* ── 阶段2:前行星轮撞击 → 翻转跨级 ── */
.addLabel("phase2", "+=0.1")
.call(() => { phaseText.textContent = "② 前轮组撞击台阶 · 支架翻转跨级"; phaseLabel.textContent = "行星支架翻转跨级"; })
// 碰撞闪光
.set(impactFlash, { x: STX, y: GY - 40, scale: 0.3, opacity: 0 })
.to(impactFlash, { opacity: 0.9, scale: 1.2, duration: 0.12 }, "phase2")
.to(impactFlash, { opacity: 0, scale: 1.8, duration: 0.35 }, "phase2+=0.12")
// 底盘绕后铰接点倾斜
.to(chassisTilt, { rotation: -TILT, duration: 2.4, ease: "power2.in",
onUpdate: function() {
const a = gsap.getProperty(chassisTilt, "rotation");
updateTiltArc(a);
}
}, "phase2")
// 前支架翻转120°
.to(frontBracket, { rotation: 120, duration: 2.4, ease: "power2.inOut" }, "phase2")
// 车辆继续前行
.to(vehicle, { x: 540, duration: 2.4, ease: "none" }, "phase2")
// 云台反向补偿(平台保持水平)
.to(platRotator, { rotation: TILT, duration: 2.4, ease: "power2.in" }, "phase2")
// 货物微颤(体现极短延迟)
.to(cargo, { rotation: 1.5, duration: 0.08, ease: "power4.out" }, "phase2+=0.08")
.to(cargo, { rotation: -0.8, duration: 0.1 }, "phase2+=0.16")
.to(cargo, { rotation: 0, duration: 0.25, ease: "power2.out" }, "phase2+=0.26")
// 显示动态标签
.to(gimbalLabel, { opacity: 1, duration: 0.3 }, "phase2+=0.3")
.to(bracketLabel, { opacity: 1, duration: 0.3 }, "phase2+=0.3")
.to(tiltArcGroup, { opacity: 1, duration: 0.3 }, "phase2+=0.3")
// 云台底座发光
.to(gimbalBase, { attr: { opacity: 1 }, duration: 0.3 }, "phase2+=0.2")
.call(() => { gimbalBase.classList.add("gimbal-pulse"); })
/* ── 阶段3:后行星轮跨级 ── */
.addLabel("phase3", "+=0.2")
.call(() => { phaseText.textContent = "③ 后轮组跨级 · 云台持续补偿"; phaseLabel.textContent = "后轮跨级 · 云台补偿"; })
// 车辆整体上升至台阶顶
.to(vehicle, { y: SY, duration: 2.4, ease: "power1.inOut" }, "phase3")
// 底盘恢复水平
.to(chassisTilt, { rotation: 0, duration: 2.4, ease: "power2.out",
onUpdate: function() {
const a = gsap.getProperty(chassisTilt, "rotation");
updateTiltArc(a);
}
}, "phase3")
// 后支架翻转120°
.to(rearBracket, { rotation: 120, duration: 2.4, ease: "power2.inOut" }, "phase3")
// 车辆继续前行
.to(vehicle, { x: 620, duration: 2.4, ease: "none" }, "phase3")
// 平台恢复0°
.to(platRotator, { rotation: 0, duration: 2.4, ease: "power2.out" }, "phase3")
// 货物微颤
.to(cargo, { rotation: 1, duration: 0.08 }, "phase3+=0.08")
.to(cargo, { rotation: -0.5, duration: 0.1 }, "phase3+=0.16")
.to(cargo, { rotation: 0, duration: 0.2 }, "phase3+=0.26")
// 隐藏标签
.to(gimbalLabel, { opacity: 0, duration: 0.5 }, "phase3+=1.5")
.to(bracketLabel, { opacity: 0, duration: 0.5 }, "phase3+=1.5")
.to(tiltArcGroup, { opacity: 0, duration: 0.5 }, "phase3+=1.5")
.call(() => { gimbalBase.classList.remove("gimbal-pulse"); })
.to(gimbalBase, { attr: { opacity: 0.85 }, duration: 0.3 }, "phase3+=1.8")
/* ── 阶段4:平稳驶离 ── */
.addLabel("phase4", "+=0.1")
.call(() => { phaseText.textContent = "④ 完成跨级 · 平稳驶离"; phaseLabel.textContent = "完成跨级"; })
.to(vehicle, { x: 850, duration: 2.8, ease: "none" }, "phase4")
/* ── 重置 ── */
.addLabel("reset")
.to(phaseText, { opacity: 0, duration: 0.5 })
.call(() => { phaseLabel.textContent = "准备重播…"; })
.set(vehicle, { x: 160, y: FY })
.set(chassisTilt, { rotation: 0 })
.set(frontBracket, { rotation: 0 })
.set(rearBracket, { rotation: 0 })
.set(platRotator, { rotation: 0 })
.set(cargo, { rotation: 0 })
.set(gimbalLabel, { opacity: 0 })
.set(bracketLabel, { opacity: 0 })
.set(tiltArcGroup, { opacity: 0 });
// ─── 控制 ───
document.getElementById("btnReplay").addEventListener("click", () => { tl.restart(); });
document.getElementById("btnPause").addEventListener("click", () => {
tl.isActive() ? tl.pause() : tl.resume();
});
</script>
</body>
</html>
实现说明:
层级解耦建模:底盘(含行星轮组)与载货平台分为独立图层。底盘倾斜组
chassisTilt通过嵌套translate(-70,0)将旋转中心锁定在后铰接点,用transformOrigin: "0px 0px"确保绕后枢轴旋转;平台组platformRotator独立补偿旋转,绕云台铰接点反向转动。行星轮翻转:前/后支架各自以铰接点为圆心旋转 120°,三小轮均布 120° 的三角框架随公转翻转,视觉上呈现"上方小轮越过台阶边缘成为新支撑点"的跨步过程。
云台补偿时机:底盘倾斜与平台反向补偿在同一时间段内同步进行(
"<"同步启动),货物仅有极短暂的微颤(<0.3s 衰减),体现 <20ms 响应延迟的约束。视觉引导:碰撞瞬间有黄色闪光;云台底座在补偿期间红色脉冲发光;倾斜角弧线与角度数值实时更新;平台上水平仪气泡始终居中,直观表达"绝对水平"。
边界条件:台阶高度 = 行星臂长(80px),右侧标注"臂长 = 台阶高"提示几何极限;底部注释说明越障失效条件。
积分规则:第一轮对话扣减8分,后续每轮扣6分
等待动画代码生成...
