分享图
A
动画渲染工坊
就绪
<!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 &middot; 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分