<!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;400;600;700&family=Share+Tech+Mono&display=swap" rel="stylesheet">
<style>
:root{
--bg:#060a12;--fg:#cdd6e8;--muted:#4a5570;
--accent:#00e5ff;--accent2:#00ff88;--warn:#ff8c00;
--danger:#ff3b4a;--card:#0c1220;--border:#1a2540;
--cable:#00e5ff;--arm:#ff8c00;--struct:#1c2640;--struct2:#283854;
}
*{margin:0;padding:0;box-sizing:border-box}
body{
background:var(--bg);color:var(--fg);font-family:'Rajdhani',sans-serif;
min-height:100vh;display:flex;flex-direction:column;align-items:center;
padding:20px 16px 40px;
background-image:
radial-gradient(ellipse 80% 50% at 50% 0%,rgba(0,229,255,.04),transparent),
radial-gradient(ellipse 60% 40% at 50% 100%,rgba(255,140,0,.03),transparent);
}
header{text-align:center;margin-bottom:18px;position:relative}
header h1{
font-size:clamp(22px,3.2vw,34px);font-weight:700;letter-spacing:2px;
background:linear-gradient(90deg,var(--accent),var(--accent2));
-webkit-background-clip:text;-webkit-text-fill-color:transparent;
background-clip:text;
}
header p{
font-family:'Share Tech Mono',monospace;font-size:clamp(11px,1.4vw,14px);
color:var(--muted);letter-spacing:3px;margin-top:4px;
}
.viz-wrap{
width:100%;max-width:1060px;position:relative;
border:1px solid var(--border);border-radius:12px;overflow:hidden;
background:var(--card);
box-shadow:0 0 60px rgba(0,229,255,.06),0 0 120px rgba(0,0,0,.4);
}
svg#main{display:block;width:100%;height:auto}
.controls{
width:100%;max-width:1060px;margin-top:16px;
display:flex;flex-wrap:wrap;gap:12px 24px;align-items:center;
padding:16px 20px;background:var(--card);border:1px solid var(--border);border-radius:10px;
}
.ctrl-group{display:flex;align-items:center;gap:8px;flex:1 1 260px;min-width:200px}
.ctrl-group label{
font-family:'Share Tech Mono',monospace;font-size:12px;color:var(--muted);
white-space:nowrap;min-width:90px;
}
.ctrl-group input[type=range]{
flex:1;-webkit-appearance:none;appearance:none;height:4px;
background:var(--border);border-radius:2px;outline:none;cursor:pointer;
}
.ctrl-group input[type=range]::-webkit-slider-thumb{
-webkit-appearance:none;width:14px;height:14px;border-radius:50%;
background:var(--accent);border:2px solid var(--bg);cursor:pointer;
box-shadow:0 0 8px rgba(0,229,255,.5);
}
.ctrl-group .val{
font-family:'Share Tech Mono',monospace;font-size:13px;
color:var(--accent);min-width:60px;text-align:right;
}
.btn{
font-family:'Rajdhani',sans-serif;font-size:14px;font-weight:600;
padding:8px 20px;border:1px solid var(--accent);border-radius:6px;
background:transparent;color:var(--accent);cursor:pointer;
letter-spacing:1px;transition:all .25s;white-space:nowrap;
}
.btn:hover{background:rgba(0,229,255,.12);box-shadow:0 0 16px rgba(0,229,255,.2)}
.btn.active{background:rgba(0,229,255,.15);color:#fff}
.btn.warn{border-color:var(--warn);color:var(--warn)}
.btn.warn:hover{background:rgba(255,140,0,.12);box-shadow:0 0 16px rgba(255,140,0,.2)}
.info-bar{
width:100%;max-width:1060px;margin-top:12px;
display:flex;flex-wrap:wrap;gap:10px 20px;
padding:14px 20px;background:var(--card);border:1px solid var(--border);border-radius:10px;
}
.info-item{
display:flex;align-items:baseline;gap:6px;
}
.info-item .lbl{font-family:'Share Tech Mono',monospace;font-size:11px;color:var(--muted)}
.info-item .num{font-family:'Share Tech Mono',monospace;font-size:15px;font-weight:600}
.info-item .num.cyan{color:var(--accent)}
.info-item .num.green{color:var(--accent2)}
.info-item .num.orange{color:var(--warn)}
.info-item .num.red{color:var(--danger)}
.info-item .unit{font-family:'Share Tech Mono',monospace;font-size:10px;color:var(--muted)}
/* SVG 内部动画 */
.flow-up{stroke-dasharray:10 7;animation:flowU .9s linear infinite}
.flow-out-L{stroke-dasharray:8 6;animation:flowOL 1.1s linear infinite}
.flow-out-R{stroke-dasharray:8 6;animation:flowOR 1.1s linear infinite}
.flow-down{stroke-dasharray:4 8;animation:flowD 1.6s linear infinite}
@keyframes flowU{to{stroke-dashoffset:-17}}
@keyframes flowOL{to{stroke-dashoffset:-14}}
@keyframes flowOR{to{stroke-dashoffset:-14}}
@keyframes flowD{to{stroke-dashoffset:12}}
.pulse{animation:pulse 2s ease-in-out infinite}
@keyframes pulse{0%,100%{opacity:.7}50%{opacity:1}}
.glow-breath{animation:glowB 3s ease-in-out infinite}
@keyframes glowB{0%,100%{filter:drop-shadow(0 0 4px rgba(0,229,255,.3))}50%{filter:drop-shadow(0 0 10px rgba(0,229,255,.6))}}
/* 安装动画阶段 */
.phase-group{opacity:0;transition:opacity .8s ease}
.phase-group.visible{opacity:1}
.device-group{transform-box:fill-box;transform-origin:center bottom;transition:transform 1.2s cubic-bezier(.22,1,.36,1)}
.device-group.lifted{transform:translateY(-18px)}
/* 楼板应力指示条 */
.floor-stress-bar{transition:fill .5s,stroke .5s}
@media(max-width:640px){
.controls{flex-direction:column;gap:10px}
.ctrl-group{min-width:0}
}
@media(prefers-reduced-motion:reduce){
.flow-up,.flow-out-L,.flow-out-R,.flow-down,.pulse,.glow-breath{animation:none}
.phase-group{transition:none}
.device-group{transition:none}
}
</style>
</head>
<body>
<header>
<h1>天花悬挂 + 侧墙锚固 混合受力构型</h1>
<p>LOAD PATH RECONFIGURATION · IDEAL FINAL RESULT</p>
</header>
<div class="viz-wrap">
<svg id="main" viewBox="0 0 1000 680" xmlns="http://www.w3.org/2000/svg">
<defs>
<!-- 辉光滤镜 -->
<filter id="glowCyan" x="-40%" y="-40%" width="180%" height="180%">
<feGaussianBlur in="SourceGraphic" stdDeviation="3" result="b"/>
<feMerge><feMergeNode in="b"/><feMergeNode in="SourceGraphic"/></feMerge>
</filter>
<filter id="glowOrange" x="-40%" y="-40%" width="180%" height="180%">
<feGaussianBlur in="SourceGraphic" stdDeviation="2.5" result="b"/>
<feMerge><feMergeNode in="b"/><feMergeNode in="SourceGraphic"/></feMerge>
</filter>
<filter id="glowGreen" x="-40%" y="-40%" width="180%" height="180%">
<feGaussianBlur in="SourceGraphic" stdDeviation="4" result="b"/>
<feMerge><feMergeNode in="b"/><feMergeNode in="SourceGraphic"/></feMerge>
</filter>
<filter id="softGlow" x="-20%" y="-20%" width="140%" height="140%">
<feGaussianBlur in="SourceGraphic" stdDeviation="6"/>
</filter>
<!-- 斜线填充 - 剪力墙 -->
<pattern id="wallHatch" patternUnits="userSpaceOnUse" width="8" height="8" patternTransform="rotate(45)">
<line x1="0" y1="0" x2="0" y2="8" stroke="#283854" stroke-width="2"/>
</pattern>
<!-- 斜线填充 - 天花板主梁 -->
<pattern id="beamHatch" patternUnits="userSpaceOnUse" width="10" height="10" patternTransform="rotate(-45)">
<line x1="0" y1="0" x2="0" y2="10" stroke="#2a3c5c" stroke-width="2.5"/>
</pattern>
<!-- 网格背景 -->
<pattern id="grid" patternUnits="userSpaceOnUse" width="40" height="40">
<path d="M 40 0 L 0 0 0 40" fill="none" stroke="#0e1628" stroke-width="0.5"/>
</pattern>
<!-- 箭头标记 -->
<marker id="arrowCyan" markerWidth="8" markerHeight="6" refX="8" refY="3" orient="auto">
<path d="M0,0 L8,3 L0,6" fill="var(--accent)"/>
</marker>
<marker id="arrowOrange" markerWidth="7" markerHeight="5" refX="7" refY="2.5" orient="auto">
<path d="M0,0 L7,2.5 L0,5" fill="var(--warn)"/>
</marker>
</defs>
<!-- 背景 -->
<rect width="1000" height="680" fill="#080d18"/>
<rect width="1000" height="680" fill="url(#grid)" opacity=".6"/>
<!-- ====== 建筑结构 ====== -->
<g id="structure">
<!-- 天花板主梁 -->
<rect x="60" y="42" width="880" height="36" rx="2" fill="#14203a" stroke="#2a3c5c" stroke-width="1.5"/>
<rect x="60" y="42" width="880" height="36" rx="2" fill="url(#beamHatch)" opacity=".5"/>
<text x="500" y="66" text-anchor="middle" font-family="'Share Tech Mono',monospace" font-size="11" fill="#3d5a80" letter-spacing="3">CEILING MAIN BEAM / 承载主梁</text>
<!-- 楼板 -->
<rect id="floorSlab" x="60" y="578" width="880" height="28" rx="2" fill="#1a1224" stroke="#3a2040" stroke-width="1.2" stroke-dasharray="6 3"/>
<text x="500" y="596" text-anchor="middle" font-family="'Share Tech Mono',monospace" font-size="10" fill="#5a3050" letter-spacing="2">FLOOR SLAB · 脆弱楼板(承载极限低)</text>
<!-- 左剪力墙 -->
<rect x="60" y="42" width="48" height="564" fill="#121c30" stroke="#1e3050" stroke-width="1"/>
<rect x="60" y="42" width="48" height="564" fill="url(#wallHatch)" opacity=".4"/>
<text x="84" y="340" text-anchor="middle" font-family="'Share Tech Mono',monospace" font-size="9" fill="#2a4060" writing-mode="tb" letter-spacing="2">SHEAR WALL</text>
<!-- 右剪力墙 -->
<rect x="892" y="42" width="48" height="564" fill="#121c30" stroke="#1e3050" stroke-width="1"/>
<rect x="892" y="42" width="48" height="564" fill="url(#wallHatch)" opacity=".4"/>
<text x="916" y="340" text-anchor="middle" font-family="'Share Tech Mono',monospace" font-size="9" fill="#2a4060" writing-mode="tb" letter-spacing="2">SHEAR WALL</text>
</g>
<!-- ====== 安装阶段1:锚固点 ====== -->
<g id="phase-anchors" class="phase-group visible">
<circle cx="340" cy="78" r="7" fill="none" stroke="var(--accent)" stroke-width="2" filter="url(#glowCyan)"/>
<circle cx="340" cy="78" r="3" fill="var(--accent)"/>
<circle cx="660" cy="78" r="7" fill="none" stroke="var(--accent)" stroke-width="2" filter="url(#glowCyan)"/>
<circle cx="660" cy="78" r="3" fill="var(--accent)"/>
<text x="340" y="96" text-anchor="middle" font-family="'Share Tech Mono',monospace" font-size="9" fill="var(--accent)" opacity=".7">锚点A</text>
<text x="660" y="96" text-anchor="middle" font-family="'Share Tech Mono',monospace" font-size="9" fill="var(--accent)" opacity=".7">锚点B</text>
</g>
<!-- ====== 安装阶段2:碳纤维拉索 ====== -->
<g id="phase-cables" class="phase-group visible">
<!-- 左拉索 -->
<line x1="340" y1="78" x2="448" y2="272" stroke="var(--cable)" stroke-width="2.8" filter="url(#glowCyan)" class="glow-breath"/>
<!-- 右拉索 -->
<line x1="660" y1="78" x2="552" y2="272" stroke="var(--cable)" stroke-width="2.8" filter="url(#glowCyan)" class="glow-breath"/>
<!-- 拉索连接件 -->
<rect x="440" y="268" width="16" height="10" rx="2" fill="#1a2a44" stroke="var(--accent)" stroke-width="1"/>
<rect x="544" y="268" width="16" height="10" rx="2" fill="#1a2a44" stroke="var(--accent)" stroke-width="1"/>
<!-- 标注 -->
<text x="370" y="170" font-family="'Share Tech Mono',monospace" font-size="10" fill="var(--accent)" opacity=".8" transform="rotate(-47,370,170)">碳纤维拉索 CF-CABLE</text>
</g>
<!-- ====== 安装阶段3:装置本体 ====== -->
<g id="phase-device" class="phase-group visible device-group lifted">
<!-- 装置主体 -->
<rect x="410" y="278" width="180" height="185" rx="5" fill="#141e36" stroke="#2a3e64" stroke-width="1.8"/>
<!-- 内部组件 -->
<rect x="425" y="295" width="150" height="40" rx="3" fill="#0e1628" stroke="#1e2e4a" stroke-width="0.8"/>
<text x="500" y="320" text-anchor="middle" font-family="'Share Tech Mono',monospace" font-size="10" fill="#3d5a80">POWER UNIT</text>
<!-- 振动电机 -->
<circle cx="500" cy="400" r="22" fill="#0c1424" stroke="#2a4060" stroke-width="1"/>
<circle cx="500" cy="400" r="8" fill="none" stroke="var(--warn)" stroke-width="1" opacity=".6" class="pulse"/>
<text x="500" y="404" text-anchor="middle" font-family="'Share Tech Mono',monospace" font-size="8" fill="var(--warn)" opacity=".6">M</text>
<!-- 装置标签 -->
<text x="500" y="450" text-anchor="middle" font-family="'Rajdhani',sans-serif" font-size="13" font-weight="600" fill="#5a7aa0" letter-spacing="1">重型设备</text>
<!-- 重心标记 -->
<g id="cogMarker">
<line x1="488" y1="370" x2="512" y2="370" stroke="var(--accent2)" stroke-width="1" opacity=".7"/>
<line x1="500" y1="358" x2="500" y2="382" stroke="var(--accent2)" stroke-width="1" opacity=".7"/>
<circle cx="500" cy="370" r="4" fill="none" stroke="var(--accent2)" stroke-width="1.2" opacity=".9"/>
<text x="520" y="367" font-family="'Share Tech Mono',monospace" font-size="9" fill="var(--accent2)" opacity=".8">CoG</text>
</g>
<!-- ±10cm 公差带 -->
<rect x="406" y="360" width="188" height="20" rx="0" fill="rgba(0,255,136,.04)" stroke="var(--accent2)" stroke-width="0.6" stroke-dasharray="4 3" opacity=".6"/>
<text x="405" y="374" font-family="'Share Tech Mono',monospace" font-size="8" fill="var(--accent2)" opacity=".5" text-anchor="end">±10cm</text>
</g>
<!-- ====== 安装阶段4:提升间隙 ====== -->
<g id="phase-gap" class="phase-group visible">
<!-- 间隙指示 -->
<line x1="430" y1="466" x2="430" y2="578" stroke="var(--accent2)" stroke-width="0.6" stroke-dasharray="3 3" opacity=".5"/>
<line x1="570" y1="466" x2="570" y2="578" stroke="var(--accent2)" stroke-width="0.6" stroke-dasharray="3 3" opacity=".5"/>
<text x="500" y="530" text-anchor="middle" font-family="'Share Tech Mono',monospace" font-size="9" fill="var(--accent2)" opacity=".6">载荷卸除区</text>
</g>
<!-- ====== 安装阶段5:侧墙阻尼支撑臂 ====== -->
<g id="phase-arms" class="phase-group visible">
<!-- 左臂 -->
<g id="armLeft">
<!-- 墙面锚座 -->
<rect x="105" y="358" width="18" height="24" rx="2" fill="#1a283e" stroke="var(--warn)" stroke-width="1.2"/>
<!-- 伸缩臂杆 -->
<line x1="123" y1="370" x2="340" y2="370" stroke="#3d4e6a" stroke-width="3.5"/>
<line x1="123" y1="370" x2="300" y2="370" stroke="#4d5e7a" stroke-width="2"/>
<!-- 阻尼器符号 -->
<rect x="310" y="362" width="30" height="16" rx="2" fill="#1a283e" stroke="var(--warn)" stroke-width="1"/>
<line x1="318" y1="362" x2="318" y2="378" stroke="var(--warn)" stroke-width="0.8" opacity=".6"/>
<line x1="326" y1="362" x2="326" y2="378" stroke="var(--warn)" stroke-width="0.8" opacity=".6"/>
<!-- 夹持端 -->
<rect x="340" y="362" width="14" height="16" rx="1" fill="#1a283e" stroke="var(--warn)" stroke-width="1.2"/>
<circle cx="347" cy="370" r="3" fill="var(--warn)" opacity=".6"/>
<!-- 标注 -->
<text x="220" y="356" text-anchor="middle" font-family="'Share Tech Mono',monospace" font-size="9" fill="var(--warn)" opacity=".8">阻尼支撑臂 L</text>
</g>
<!-- 右臂 -->
<g id="armRight">
<rect x="877" y="358" width="18" height="24" rx="2" fill="#1a283e" stroke="var(--warn)" stroke-width="1.2"/>
<line x1="877" y1="370" x2="660" y2="370" stroke="#3d4e6a" stroke-width="3.5"/>
<line x1="877" y1="370" x2="700" y2="370" stroke="#4d5e7a" stroke-width="2"/>
<rect x="660" y="362" width="30" height="16" rx="2" fill="#1a283e" stroke="var(--warn)" stroke-width="1"/>
<line x1="668" y1="362" x2="668" y2="378" stroke="var(--warn)" stroke-width="0.8" opacity=".6"/>
<line x1="676" y1="362" x2="676" y2="378" stroke="var(--warn)" stroke-width="0.8" opacity=".6"/>
<rect x="646" y="362" width="14" height="16" rx="1" fill="#1a283e" stroke="var(--warn)" stroke-width="1.2"/>
<circle cx="653" cy="370" r="3" fill="var(--warn)" opacity=".6"/>
<text x="780" y="356" text-anchor="middle" font-family="'Share Tech Mono',monospace" font-size="9" fill="var(--warn)" opacity=".8">阻尼支撑臂 R</text>
</g>
</g>
<!-- ====== 力流路径 ====== -->
<g id="forcePaths" class="phase-group visible">
<!-- 拉索力流 - 向上 -->
<line x1="448" y1="272" x2="340" y2="78" stroke="var(--accent)" stroke-width="2" class="flow-up" filter="url(#glowCyan)" opacity=".85"/>
<line x1="552" y1="272" x2="660" y2="78" stroke="var(--accent)" stroke-width="2" class="flow-up" filter="url(#glowCyan)" opacity=".85"/>
<!-- 臂力流 - 向外 -->
<line x1="354" y1="370" x2="123" y2="370" stroke="var(--warn)" stroke-width="1.5" class="flow-out-L" filter="url(#glowOrange)" opacity=".7"/>
<line x1="646" y1="370" x2="877" y2="370" stroke="var(--warn)" stroke-width="1.5" class="flow-out-R" filter="url(#glowOrange)" opacity=".7"/>
<!-- 残余楼板载荷 - 向下(极小) -->
<line x1="500" y1="466" x2="500" y2="578" stroke="var(--danger)" stroke-width="1.2" class="flow-down" opacity=".35"/>
</g>
<!-- ====== 力矢量标注 ====== -->
<g id="forceLabels" class="phase-group visible">
<!-- 拉索力 -->
<g transform="translate(375,165)">
<rect x="-42" y="-12" width="84" height="18" rx="3" fill="rgba(0,229,255,.1)" stroke="var(--accent)" stroke-width=".6"/>
<text text-anchor="middle" y="2" font-family="'Share Tech Mono',monospace" font-size="10" fill="var(--accent)" id="labelCableForce">T=4.9kN</text>
</g>
<!-- 左臂力 -->
<g transform="translate(220,394)">
<rect x="-36" y="-12" width="72" height="18" rx="3" fill="rgba(255,140,0,.08)" stroke="var(--warn)" stroke-width=".6"/>
<text text-anchor="middle" y="2" font-family="'Share Tech Mono',monospace" font-size="10" fill="var(--warn)" id="labelArmForceL">F=1.2kN</text>
</g>
<!-- 右臂力 -->
<g transform="translate(780,394)">
<rect x="-36" y="-12" width="72" height="18" rx="3" fill="rgba(255,140,0,.08)" stroke="var(--warn)" stroke-width=".6"/>
<text text-anchor="middle" y="2" font-family="'Share Tech Mono',monospace" font-size="10" fill="var(--warn)" id="labelArmForceR">F=1.2kN</text>
</g>
<!-- 残余楼板力 -->
<g transform="translate(535,530)">
<rect x="-30" y="-12" width="60" height="18" rx="3" fill="rgba(255,59,74,.08)" stroke="var(--danger)" stroke-width=".6"/>
<text text-anchor="middle" y="2" font-family="'Share Tech Mono',monospace" font-size="10" fill="var(--danger)" opacity=".7" id="labelFloorForce">0.2kN</text>
</g>
</g>
<!-- ====== 楼板应力指示条 ====== -->
<g id="floorStressIndicator" transform="translate(960,578)">
<rect x="0" y="0" width="12" height="28" rx="2" fill="#0a0e18" stroke="#2a3050" stroke-width=".8"/>
<rect id="stressBar" x="1" y="1" width="10" height="26" rx="1.5" fill="var(--accent2)" class="floor-stress-bar"/>
<text x="6" y="-4" text-anchor="middle" font-family="'Share Tech Mono',monospace" font-size="7" fill="var(--muted)">LOAD</text>
</g>
<!-- ====== HUD 数据面板 ====== -->
<g id="hud" transform="translate(68,620)">
<rect x="0" y="0" width="870" height="44" rx="4" fill="rgba(8,13,24,.85)" stroke="var(--border)" stroke-width=".8"/>
<text x="16" y="17" font-family="'Share Tech Mono',monospace" font-size="10" fill="var(--muted)">SYS STATUS</text>
<text x="16" y="34" font-family="'Share Tech Mono',monospace" font-size="12" fill="var(--accent2)" id="hudStatus">■ IFR ACHIEVED — 楼板载荷已卸除</text>
<text x="280" y="17" font-family="'Share Tech Mono',monospace" font-size="10" fill="var(--muted)">CABLE PRE-TENSION</text>
<text x="280" y="34" font-family="'Share Tech Mono',monospace" font-size="13" fill="var(--accent)" id="hudTension">5.0 kN</text>
<text x="440" y="17" font-family="'Share Tech Mono',monospace" font-size="10" fill="var(--muted)">FLOOR LOAD</text>
<text x="440" y="34" font-family="'Share Tech Mono',monospace" font-size="13" id="hudFloor" fill="var(--accent2)">0.2 kN ▼99%</text>
<text x="600" y="17" font-family="'Share Tech Mono',monospace" font-size="10" fill="var(--muted)">SAFETY MARGIN</text>
<text x="600" y="34" font-family="'Share Tech Mono',monospace" font-size="13" id="hudMargin" fill="var(--accent2)">94.8%</text>
<text x="760" y="17" font-family="'Share Tech Mono',monospace" font-size="10" fill="var(--muted)">LATERAL MOM</text>
<text x="760" y="34" font-family="'Share Tech Mono',monospace" font-size="13" fill="var(--warn)" id="hudMoment">2.4 kN·m</text>
</g>
<!-- ====== 能量脉冲粒子层 ====== -->
<g id="particles"></g>
<!-- ====== 右上角 IFR 标记 ====== -->
<g transform="translate(920,16)">
<rect x="0" y="0" width="68" height="22" rx="4" fill="rgba(0,255,136,.08)" stroke="var(--accent2)" stroke-width=".8"/>
<text x="34" y="15" text-anchor="middle" font-family="'Share Tech Mono',monospace" font-size="10" fill="var(--accent2)" font-weight="600">IFR ✓</text>
</g>
<!-- 载荷路径说明 -->
<g transform="translate(60,18)">
<text font-family="'Share Tech Mono',monospace" font-size="10" fill="#2a4060" letter-spacing="1">CROSS-SECTION VIEW · 楼层横截面</text>
</g>
</svg>
</div>
<!-- 控制面板 -->
<div class="controls">
<div class="ctrl-group">
<label>拉索预紧力</label>
<input type="range" id="sliderTension" min="1" max="10" step="0.1" value="5">
<span class="val" id="valTension">5.0 kN</span>
</div>
<div class="ctrl-group">
<label>装置质量</label>
<input type="range" id="sliderMass" min="200" max="2500" step="10" value="1000">
<span class="val" id="valMass">1000 kg</span>
</div>
<button class="btn" id="btnInstall" aria-label="播放安装流程动画">播放安装流程</button>
<button class="btn warn" id="btnForce" aria-label="切换力路径显示">力路径: 开</button>
</div>
<!-- 信息栏 -->
<div class="info-bar">
<div class="info-item">
<span class="lbl">重力W=</span>
<span class="num cyan" id="infoW">9.8</span><span class="unit">kN</span>
</div>
<div class="info-item">
<span class="lbl">单索拉力T=</span>
<span class="num cyan" id="infoT">4.9</span><span class="unit">kN</span>
</div>
<div class="info-item">
<span class="lbl">楼板载荷=</span>
<span class="num green" id="infoFloor">0.2</span><span class="unit">kN</span>
</div>
<div class="info-item">
<span class="lbl">卸载率=</span>
<span class="num green" id="infoUnload">98.0</span><span class="unit">%</span>
</div>
<div class="info-item">
<span class="lbl">安全裕度=</span>
<span class="num" id="infoMargin" style="color:var(--accent2)">94.8</span><span class="unit">%</span>
</div>
</div>
<script>
/* ============================
交互与动画控制
============================ */
const $ = s => document.querySelector(s);
const svg = $('#main');
const particleLayer = $('#particles');
// 滑块与显示
const sliderTension = $('#sliderTension');
const sliderMass = $('#sliderMass');
const valTension = $('#valTension');
const valMass = $('#valMass');
// 状态
let preTension = 5.0; // kN, 单根拉索预紧力
let deviceMass = 1000; // kg
let showForce = true;
let installing = false;
// 粒子系统
const particles = [];
const PARTICLE_COUNT_PER_PATH = 6;
// 定义力流路径(按力传递方向绘制)
const flowPaths = {
cableL: { points: [{x:448,y:272},{x:340,y:78}], color:'#00e5ff', speed:0.6 },
cableR: { points: [{x:552,y:272},{x:660,y:78}], color:'#00e5ff', speed:0.6 },
armL: { points: [{x:354,y:370},{x:123,y:370}], color:'#ff8c00', speed:0.4 },
armR: { points: [{x:646,y:370},{x:877,y:370}], color:'#ff8c00', speed:0.4 },
floor: { points: [{x:500,y:466},{x:500,y:578}], color:'#ff3b4a', speed:0.25 },
};
// 初始化粒子
function initParticles() {
particleLayer.innerHTML = '';
particles.length = 0;
for (const [key, path] of Object.entries(flowPaths)) {
for (let i = 0; i < PARTICLE_COUNT_PER_PATH; i++) {
const circle = document.createElementNS('http://www.w3.org/2000/svg','circle');
const r = key === 'floor' ? 2 : 3;
circle.setAttribute('r', r);
circle.setAttribute('fill', path.color);
circle.setAttribute('opacity', '0');
particleLayer.appendChild(circle);
particles.push({
el: circle,
pathKey: key,
progress: i / PARTICLE_COUNT_PER_PATH,
speed: path.speed * (0.8 + Math.random() * 0.4),
baseOpacity: key === 'floor' ? 0.3 : 0.7,
});
}
}
}
// 线性插值
function lerp(a, b, t) { return a + (b - a) * t; }
// 获取路径上的位置
function getPathPos(key, t) {
const p = flowPaths[key].points;
return {
x: lerp(p[0].x, p[1].x, t),
y: lerp(p[0].y, p[1].y, t),
};
}
// 动画循环
let lastTime = 0;
function animate(time) {
const dt = Math.min((time - lastTime) / 1000, 0.05);
lastTime = time;
for (const p of particles) {
if (!showForce) {
p.el.setAttribute('opacity', '0');
continue;
}
p.progress += dt * p.speed;
if (p.progress > 1) p.progress -= 1;
const pos = getPathPos(p.pathKey, p.progress);
p.el.setAttribute('cx', pos.x);
p.el.setAttribute('cy', pos.y);
// 两端淡入淡出
const fade = Math.sin(p.progress * Math.PI);
p.el.setAttribute('opacity', (fade * p.baseOpacity).toFixed(2));
}
requestAnimationFrame(animate);
}
// 物理计算
function calcForces() {
const g = 9.8;
const W = deviceMass * g / 1000; // kN
const T = Math.max(preTension, W / 2); // 单根拉索力取预紧力与重力分量中较大者
const cableTotal = 2 * T;
const floorLoad = Math.max(0, W - cableTotal);
const unloadRate = W > 0 ? ((W - floorLoad) / W * 100) : 100;
const margin = W > 0 ? ((cableTotal - W) / W * 100) : 100;
const armF = W * 0.12; // 侧向约12%重力用于抗倾覆
const moment = armF * 2.0; // 简化力矩
return { W, T, floorLoad, unloadRate, margin, armF, moment };
}
// 更新显示
function updateDisplay() {
const f = calcForces();
// 滑块值
valTension.textContent = preTension.toFixed(1) + ' kN';
valMass.textContent = deviceMass + ' kg';
// SVG 标注
$('#labelCableForce').textContent = 'T=' + f.T.toFixed(1) + 'kN';
$('#labelArmForceL').textContent = 'F=' + f.armF.toFixed(1) + 'kN';
$('#labelArmForceR').textContent = 'F=' + f.armF.toFixed(1) + 'kN';
$('#labelFloorForce').textContent = f.floorLoad.toFixed(1) + 'kN';
// HUD
$('#hudTension').textContent = f.T.toFixed(1) + ' kN';
$('#hudFloor').textContent = f.floorLoad.toFixed(1) + ' kN ▼' + f.unloadRate.toFixed(0) + '%';
$('#hudMargin').textContent = f.margin.toFixed(1) + '%';
$('#hudMoment').textContent = f.moment.toFixed(1) + ' kN·m';
// 信息栏
$('#infoW').textContent = f.W.toFixed(1);
$('#infoT').textContent = f.T.toFixed(1);
$('#infoFloor').textContent = f.floorLoad.toFixed(1);
$('#infoUnload').textContent = f.unloadRate.toFixed(1);
$('#infoMargin').textContent = f.margin.toFixed(1);
// 颜色状态
const safe = f.margin > 20;
const warn = f.margin > 0 && f.margin <= 20;
$('#hudFloor').setAttribute('fill', safe ? '#00ff88' : warn ? '#ff8c00' : '#ff3b4a');
$('#hudMargin').setAttribute('fill', safe ? '#00ff88' : warn ? '#ff8c00' : '#ff3b4a');
$('#infoFloor').style.color = safe ? 'var(--accent2)' : warn ? 'var(--warn)' : 'var(--danger)';
$('#infoMargin').style.color = safe ? 'var(--accent2)' : warn ? 'var(--warn)' : 'var(--danger)';
// HUD状态
if (f.margin > 20) {
$('#hudStatus').textContent = '■ IFR ACHIEVED — 楼板载荷已卸除';
$('#hudStatus').setAttribute('fill', '#00ff88');
} else if (f.margin > 0) {
$('#hudStatus').textContent = '■ MARGINAL — 安全裕度偏低';
$('#hudStatus').setAttribute('fill', '#ff8c00');
} else {
$('#hudStatus').textContent = '■ WARNING — 楼板超载风险';
$('#hudStatus').setAttribute('fill', '#ff3b4a');
}
// 楼板应力条颜色
const stressBar = $('#stressBar');
if (safe) {
stressBar.setAttribute('fill', '#00ff88');
stressBar.setAttribute('height', '4');
stressBar.setAttribute('y', '23');
} else if (warn) {
stressBar.setAttribute('fill', '#ff8c00');
stressBar.setAttribute('height', '14');
stressBar.setAttribute('y', '13');
} else {
stressBar.setAttribute('fill', '#ff3b4a');
stressBar.setAttribute('height', '26');
stressBar.setAttribute('y', '1');
}
// 拉索视觉粗细随预紧力变化
const cableWidth = 1.5 + (preTension / 10) * 2;
document.querySelectorAll('#phase-cables line').forEach(l => {
l.setAttribute('stroke-width', cableWidth.toFixed(1));
});
// IFR标记
const ifrMark = svg.querySelector('[transform="translate(920,16)"] text');
if (ifrMark) {
ifrMark.textContent = f.margin > 0 ? 'IFR ✓' : 'IFR ✗';
ifrMark.setAttribute('fill', f.margin > 0 ? '#00ff88' : '#ff3b4a');
}
}
// 滑块事件
sliderTension.addEventListener('input', e => {
preTension = parseFloat(e.target.value);
updateDisplay();
});
sliderMass.addEventListener('input', e => {
deviceMass = parseInt(e.target.value);
updateDisplay();
});
// 力路径开关
const btnForce = $('#btnForce');
btnForce.addEventListener('click', () => {
showForce = !showForce;
btnForce.textContent = '力路径: ' + (showForce ? '开' : '关');
btnForce.classList.toggle('active', showForce);
const fpGroup = $('#forcePaths');
const flGroup = $('#forceLabels');
if (showForce) {
fpGroup.classList.add('visible');
flGroup.classList.add('visible');
} else {
fpGroup.classList.remove('visible');
flGroup.classList.remove('visible');
}
});
// 安装流程动画
const btnInstall = $('#btnInstall');
const phaseGroups = [
{ id: 'phase-anchors', delay: 400 },
{ id: 'phase-cables', delay: 1200 },
{ id: 'phase-device', delay: 2000 },
{ id: 'phase-gap', delay: 3200 },
{ id: 'phase-arms', delay: 4200 },
{ id: 'forcePaths', delay: 5400 },
{ id: 'forceLabels', delay: 5800 },
];
btnInstall.addEventListener('click', () => {
if (installing) return;
installing = true;
btnInstall.classList.add('active');
btnInstall.textContent = '安装中...';
// 重置:先隐藏所有阶段
phaseGroups.forEach(pg => {
const el = $('#' + pg.id);
if (el) el.classList.remove('visible');
});
// 设备回到地面
const dev = $('#phase-device');
dev.classList.remove('lifted');
// 依次显示
phaseGroups.forEach(pg => {
setTimeout(() => {
const el = $('#' + pg.id);
if (el) el.classList.add('visible');
// 设备提升在gap阶段触发
if (pg.id === 'phase-gap') {
setTimeout(() => {
dev.classList.add('lifted');
}, 300);
}
}, pg.delay);
});
// 完成
setTimeout(() => {
installing = false;
btnInstall.classList.remove('active');
btnInstall.textContent = '播放安装流程';
updateDisplay();
}, 6800);
});
// 初始化
initParticles();
updateDisplay();
requestAnimationFrame(animate);
// 初始状态:力路径按钮为激活
btnForce.classList.add('active');
</script>
</body>
</html>
实现说明
设计美学
采用 深色工程 HUD 风格,以深蓝黑为底色,搭配 青色(拉索/张力)、橙色(阻尼臂/侧向力)、绿色(安全/IFR达成)、红色(危险/楼板应力) 四色系统,形成强烈的工程语义色彩编码。字体选用 Rajdhani(棱角分明的技术标题字体)与 Share Tech Mono(等宽数据字体),避免常见无趣字体。
IFR 核心表达
动画直接呈现最终理想解状态——装置悬浮于楼板之上,重力通过碳纤维拉索向上传递至主梁,侧向约束通过阻尼臂传递至剪力墙。力流粒子沿传递路径持续流动,楼板仅承受极小残余载荷(红色微弱粒子),直观体现"矛盾已被消除"的 IFR 本质。
交互设计
- 拉索预紧力滑块:调整预紧力,实时观察力分配变化与安全裕度响应
- 装置质量滑块:改变质量,体验系统在不同载荷下的自适应能力
- 力路径开关:显隐力流可视化,聚焦结构或力学视角
- 安装流程回放:分阶段展示"锚固→挂索→吊装→提升→支撑臂锁紧→力路激活"的完整时序
关键视觉细节
- 重心(CoG)十字标记与 ±10cm 公差带虚线框,精确表达支撑臂安装约束
- 楼板右侧应力指示条实时反映载荷水平(绿色低位/橙色中位/红色满位)
- 底部 HUD 面板实时显示所有力学参数与系统状态判定
- 拉索辉光呼吸动画暗示持续受力状态
积分规则:第一轮对话扣减6分,后续每轮扣4分
等待动画代码生成...
