独立渲染引擎就绪引擎就绪
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>多腔体挤压铝型材电池包边框 · IFR原理动画</title>
<link href="https://fonts.googleapis.com/css2?family=Rajdhani:wght@300;500;700&family=Share+Tech+Mono&display=swap" rel="stylesheet">
<style>
*{margin:0;padding:0;box-sizing:border-box}
:root{
--bg:#060a12;--grid:#0c1422;--accent:#00e5ff;--gold:#ffd740;
--hv:#ff4455;--lv:#3399ff;--struct:#c8d4e4;--text:#d8e0f0;
--dim:#3a4a60;--al-light:#d0d8e8;--al-mid:#9aa8bc;--al-dark:#6a7888;
}
body{background:var(--bg);color:var(--text);font-family:'Rajdhani',sans-serif;
min-height:100vh;display:flex;flex-direction:column;align-items:center;
justify-content:center;overflow:hidden;position:relative}
#wrap{width:100vw;height:100vh;position:relative}
svg#scene{width:100%;height:100%;display:block}
.phase-bar{position:fixed;bottom:24px;left:50%;transform:translateX(-50%);
display:flex;gap:6px;z-index:10}
.phase-dot{width:48px;height:4px;border-radius:2px;background:var(--dim);
transition:background .4s,box-shadow .4s}
.phase-dot.active{background:var(--accent);box-shadow:0 0 8px var(--accent)}
.phase-dot.done{background:rgba(0,229,255,.35)}
.ctrl-bar{position:fixed;top:20px;right:24px;display:flex;gap:12px;
align-items:center;z-index:10;font-family:'Share Tech Mono',monospace;font-size:13px}
.ctrl-bar label{color:var(--dim)}
.ctrl-bar input[type=range]{width:80px;accent-color:var(--accent)}
.ctrl-bar button{background:rgba(0,229,255,.1);border:1px solid rgba(0,229,255,.3);
color:var(--accent);padding:4px 12px;border-radius:4px;cursor:pointer;
font-family:'Share Tech Mono',monospace;font-size:12px;transition:all .2s}
.ctrl-bar button:hover{background:rgba(0,229,255,.2);border-color:var(--accent)}
.info-tag{position:fixed;top:20px;left:24px;z-index:10;
font-family:'Share Tech Mono',monospace;font-size:12px;color:var(--dim);
letter-spacing:.5px}
@keyframes pulseGlow{0%,100%{opacity:.3}50%{opacity:.8}}
@keyframes dashFlow{to{stroke-dashoffset:-24}}
</style>
</head>
<body>
<div id="wrap">
<svg id="scene" viewBox="0 0 1400 900" preserveAspectRatio="xMidYMid meet">
<defs>
<!-- 网格图案 -->
<pattern id="grid" width="40" height="40" patternUnits="userSpaceOnUse">
<path d="M40 0L0 0 0 40" fill="none" stroke="#0e1828" stroke-width=".5"/>
</pattern>
<!-- 铝合金渐变 -->
<linearGradient id="alGrad" x1="0" y1="0" x2=".8" y2="1">
<stop offset="0%" stop-color="#d4dce8"/><stop offset="35%" stop-color="#b0b8c8"/>
<stop offset="55%" stop-color="#c4ccd8"/><stop offset="80%" stop-color="#96a0b2"/>
<stop offset="100%" stop-color="#b8c2d0"/>
</linearGradient>
<linearGradient id="alGradV" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#c8d0dc"/><stop offset="50%" stop-color="#a8b0c0"/>
<stop offset="100%" stop-color="#bcc4d2"/>
</linearGradient>
<!-- 腔室填充渐变 -->
<linearGradient id="hvFill" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="rgba(255,68,85,.22)"/><stop offset="100%" stop-color="rgba(255,68,85,.08)"/>
</linearGradient>
<linearGradient id="lvFill" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="rgba(51,153,255,.22)"/><stop offset="100%" stop-color="rgba(51,153,255,.08)"/>
</linearGradient>
<linearGradient id="mainFill" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="rgba(200,212,228,.1)"/><stop offset="100%" stop-color="rgba(200,212,228,.04)"/>
</linearGradient>
<!-- 发光滤镜 -->
<filter id="glowCyan" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur in="SourceGraphic" stdDeviation="4" result="b"/>
<feMerge><feMergeNode in="b"/><feMergeNode in="SourceGraphic"/></feMerge>
</filter>
<filter id="glowRed" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur in="SourceGraphic" stdDeviation="6" result="b"/>
<feMerge><feMergeNode in="b"/><feMergeNode in="SourceGraphic"/></feMerge>
</filter>
<filter id="glowBlue" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur in="SourceGraphic" stdDeviation="6" result="b"/>
<feMerge><feMergeNode in="b"/><feMergeNode in="SourceGraphic"/></feMerge>
</filter>
<filter id="glowGold" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur in="SourceGraphic" stdDeviation="5" result="b"/>
<feMerge><feMergeNode in="b"/><feMergeNode in="SourceGraphic"/></feMerge>
</filter>
<filter id="shadowSm">
<feDropShadow dx="0" dy="2" stdDeviation="3" flood-color="#000" flood-opacity=".4"/>
</filter>
<!-- 线束流动标记 -->
<marker id="arrowCyan" markerWidth="8" markerHeight="6" refX="8" refY="3" orient="auto">
<path d="M0,0 L8,3 L0,6" fill="none" stroke="var(--accent)" stroke-width="1"/>
</marker>
</defs>
<!-- 背景 -->
<rect width="1400" height="900" fill="var(--bg)"/>
<rect width="1400" height="900" fill="url(#grid)"/>
<!-- ====== 标题 ====== -->
<text x="700" y="52" text-anchor="middle" font-family="Rajdhani,sans-serif"
font-weight="700" font-size="28" fill="var(--text)" letter-spacing="3"
opacity="0" id="mainTitle">多腔体挤压铝型材 · 三合一电池包边框</text>
<text x="700" y="78" text-anchor="middle" font-family="Share Tech Mono,monospace"
font-size="13" fill="var(--dim)" letter-spacing="1" opacity="0" id="subTitle">
IFR: 一材承结构 · 走线 · 吊装 —— 消除独立框架/吊耳/线槽的冗余</text>
<!-- ====== 截面视图 ====== -->
<g id="xsectGroup" transform="translate(330,470)" opacity="0">
<!-- 型材主体 -->
<rect x="-250" y="-160" width="500" height="320" rx="2"
fill="url(#alGrad)" stroke="#7a8a9e" stroke-width="1.2" id="profileBody"/>
<!-- 低压信号线束腔 -->
<rect id="lvChamber" x="-236" y="-146" width="130" height="292" rx="1"
fill="url(#lvFill)" opacity="0"/>
<!-- 主承重腔 -->
<rect id="mainChamber" x="-90" y="-146" width="180" height="292" rx="1"
fill="url(#mainFill)" opacity="0"/>
<!-- 高压线束腔 -->
<rect id="hvChamber" x="106" y="-146" width="130" height="292" rx="1"
fill="url(#hvFill)" opacity="0"/>
<!-- 内部隔墙 -->
<rect x="-106" y="-160" width="16" height="320" fill="url(#alGradV)"
stroke="#7a8a9e" stroke-width=".6" id="partition1" opacity="0"/>
<rect x="90" y="-160" width="16" height="320" fill="url(#alGradV)"
stroke="#7a8a9e" stroke-width=".6" id="partition2" opacity="0"/>
<!-- T型槽 (外左侧壁) -->
<g id="tSlotGroup" opacity="0">
<rect x="-250" y="-22" width="18" height="44" fill="var(--bg)"/>
<rect x="-250" y="-14" width="10" height="28" fill="#0a1020"/>
<rect x="-250" y="-10" width="26" height="20" rx="1" fill="#0a1020"/>
<line x1="-250" y1="-22" x2="-250" y2="22" stroke="#5a6a80" stroke-width=".8"/>
<text x="-268" y="4" text-anchor="end" font-family="Share Tech Mono,monospace"
font-size="11" fill="var(--accent)">T型槽</text>
<text x="-268" y="18" text-anchor="end" font-family="Share Tech Mono,monospace"
font-size="9" fill="var(--dim)">8mm</text>
</g>
<!-- 吊装孔 (顶部) -->
<g id="liftHoleGroup" opacity="0">
<circle cx="0" cy="-153" r="12" fill="var(--bg)"/>
<circle cx="0" cy="-160" r="16" fill="#0a1020" stroke="#5a6a80" stroke-width=".6"/>
<circle cx="0" cy="-153" r="12" fill="none" stroke="#5a6a80" stroke-width=".6"/>
<!-- 防脱挡圈 -->
<path d="M-14,-160 A16,16 0 0,1 14,-160" fill="none" stroke="var(--gold)"
stroke-width="1.5" stroke-dasharray="3,2"/>
<text x="0" y="-176" text-anchor="middle" font-family="Share Tech Mono,monospace"
font-size="11" fill="var(--gold)">吊装孔</text>
<text x="0" y="-190" text-anchor="middle" font-family="Share Tech Mono,monospace"
font-size="9" fill="var(--dim)">间距 300mm</text>
</g>
<!-- 腔室标签 -->
<g id="chamberLabels" opacity="0">
<text x="-171" y="-6" text-anchor="middle" font-family="Rajdhani,sans-serif"
font-weight="500" font-size="14" fill="var(--lv)">低压信号</text>
<text x="-171" y="12" text-anchor="middle" font-family="Rajdhani,sans-serif"
font-weight="300" font-size="11" fill="rgba(51,153,255,.6)">线束腔</text>
<text x="0" y="-6" text-anchor="middle" font-family="Rajdhani,sans-serif"
font-weight="500" font-size="14" fill="var(--struct)">主承重</text>
<text x="0" y="12" text-anchor="middle" font-family="Rajdhani,sans-serif"
font-weight="300" font-size="11" fill="rgba(200,212,228,.5)">结构腔</text>
<text x="171" y="-6" text-anchor="middle" font-family="Rajdhani,sans-serif"
font-weight="500" font-size="14" fill="var(--hv)">高压线束</text>
<text x="171" y="12" text-anchor="middle" font-family="Rajdhani,sans-serif"
font-weight="300" font-size="11" fill="rgba(255,68,85,.6)">走线腔</text>
</g>
<!-- 尺寸标注 -->
<g id="dimLines" opacity="0">
<!-- 壁厚标注 -->
<line x1="252" y1="-160" x2="252" y2="-146" stroke="var(--dim)" stroke-width=".5"/>
<line x1="248" y1="-160" x2="256" y2="-160" stroke="var(--dim)" stroke-width=".5"/>
<line x1="248" y1="-146" x2="256" y2="-146" stroke="var(--dim)" stroke-width=".5"/>
<text x="260" y="-149" font-family="Share Tech Mono,monospace" font-size="9"
fill="var(--dim)">2.5mm</text>
<!-- 隔离壁厚 -->
<line x1="-98" y1="168" x2="-82" y2="168" stroke="var(--dim)" stroke-width=".5"/>
<line x1="-98" y1="164" x2="-98" y2="172" stroke="var(--dim)" stroke-width=".5"/>
<line x1="-82" y1="164" x2="-82" y2="172" stroke="var(--dim)" stroke-width=".5"/>
<text x="-90" y="182" text-anchor="middle" font-family="Share Tech Mono,monospace"
font-size="9" fill="var(--dim)">3mm</text>
</g>
<!-- ====== 线束走线动画路径 ====== -->
<!-- 高压线束路径 (红色) -->
<path id="hvWire" d="M236,0 L106,0 C80,0 60,-20 60,-60 L60,-120 C60,-140 40,-146 20,-146 L-200,-146"
fill="none" stroke="var(--hv)" stroke-width="3" stroke-linecap="round"
stroke-dasharray="8,16" opacity="0" filter="url(#glowRed)"/>
<!-- 低压信号线束路径 (蓝色) -->
<path id="lvWire" d="M-236,20 L-236,80 C-236,100 -220,120 -200,120 L100,120 C120,120 130,110 130,90"
fill="none" stroke="var(--lv)" stroke-width="2.5" stroke-linecap="round"
stroke-dasharray="6,12" opacity="0" filter="url(#glowBlue)"/>
<!-- ====== 吊装钩动画 ====== -->
<g id="liftHook" transform="translate(0,-160)" opacity="0">
<line x1="0" y1="-60" x2="0" y2="-20" stroke="#8899aa" stroke-width="2.5"/>
<path d="M-8,-20 C-8,-6 8,-6 8,-20" fill="none" stroke="var(--gold)"
stroke-width="3" stroke-linecap="round"/>
<circle cx="0" cy="-60" r="5" fill="none" stroke="#8899aa" stroke-width="1.5"/>
</g>
<!-- ====== T型槽滑块螺母 ====== -->
<g id="tSlider" opacity="0">
<rect x="-236" y="-10" width="20" height="20" rx="2" fill="var(--gold)"
stroke="#b8960a" stroke-width=".8"/>
<line x1="-226" y1="-4" x2="-226" y2="4" stroke="#8a7000" stroke-width="1.5"/>
<!-- 螺栓 -->
<rect x="-230" y="10" width="8" height="18" rx="1" fill="#998866"
stroke="#776644" stroke-width=".5"/>
<line x1="-226" y1="10" x2="-226" y2="28" stroke="#665533" stroke-width="1"/>
</g>
</g>
<!-- ====== 等轴测框架视图 ====== -->
<g id="isoGroup" opacity="0">
<!-- 由JS动态生成 -->
</g>
<!-- ====== 阶段标签 ====== -->
<text id="phaseLabel" x="700" y="860" text-anchor="middle"
font-family="Rajdhani,sans-serif" font-weight="700" font-size="20"
fill="var(--accent)" opacity="0" letter-spacing="2"></text>
<!-- ====== IFR 总结文字 ====== -->
<g id="ifrSummary" opacity="0">
<text x="700" y="830" text-anchor="middle" font-family="Rajdhani,sans-serif"
font-weight="700" font-size="22" fill="var(--accent)" letter-spacing="4">
最终理想解:一材三用 · 消除冗余</text>
<text x="700" y="856" text-anchor="middle" font-family="Share Tech Mono,monospace"
font-size="12" fill="var(--dim)" letter-spacing="1">
结构骨架 ═ 走线通道 ═ 吊装支点 — 零附加零件</text>
</g>
<!-- ====== 力流指示箭头 ====== -->
<g id="forceArrows" opacity="0">
<!-- 由JS动态生成 -->
</g>
<!-- ====== 高低压隔离指示 ====== -->
<g id="isolationIndicator" opacity="0">
<rect x="-106" y="-160" width="16" height="320" fill="none"
stroke="var(--accent)" stroke-width="2" stroke-dasharray="4,4"
filter="url(#glowCyan)" transform="translate(330,470)"/>
<rect x="90" y="-160" width="16" height="320" fill="none"
stroke="var(--accent)" stroke-width="2" stroke-dasharray="4,4"
filter="url(#glowCyan)" transform="translate(330,470)"/>
<text x="330" y="290" text-anchor="middle" font-family="Share Tech Mono,monospace"
font-size="11" fill="var(--accent)" letter-spacing="1">高低压隔离壁</text>
</g>
</svg>
</div>
<!-- 阶段指示条 -->
<div class="phase-bar" id="phaseBar"></div>
<!-- 控制栏 -->
<div class="ctrl-bar">
<label>速度</label>
<input type="range" id="speedSlider" min="0.3" max="2" step="0.1" value="1">
<span id="speedVal" style="color:var(--accent);min-width:32px">1.0x</span>
<button id="pauseBtn">暂停</button>
<button id="resetBtn">重置</button>
</div>
<!-- 信息标签 -->
<div class="info-tag" id="infoTag">IFR PRINCIPLE ANIMATION</div>
<script>
(function(){
/* ===== 常量与配置 ===== */
const TOTAL = 26000; // 总周期 26s
const PHASES = [
{id:'structure', start:0, end:4500, label:'型材截面解剖'},
{id:'chambers', start:4500, end:9000, label:'三腔室功能集成'},
{id:'wiring', start:9000, end:14500, label:'高低压线束走线'},
{id:'lifting', start:14500,end:19500, label:'集成吊装受力'},
{id:'mounting', start:19500,end:23500, label:'T型槽滑块固定'},
{id:'summary', start:23500,end:26000, label:'最终理想解'}
];
/* ===== DOM引用 ===== */
const $ = id => document.getElementById(id);
const svg = $('scene');
const xsectGroup = $('xsectGroup');
const isoGroup = $('isoGroup');
const phaseLabel = $('phaseLabel');
const ifrSummary = $('ifrSummary');
const forceArrows = $('forceArrows');
const isolationIndicator = $('isolationIndicator');
/* ===== 等轴测投影函数 ===== */
const ISO_CX = 1080, ISO_CY = 460, ISO_SCALE = 0.75;
function iso(x, y, z) {
const px = ISO_CX + (x - y) * 0.866 * ISO_SCALE;
const py = ISO_CY + (x + y) * 0.5 * ISO_SCALE - z * ISO_SCALE;
return [px, py];
}
function pts(arr){ return arr.map(p => p[0]+','+p[1]).join(' '); }
/* ===== 构建等轴测框架 ===== */
function buildIsoFrame(){
const ns = 'http://www.w3.org/2000/svg';
// 框架尺寸
const FW=220, FD=160, FH=55, PT=18;
// 四根型材梁
const beams = [
// 前梁 (沿x)
{x:0,y:0,w:FW,d:PT,h:FH,color:'url(#alGrad)'},
// 后梁
{x:0,y:FD-PT,w:FW,d:PT,h:FH,color:'url(#alGrad)'},
// 左梁 (沿y)
{x:0,y:0,w:PT,d:FD,h:FH,color:'url(#alGradV)'},
// 右梁
{x:FW-PT,y:0,w:PT,d:FD,h:FH,color:'url(#alGradV)'}
];
const modules = [
{x:PT+6,y:PT+6,w:FW-2*PT-12,d:FD-2*PT-12,h:FH-10,
color:'#1a2236',stroke:'#2a3a56'}
];
// 绘制模块(先画,在梁下面)
modules.forEach(m => {
const b0=iso(m.x,m.y,m.h), b1=iso(m.x+m.w,m.y,m.h),
b2=iso(m.x+m.w,m.y+m.d,m.h), b3=iso(m.x,m.y+m.d,m.h);
const t0=iso(m.x,m.y,0), t1=iso(m.x+m.w,m.y,0),
t2=iso(m.x+m.w,m.y+m.d,0), t3=iso(m.x,m.y+m.d,0);
// 顶面
const topFace = document.createElementNS(ns,'polygon');
topFace.setAttribute('points',pts([b0,b1,b2,b3]));
topFace.setAttribute('fill',m.color);
topFace.setAttribute('stroke',m.stroke);
topFace.setAttribute('stroke-width','1');
topFace.setAttribute('opacity','0.6');
isoGroup.appendChild(topFace);
// 前面
const frontFace = document.createElementNS(ns,'polygon');
frontFace.setAttribute('points',pts([b0,b1,t1,t0]));
frontFace.setAttribute('fill','#141e30');
frontFace.setAttribute('stroke',m.stroke);
frontFace.setAttribute('stroke-width','.8');
frontFace.setAttribute('opacity','0.7');
isoGroup.appendChild(frontFace);
// 右面
const rightFace = document.createElementNS(ns,'polygon');
rightFace.setAttribute('points',pts([b1,b2,t2,t1]));
rightFace.setAttribute('fill','#101828');
rightFace.setAttribute('stroke',m.stroke);
rightFace.setAttribute('stroke-width','.8');
rightFace.setAttribute('opacity','0.5');
isoGroup.appendChild(rightFace);
// 模组标签
const cx = (b0[0]+b2[0])/2, cy = (b0[1]+b2[1])/2;
const label = document.createElementNS(ns,'text');
label.setAttribute('x', cx);
label.setAttribute('y', cy+4);
label.setAttribute('text-anchor','middle');
label.setAttribute('font-family','Rajdhani,sans-serif');
label.setAttribute('font-size','13');
label.setAttribute('fill','rgba(200,212,228,.5)');
label.setAttribute('font-weight','500');
label.textContent = '电芯模组';
isoGroup.appendChild(label);
});
// 绘制梁
beams.forEach((b,i) => {
const corners = [
iso(b.x,b.y,b.h), iso(b.x+b.w,b.y,b.h),
iso(b.x+b.w,b.y+b.d,b.h), iso(b.x,b.y+b.d,b.h),
iso(b.x,b.y,0), iso(b.x+b.w,b.y,0),
iso(b.x+b.w,b.y+b.d,0), iso(b.x,b.y+b.d,0)
];
// 顶面
const top = document.createElementNS(ns,'polygon');
top.setAttribute('points',pts([corners[0],corners[1],corners[2],corners[3]]));
top.setAttribute('fill','#b8c4d4');
top.setAttribute('stroke','#8898ac');
top.setAttribute('stroke-width','1');
top.setAttribute('opacity','0.9');
top.classList.add('iso-beam-top');
isoGroup.appendChild(top);
// 前面
const front = document.createElementNS(ns,'polygon');
front.setAttribute('points',pts([corners[4],corners[5],corners[1],corners[0]]));
front.setAttribute('fill','#8a98ac');
front.setAttribute('stroke','#7a8898');
front.setAttribute('stroke-width','.8');
front.setAttribute('opacity','0.85');
front.classList.add('iso-beam-front');
isoGroup.appendChild(front);
// 右面
const right = document.createElementNS(ns,'polygon');
right.setAttribute('points',pts([corners[5],corners[6],corners[2],corners[1]]));
right.setAttribute('fill','#6a7888');
right.setAttribute('stroke','#5a6878');
right.setAttribute('stroke-width','.8');
right.setAttribute('opacity','0.75');
right.classList.add('iso-beam-right');
isoGroup.appendChild(right);
});
// 角件标记
const corners3d = [[0,0],[FW-PT,0],[FW-PT,FD-PT],[0,FD-PT]];
corners3d.forEach(c => {
const [px,py] = iso(c[0]+PT/2, c[1]+PT/2, FH+2);
const circle = document.createElementNS(ns,'circle');
circle.setAttribute('cx',px); circle.setAttribute('cy',py);
circle.setAttribute('r','4');
circle.setAttribute('fill','var(--accent)');
circle.setAttribute('opacity','0.7');
circle.classList.add('iso-corner');
isoGroup.appendChild(circle);
});
// 等轴测标签
const [lx,ly] = iso(FW/2, -20, FH+20);
const isoLabel = document.createElementNS(ns,'text');
isoLabel.setAttribute('x', lx);
isoLabel.setAttribute('y', ly);
isoLabel.setAttribute('text-anchor','middle');
isoLabel.setAttribute('font-family','Share Tech Mono,monospace');
isoLabel.setAttribute('font-size','12');
isoLabel.setAttribute('fill','var(--dim)');
isoLabel.textContent = '等轴测装配视图';
isoGroup.appendChild(isoLabel);
}
/* ===== 构建阶段指示条 ===== */
function buildPhaseBar(){
const bar = $('phaseBar');
PHASES.forEach((p,i) => {
const dot = document.createElement('div');
dot.className = 'phase-dot';
dot.id = 'pdot-'+i;
dot.title = p.label;
bar.appendChild(dot);
});
}
/* ===== 辅助动画函数 ===== */
function ease(t){ return t<.5?2*t*t:-1+(4-2*t)*t; }
function lerp(a,b,t){ return a+(b-a)*t; }
function clamp01(t){ return Math.max(0,Math.min(1,t)); }
function fadeIn(el,dur,delay){
el.style.transition = `opacity ${dur}ms ease ${delay}ms`;
el.setAttribute('opacity','1');
}
function fadeOut(el,dur){
el.style.transition = `opacity ${dur}ms ease`;
el.setAttribute('opacity','0');
}
function setOp(el,v){ el.setAttribute('opacity',String(v)); }
/* ===== 动画状态 ===== */
let startTime = null;
let paused = false;
let pauseTime = 0;
let speed = 1;
let rafId = null;
/* ===== 阶段更新函数 ===== */
function updatePhase(elapsed){
const t = elapsed % TOTAL;
let phaseIdx = PHASES.length - 1;
for(let i=0;i<PHASES.length;i++){
if(t >= PHASES[i].start && t < PHASES[i].end){ phaseIdx = i; break; }
}
const phase = PHASES[phaseIdx];
const progress = clamp01((t - phase.start)/(phase.end - phase.start));
// 更新阶段指示条
PHASES.forEach((p,i) => {
const dot = $('pdot-'+i);
if(!dot) return;
dot.className = 'phase-dot' + (i<phaseIdx?' done':'') + (i===phaseIdx?' active':'');
});
// 更新阶段标签
phaseLabel.textContent = phase.label;
setOp(phaseLabel, progress < 0.1 ? ease(progress/0.1) : (progress > 0.9 ? ease((1-progress)/0.1) : 1));
// 根据阶段执行动画
switch(phase.id){
case 'structure': animStructure(progress, t); break;
case 'chambers': animChambers(progress, t); break;
case 'wiring': animWiring(progress, t); break;
case 'lifting': animLifting(progress, t); break;
case 'mounting': animMounting(progress, t); break;
case 'summary': animSummary(progress, t); break;
}
}
/* ===== 阶段1: 型材截面解剖 ===== */
let structureInit = false;
function animStructure(p, t){
if(!structureInit){
structureInit = true;
// 重置所有元素
setOp(xsectGroup, 1);
setOp($('profileBody'), 1);
setOp($('lvChamber'), 0);
setOp($('mainChamber'), 0);
setOp($('hvChamber'), 0);
setOp($('partition1'), 0);
setOp($('partition2'), 0);
setOp($('tSlotGroup'), 0);
setOp($('liftHoleGroup'), 0);
setOp($('chamberLabels'), 0);
setOp($('dimLines'), 0);
setOp($('hvWire'), 0);
setOp($('lvWire'), 0);
setOp($('liftHook'), 0);
setOp($('tSlider'), 0);
setOp(isoGroup, 0);
setOp(ifrSummary, 0);
setOp(forceArrows, 0);
setOp(isolationIndicator, 0);
setOp($('mainTitle'), 0);
setOp($('subTitle'), 0);
}
// 标题淡入
setOp($('mainTitle'), clamp01(p/0.15));
setOp($('subTitle'), clamp01((p-0.05)/0.15));
// 截面组淡入
setOp(xsectGroup, clamp01(p/0.1));
// 隔墙出现
setOp($('partition1'), clamp01((p-0.15)/0.1));
setOp($('partition2'), clamp01((p-0.2)/0.1));
// 尺寸标注
setOp($('dimLines'), clamp01((p-0.4)/0.15));
// T型槽
setOp($('tSlotGroup'), clamp01((p-0.6)/0.12));
// 吊装孔
setOp($('liftHoleGroup'), clamp01((p-0.75)/0.12));
// 等轴测框架淡入
setOp(isoGroup, clamp01((p-0.5)/0.2));
}
/* ===== 阶段2: 三腔室功能集成 ===== */
let chambersInit = false;
function animChambers(p, t){
if(!chambersInit){
chambersInit = true;
// 确保基础元素可见
setOp(xsectGroup, 1);
setOp($('partition1'), 1);
setOp($('partition2'), 1);
setOp($('dimLines'), 1);
setOp(isoGroup, 1);
}
// 三个腔室依次高亮
const lvP = clamp01((p - 0.0) / 0.2);
const mainP = clamp01((p - 0.25) / 0.2);
const hvP = clamp01((p - 0.5) / 0.2);
setOp($('lvChamber'), lvP);
setOp($('mainChamber'), mainP);
setOp($('hvChamber'), hvP);
// 标签
setOp($('chamberLabels'), clamp01((p - 0.6) / 0.15));
// 高低压隔离指示 (后期出现)
setOp(isolationIndicator, clamp01((p - 0.75) / 0.15));
}
/* ===== 阶段3: 高低压线束走线 ===== */
let wiringInit = false;
function animWiring(p, t){
if(!wiringInit){
wiringInit = true;
setOp($('lvChamber'), 1);
setOp($('mainChamber'), 1);
setOp($('hvChamber'), 1);
setOp($('chamberLabels'), 1);
setOp(isolationIndicator, 0.8);
}
// 高压线动画 (红色)
const hvP = clamp01((p - 0.05) / 0.4);
setOp($('hvWire'), hvP > 0 ? 0.9 : 0);
if(hvP > 0){
const wire = $('hvWire');
const len = wire.getTotalLength ? wire.getTotalLength() : 400;
wire.setAttribute('stroke-dashoffset', String(-t/40));
}
// 低压线动画 (蓝色)
const lvP = clamp01((p - 0.3) / 0.4);
setOp($('lvWire'), lvP > 0 ? 0.9 : 0);
if(lvP > 0){
const wire = $('lvWire');
wire.setAttribute('stroke-dashoffset', String(-t/50));
}
// 隔离壁闪烁强调
if(p > 0.7){
const blink = 0.5 + 0.5 * Math.sin(t / 200);
setOp(isolationIndicator, blink * 0.8);
}
}
/* ===== 阶段4: 集成吊装受力 ===== */
let liftingInit = false;
function animLifting(p, t){
if(!liftingInit){
liftingInit = true;
setOp($('hvWire'), 0.6);
setOp($('lvWire'), 0.6);
setOp(isolationIndicator, 0.3);
// 线束停止流动
}
// 吊钩下降
const hookP = clamp01(p / 0.3);
const hookY = lerp(-50, 0, ease(hookP));
$('liftHook').setAttribute('transform', `translate(0,${-160 + hookY - 10})`);
setOp($('liftHook'), clamp01(p / 0.15));
// 框架微微抬起 (表示受力)
if(p > 0.4){
const liftP = ease(clamp01((p - 0.4) / 0.25));
const liftY = -8 * liftP + Math.sin(t/300) * 1.5 * liftP;
xsectGroup.setAttribute('transform', `translate(330,${470 + liftY})`);
isoGroup.setAttribute('transform', `translate(0,${liftY})`);
}
// 力流箭头
setOp(forceArrows, clamp01((p - 0.5) / 0.15));
if(p > 0.5 && forceArrows.childElementCount === 0){
buildForceArrows();
}
}
/* ===== 阶段5: T型槽滑块固定 ===== */
let mountingInit = false;
function animMounting(p, t){
if(!mountingInit){
mountingInit = true;
// 恢复框架位置
xsectGroup.setAttribute('transform', 'translate(330,470)');
isoGroup.setAttribute('transform', 'translate(0,0)');
setOp($('liftHook'), 0.4);
}
// T型槽高亮
setOp($('tSlotGroup'), 0.5 + 0.5 * Math.sin(t / 200));
// 滑块螺母滑入
const sliderP = ease(clamp01((p - 0.1) / 0.35));
const sliderX = lerp(-300, -236, sliderP);
$('tSlider').setAttribute('transform', `translate(${sliderX - (-236)},0)`);
setOp($('tSlider'), clamp01(p / 0.1));
// 底板 (在等轴测视图中)
if(p > 0.6 && !document.querySelector('.base-plate')){
buildBasePlate();
}
}
/* ===== 阶段6: 最终理想解 ===== */
let summaryInit = false;
function animSummary(p, t){
if(!summaryInit){
summaryInit = true;
}
// 所有功能同时高亮
setOp($('lvChamber'), 0.8 + 0.2 * Math.sin(t/300));
setOp($('mainChamber'), 0.8 + 0.2 * Math.sin(t/300 + 1));
setOp($('hvChamber'), 0.8 + 0.2 * Math.sin(t/300 + 2));
setOp($('chamberLabels'), 1);
setOp($('tSlotGroup'), 0.7 + 0.3 * Math.sin(t/250));
setOp($('liftHoleGroup'), 0.7 + 0.3 * Math.sin(t/250 + 1));
setOp($('hvWire'), 0.6);
setOp($('lvWire'), 0.6);
$('hvWire').setAttribute('stroke-dashoffset', String(-t/40));
$('lvWire').setAttribute('stroke-dashoffset', String(-t/50));
// IFR总结
setOp(ifrSummary, ease(clamp01(p / 0.3)));
// 重置标记(为循环准备)
if(p > 0.95){
structureInit = false;
chambersInit = false;
wiringInit = false;
liftingInit = false;
mountingInit = false;
summaryInit = false;
// 清理力箭头和底板
forceArrows.innerHTML = '';
const bp = document.querySelector('.base-plate');
if(bp) bp.remove();
xsectGroup.setAttribute('transform', 'translate(330,470)');
isoGroup.setAttribute('transform', 'translate(0,0)');
}
}
/* ===== 构建力流箭头 ===== */
function buildForceArrows(){
const ns = 'http://www.w3.org/2000/svg';
// 向下的力 (重力)
const arrow1 = document.createElementNS(ns,'polygon');
arrow1.setAttribute('points','326,280 334,280 330,300');
arrow1.setAttribute('fill','var(--gold)');
arrow1.setAttribute('opacity','0.8');
forceArrows.appendChild(arrow1);
// 向上的反力 (吊装)
const arrow2 = document.createElementNS(ns,'polygon');
arrow2.setAttribute('points','326,260 334,260 330,240');
arrow2.setAttribute('fill','var(--accent)');
arrow2.setAttribute('opacity','0.8');
forceArrows.appendChild(arrow2);
// 力传递路径 (在截面内向下)
const path = document.createElementNS(ns,'line');
path.setAttribute('x1','330'); path.setAttribute('y1','300');
path.setAttribute('x2','330'); path.setAttribute('y2','360');
path.setAttribute('stroke','var(--gold)');
path.setAttribute('stroke-width','2');
path.setAttribute('stroke-dasharray','4,3');
path.setAttribute('opacity','0.6');
forceArrows.appendChild(path);
// 力流标注
const label = document.createElementNS(ns,'text');
label.setAttribute('x','348'); label.setAttribute('y','330');
label.setAttribute('font-family','Share Tech Mono,monospace');
label.setAttribute('font-size','10');
label.setAttribute('fill','var(--gold)');
label.textContent='力流传导';
forceArrows.appendChild(label);
}
/* ===== 构建底板 ===== */
function buildBasePlate(){
const ns = 'http://www.w3.org/2000/svg';
const g = document.createElementNS(ns,'g');
g.classList.add('base-plate');
// 底板 (等轴测)
const p0=iso(-20,FD+10,-5), p1=iso(FW+20,FD+10,-5),
p2=iso(FW+20,-10,-5), p3=iso(-20,-10,-5);
const plate = document.createElementNS(ns,'polygon');
plate.setAttribute('points',pts([p0,p1,p2,p3]));
plate.setAttribute('fill','rgba(30,50,70,.6)');
plate.setAttribute('stroke','rgba(60,90,120,.5)');
plate.setAttribute('stroke-width','1');
g.appendChild(plate);
// 底板标签
const cx=(p0[0]+p2[0])/2, cy=(p0[1]+p2[1])/2;
const label = document.createElementNS(ns,'text');
label.setAttribute('x',cx); label.setAttribute('y',cy+4);
label.setAttribute('text-anchor','middle');
label.setAttribute('font-family','Share Tech Mono,monospace');
label.setAttribute('font-size','11');
label.setAttribute('fill','rgba(100,140,180,.6)');
label.textContent='设备底盘';
g.appendChild(label);
// 连接螺栓 (从T型槽到底板)
const boltPositions = [[PT/2, PT/2], [PT/2, FD-PT/2],
[FW-PT/2, PT/2], [FW-PT/2, FD-PT/2]];
boltPositions.forEach(bp => {
const [bx, by] = iso(bp[0], bp[1], -5);
const [tx, ty] = iso(bp[0], bp[1], 0);
const bolt = document.createElementNS(ns,'line');
bolt.setAttribute('x1',tx); bolt.setAttribute('y1',ty);
bolt.setAttribute('x2',bx); bolt.setAttribute('y2',by);
bolt.setAttribute('stroke','var(--gold)');
bolt.setAttribute('stroke-width','2');
bolt.setAttribute('opacity','0.6');
g.appendChild(bolt);
});
isoGroup.appendChild(g);
}
/* ===== 主动画循环 ===== */
function animate(now){
if(!startTime) startTime = now;
if(paused){ rafId = requestAnimationFrame(animate); return; }
const elapsed = (now - startTime) * speed;
updatePhase(elapsed);
rafId = requestAnimationFrame(animate);
}
/* ===== 控件事件 ===== */
$('speedSlider').addEventListener('input', function(){
speed = parseFloat(this.value);
$('speedVal').textContent = speed.toFixed(1) + 'x';
});
$('pauseBtn').addEventListener('click', function(){
paused = !paused;
this.textContent = paused ? '继续' : '暂停';
if(!paused){
// 调整startTime以保持连续性
startTime = performance.now() - (pauseTime / speed) * speed;
} else {
pauseTime = performance.now() - startTime;
}
});
$('resetBtn').addEventListener('click', function(){
startTime = performance.now();
paused = false;
$('pauseBtn').textContent = '暂停';
// 重置所有初始化标记
structureInit = chambersInit = wiringInit = liftingInit = mountingInit = summaryInit = false;
forceArrows.innerHTML = '';
const bp = document.querySelector('.base-plate');
if(bp) bp.remove();
xsectGroup.setAttribute('transform','translate(330,470)');
isoGroup.setAttribute('transform','translate(0,0)');
});
/* ===== 初始化 ===== */
function init(){
buildIsoFrame();
buildPhaseBar();
rafId = requestAnimationFrame(animate);
}
// 页面加载后自动启动
if(document.readyState === 'loading'){
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
// 确保重载时也能自动播放
window.addEventListener('load', function(){
if(!startTime){
startTime = performance.now();
if(!rafId) rafId = requestAnimationFrame(animate);
}
});
})();
</script>
</body>
</html>
实现说明
动画核心设计思路:基于 TRIZ "最终理想解 (IFR)" 思想,整个动画聚焦于展示型材"一材三用"的理想状态——结构骨架、走线通道、吊装支点三合一,而非前后对比。
六阶段自动循环动画(26秒一周期):
- 截面解剖:型材截面主体绘制,隔墙、T型槽、吊装孔依次显现,等轴测框架同步淡入
- 三腔室集成:低压信号腔(蓝)、主承重腔(银)、高压线束腔(红)依次高亮填充,高低压隔离壁闪烁强调
- 线束走线:红色高压线与蓝色低压线以流动虚线动画沿各自腔室穿行,隔离壁脉冲闪烁突出防干扰核心
- 集成吊装:吊钩下降穿入吊装孔,框架微微抬起并伴有呼吸式振动,力流箭头指示传力路径
- T型槽固定:滑块螺母从侧方滑入T型槽并锁紧,等轴测视图出现设备底盘与连接螺栓
- 最终理想解:所有功能同时呼吸式高亮,IFR 总结文字浮现——"一材三用 · 消除冗余"
交互控制:右上角提供速度滑块(0.3x–2x)、暂停/继续和重置按钮;底部阶段指示条实时标记当前进度。
视觉特色:深色工业蓝图风格,铝合金金属渐变截面,红蓝双色线束流动,青色/金色高亮引导视觉焦点,等轴测投影提供三维装配语境。
积分规则:第一轮对话扣减8分,后续每轮扣6分
等待动画代码生成...
