分享图
动画工坊
引擎就绪
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>算力毛细血管网 · 液态路由原理动画</title>
<link href="https://fonts.googleapis.com/css2?family=Syne:wght@400;600;700;800&family=Azeret+Mono:wght@300;400;500&display=swap" rel="stylesheet">
<style>
*{margin:0;padding:0;box-sizing:border-box}
:root{
  --bg:#060b18;--surface:#0c1425;--border:rgba(255,255,255,0.06);
  --private:#00e5c7;--private-dim:rgba(0,229,199,0.15);
  --public:#ff8c42;--public-dim:rgba(255,140,66,0.12);
  --router:#00ffa3;--router-glow:rgba(0,255,163,0.35);
  --infer:#c084fc;--text:#e8edf5;--muted:#5a6d8a;--accent:#00ffa3;
}
body{
  background:var(--bg);color:var(--text);
  font-family:'Azeret Mono',monospace;
  height:100vh;overflow:hidden;display:flex;align-items:center;justify-content:center;
}
#wrap{width:100vw;height:100vh;position:relative;display:flex;align-items:center;justify-content:center}
svg#main{width:100%;height:100%;display:block}

/* ─── 标题面板 ─── */
#title-panel{
  position:fixed;top:20px;left:24px;z-index:10;pointer-events:none;
}
#title-panel h1{
  font-family:'Syne',sans-serif;font-size:26px;font-weight:800;
  background:linear-gradient(135deg,var(--private),var(--router));
  -webkit-background-clip:text;-webkit-text-fill-color:transparent;
  letter-spacing:-0.5px;
}
#title-panel .sub{
  font-size:11px;color:var(--muted);margin-top:3px;letter-spacing:1.5px;text-transform:uppercase;
}

/* ─── 指标面板 ─── */
#metrics{
  position:fixed;top:18px;right:24px;z-index:10;
  display:flex;flex-direction:column;gap:8px;pointer-events:none;
}
.met{
  background:rgba(12,20,37,0.88);backdrop-filter:blur(14px);
  border:1px solid var(--border);border-radius:10px;padding:9px 16px;
  display:flex;align-items:center;gap:10px;min-width:210px;
}
.met-dot{width:7px;height:7px;border-radius:50%;flex-shrink:0}
.met-body{display:flex;flex-direction:column;gap:1px}
.met-label{font-size:9px;color:var(--muted);text-transform:uppercase;letter-spacing:.8px}
.met-val{font-size:15px;font-weight:600;font-family:'Syne',sans-serif}

/* ─── 控制面板 ─── */
#controls{
  position:fixed;bottom:22px;left:50%;transform:translateX(-50%);z-index:10;
  background:rgba(12,20,37,0.92);backdrop-filter:blur(18px);
  border:1px solid rgba(0,255,163,0.15);border-radius:14px;
  padding:14px 30px;display:flex;gap:36px;align-items:center;
}
.ctrl{display:flex;flex-direction:column;gap:3px}
.ctrl label{font-size:9px;color:var(--muted);text-transform:uppercase;letter-spacing:1px}
.ctrl input[type=range]{
  -webkit-appearance:none;width:170px;height:3px;
  background:rgba(255,255,255,0.08);border-radius:2px;outline:none;cursor:pointer;
}
.ctrl input[type=range]::-webkit-slider-thumb{
  -webkit-appearance:none;width:14px;height:14px;
  background:var(--accent);border-radius:50%;cursor:pointer;
  box-shadow:0 0 10px var(--router-glow);
}
.ctrl .val{font-size:12px;color:var(--accent);font-weight:500}
.ctrl .hint{font-size:9px;color:var(--muted);margin-top:1px}

/* ─── 图例 ─── */
#legend{
  position:fixed;bottom:80px;left:24px;z-index:10;
  display:flex;flex-direction:column;gap:6px;pointer-events:none;
}
.leg{display:flex;align-items:center;gap:8px;font-size:10px;color:var(--muted)}
.leg-line{width:22px;height:3px;border-radius:2px}

/* ─── 流量指示器 ─── */
#traffic-indicator{
  position:fixed;bottom:80px;right:24px;z-index:10;pointer-events:none;
  text-align:right;
}
#traffic-indicator .lbl{font-size:9px;color:var(--muted);text-transform:uppercase;letter-spacing:1px}
#traffic-indicator .state{font-size:22px;font-weight:700;font-family:'Syne',sans-serif;margin-top:2px}

@media(prefers-reduced-motion:reduce){
  svg *{animation:none!important;transition:none!important}
}
</style>
</head>
<body>
<div id="wrap">
<svg id="main" viewBox="0 0 1400 900" preserveAspectRatio="xMidYMid meet">
<defs>
  <!-- 背景径向渐变 -->
  <radialGradient id="bg-grad" cx="50%" cy="50%" r="65%">
    <stop offset="0%" stop-color="#0f1a2e"/><stop offset="100%" stop-color="#060b18"/>
  </radialGradient>
  <!-- 路由器渐变 -->
  <radialGradient id="rtr-grad" cx="50%" cy="50%" r="50%">
    <stop offset="0%" stop-color="#00ffa3"/><stop offset="80%" stop-color="#00cc80"/><stop offset="100%" stop-color="#009960"/>
  </radialGradient>
  <!-- 发光滤镜 -->
  <filter id="gl" x="-50%" y="-50%" width="200%" height="200%">
    <feGaussianBlur stdDeviation="6" result="b"/><feMerge><feMergeNode in="b"/><feMergeNode in="SourceGraphic"/></feMerge>
  </filter>
  <filter id="gl2" x="-80%" y="-80%" width="260%" height="260%">
    <feGaussianBlur stdDeviation="14" result="b"/><feMerge><feMergeNode in="b"/><feMergeNode in="SourceGraphic"/></feMerge>
  </filter>
  <filter id="gl3" x="-100%" y="-100%" width="300%" height="300%">
    <feGaussianBlur stdDeviation="25" result="b"/><feMerge><feMergeNode in="b"/><feMergeNode in="SourceGraphic"/></feMerge>
  </filter>
  <!-- 网格图案 -->
  <pattern id="grid" width="60" height="60" patternUnits="userSpaceOnUse">
    <path d="M60,0 L0,0 0,60" fill="none" stroke="rgba(255,255,255,0.018)" stroke-width="0.5"/>
  </pattern>
</defs>

<!-- 背景 -->
<rect width="1400" height="900" fill="url(#bg-grad)"/>
<rect width="1400" height="900" fill="url(#grid)"/>

<!-- 大气光晕 -->
<circle cx="700" cy="450" r="260" fill="rgba(0,255,163,0.015)" filter="url(#gl3)"/>
<circle cx="230" cy="450" r="140" fill="rgba(0,229,199,0.012)" filter="url(#gl3)"/>
<circle cx="1170" cy="450" r="140" fill="rgba(255,140,66,0.008)" filter="url(#gl3)"/>

<!-- ═══════ 管壁层(宽描边,低透明度) ═══════ -->
<g id="vessels" opacity="1">
  <!-- 顶部:业务→感应器 -->
  <path d="M700,108 C700,140 700,170 700,195" fill="none" stroke="rgba(232,237,245,0.06)" stroke-width="14" stroke-linecap="round"/>
  <!-- 感应器→路由器 -->
  <path d="M700,240 C700,290 700,350 700,400" fill="none" stroke="rgba(232,237,245,0.06)" stroke-width="14" stroke-linecap="round"/>
  <!-- 左侧毛细管壁 -->
  <path d="M642,425 C530,400 380,348 265,335" fill="none" stroke="rgba(0,229,199,0.07)" stroke-width="16" stroke-linecap="round"/>
  <path d="M632,450 C480,450 340,450 200,450" fill="none" stroke="rgba(0,229,199,0.07)" stroke-width="16" stroke-linecap="round"/>
  <path d="M642,475 C530,500 380,552 265,565" fill="none" stroke="rgba(0,229,199,0.07)" stroke-width="16" stroke-linecap="round"/>
  <!-- 右侧毛细管壁 -->
  <path id="vw-r1" d="M758,425 C870,400 1020,348 1135,335" fill="none" stroke="rgba(255,140,66,0.03)" stroke-width="16" stroke-linecap="round"/>
  <path id="vw-r2" d="M768,450 C920,450 1060,450 1200,450" fill="none" stroke="rgba(255,140,66,0.03)" stroke-width="16" stroke-linecap="round"/>
  <path id="vw-r3" d="M758,475 C870,500 1020,552 1135,565" fill="none" stroke="rgba(255,140,66,0.03)" stroke-width="16" stroke-linecap="round"/>
</g>

<!-- ═══════ 流动路径层(细描边 + dash 动画) ═══════ -->
<g id="flow-paths">
  <!-- 顶部下行 -->
  <path id="fp-top" d="M700,108 C700,140 700,170 700,195" fill="none" stroke="#e8edf5" stroke-width="2.5" stroke-linecap="round" opacity="0.5"
    stroke-dasharray="6 10">
    <animate attributeName="stroke-dashoffset" from="0" to="-16" dur="1s" repeatCount="indefinite"/>
  </path>
  <path id="fp-sr" d="M700,240 C700,290 700,350 700,400" fill="none" stroke="#e8edf5" stroke-width="2.5" stroke-linecap="round" opacity="0.5"
    stroke-dasharray="6 10">
    <animate attributeName="stroke-dashoffset" from="0" to="-16" dur="0.8s" repeatCount="indefinite"/>
  </path>
  <!-- 左侧流动 -->
  <path id="fp-l1" d="M642,425 C530,400 380,348 265,335" fill="none" stroke="#00e5c7" stroke-width="2" stroke-linecap="round" opacity="0.65"
    stroke-dasharray="5 9">
    <animate attributeName="stroke-dashoffset" from="0" to="-14" dur="1.2s" repeatCount="indefinite"/>
  </path>
  <path id="fp-l2" d="M632,450 C480,450 340,450 200,450" fill="none" stroke="#00e5c7" stroke-width="2" stroke-linecap="round" opacity="0.65"
    stroke-dasharray="5 9">
    <animate attributeName="stroke-dashoffset" from="0" to="-14" dur="1s" repeatCount="indefinite"/>
  </path>
  <path id="fp-l3" d="M642,475 C530,500 380,552 265,565" fill="none" stroke="#00e5c7" stroke-width="2" stroke-linecap="round" opacity="0.65"
    stroke-dasharray="5 9">
    <animate attributeName="stroke-dashoffset" from="0" to="-14" dur="1.2s" repeatCount="indefinite"/>
  </path>
  <!-- 右侧流动 -->
  <path id="fp-r1" d="M758,425 C870,400 1020,348 1135,335" fill="none" stroke="#ff8c42" stroke-width="2" stroke-linecap="round" opacity="0.2"
    stroke-dasharray="5 9">
    <animate attributeName="stroke-dashoffset" from="0" to="-14" dur="1.2s" repeatCount="indefinite"/>
  </path>
  <path id="fp-r2" d="M768,450 C920,450 1060,450 1200,450" fill="none" stroke="#ff8c42" stroke-width="2" stroke-linecap="round" opacity="0.2"
    stroke-dasharray="5 9">
    <animate attributeName="stroke-dashoffset" from="0" to="-14" dur="1s" repeatCount="indefinite"/>
  </path>
  <path id="fp-r3" d="M758,475 C870,500 1020,552 1135,565" fill="none" stroke="#ff8c42" stroke-width="2" stroke-linecap="round" opacity="0.2"
    stroke-dasharray="5 9">
    <animate attributeName="stroke-dashoffset" from="0" to="-14" dur="1.2s" repeatCount="indefinite"/>
  </path>
  <!-- 底部汇聚路径 -->
  <path id="fp-bl" d="M200,480 C200,570 440,680 580,740" fill="none" stroke="#00e5c7" stroke-width="1.5" stroke-linecap="round" opacity="0.35"
    stroke-dasharray="4 8">
    <animate attributeName="stroke-dashoffset" from="0" to="-12" dur="1.4s" repeatCount="indefinite"/>
  </path>
  <path id="fp-bc" d="M700,510 C700,580 700,660 700,720" fill="none" stroke="rgba(232,237,245,0.25)" stroke-width="1.5" stroke-linecap="round"
    stroke-dasharray="4 8">
    <animate attributeName="stroke-dashoffset" from="0" to="-12" dur="1.2s" repeatCount="indefinite"/>
  </path>
  <path id="fp-br" d="M1200,480 C1200,570 960,680 820,740" fill="none" stroke="#ff8c42" stroke-width="1.5" stroke-linecap="round" opacity="0.15"
    stroke-dasharray="4 8">
    <animate attributeName="stroke-dashoffset" from="0" to="-12" dur="1.4s" repeatCount="indefinite"/>
  </path>
</g>

<!-- ═══════ 节点层 ═══════ -->

<!-- 业务请求源 -->
<g id="node-req">
  <circle cx="700" cy="82" r="24" fill="#0c1425" stroke="#e8edf5" stroke-width="1.5" filter="url(#gl)"/>
  <text x="700" y="78" text-anchor="middle" fill="#e8edf5" font-size="9" font-family="Syne,sans-serif" font-weight="700">REQ</text>
  <text x="700" y="92" text-anchor="middle" fill="#5a6d8a" font-size="7">业务请求</text>
</g>

<!-- 潮汐感应器 -->
<g id="node-sensor">
  <circle cx="700" cy="218" r="28" fill="#0c1425" stroke="#e8edf5" stroke-width="1.5" filter="url(#gl)"/>
  <text x="700" y="215" text-anchor="middle" fill="#e8edf5" font-size="9" font-family="Syne,sans-serif" font-weight="600">潮汐</text>
  <text x="700" y="228" text-anchor="middle" fill="#5a6d8a" font-size="7">感应器</text>
  <!-- 队列条 -->
  <rect x="744" y="204" width="5" height="28" rx="2.5" fill="#0c1425" stroke="rgba(255,255,255,0.08)" stroke-width="0.5"/>
  <rect id="queue-fill" x="744" y="232" width="5" height="0" rx="2.5" fill="#00ffa3" opacity="0.85">
    <animate attributeName="opacity" values="0.85;1;0.85" dur="1s" repeatCount="indefinite"/>
  </rect>
</g>

<!-- ═══ 液态路由器(中心) ═══ -->
<g id="node-router">
  <!-- 脉冲环 -->
  <circle cx="700" cy="450" r="60" fill="none" stroke="#00ffa3" stroke-width="0.8">
    <animate attributeName="r" values="60;150" dur="3s" repeatCount="indefinite"/>
    <animate attributeName="opacity" values="0.5;0" dur="3s" repeatCount="indefinite"/>
  </circle>
  <circle cx="700" cy="450" r="60" fill="none" stroke="#00ffa3" stroke-width="0.8">
    <animate attributeName="r" values="60;150" dur="3s" begin="1s" repeatCount="indefinite"/>
    <animate attributeName="opacity" values="0.5;0" dur="3s" begin="1s" repeatCount="indefinite"/>
  </circle>
  <circle cx="700" cy="450" r="60" fill="none" stroke="#00ffa3" stroke-width="0.8">
    <animate attributeName="r" values="60;150" dur="3s" begin="2s" repeatCount="indefinite"/>
    <animate attributeName="opacity" values="0.5;0" dur="3s" begin="2s" repeatCount="indefinite"/>
  </circle>
  <!-- 外环 -->
  <circle cx="700" cy="450" r="62" fill="#080e1e" stroke="#00ffa3" stroke-width="2" opacity="0.85" filter="url(#gl)"/>
  <!-- 内核 -->
  <circle id="router-core" cx="700" cy="450" r="36" fill="url(#rtr-grad)" opacity="0.92" filter="url(#gl2)">
    <animate attributeName="r" values="36;39;36" dur="2s" repeatCount="indefinite"/>
  </circle>
  <text x="700" y="447" text-anchor="middle" fill="#060b18" font-size="10" font-family="Syne,sans-serif" font-weight="800">液态</text>
  <text x="700" y="460" text-anchor="middle" fill="#060b18" font-size="10" font-family="Syne,sans-serif" font-weight="800">路由</text>
</g>

<!-- ═══ 私云节点 ═══ -->
<g id="node-priv">
  <g>
    <circle cx="265" cy="335" r="32" fill="#080e1e" stroke="#00e5c7" stroke-width="1.5" filter="url(#gl)"/>
    <circle cx="265" cy="335" r="16" fill="#00e5c7" opacity="0.15"/>
    <text x="265" y="332" text-anchor="middle" fill="#00e5c7" font-size="9" font-family="Syne,sans-serif" font-weight="700">私云</text>
    <text x="265" y="345" text-anchor="middle" fill="#00e5c7" font-size="11" font-weight="700">α</text>
  </g>
  <g>
    <circle cx="200" cy="450" r="32" fill="#080e1e" stroke="#00e5c7" stroke-width="1.5" filter="url(#gl)"/>
    <circle cx="200" cy="450" r="16" fill="#00e5c7" opacity="0.15"/>
    <text x="200" y="447" text-anchor="middle" fill="#00e5c7" font-size="9" font-family="Syne,sans-serif" font-weight="700">私云</text>
    <text x="200" y="460" text-anchor="middle" fill="#00e5c7" font-size="11" font-weight="700">β</text>
  </g>
  <g>
    <circle cx="265" cy="565" r="32" fill="#080e1e" stroke="#00e5c7" stroke-width="1.5" filter="url(#gl)"/>
    <circle cx="265" cy="565" r="16" fill="#00e5c7" opacity="0.15"/>
    <text x="265" y="562" text-anchor="middle" fill="#00e5c7" font-size="9" font-family="Syne,sans-serif" font-weight="700">私云</text>
    <text x="265" y="575" text-anchor="middle" fill="#00e5c7" font-size="11" font-weight="700">γ</text>
  </g>
  <!-- 区域标签 -->
  <text x="230" y="640" text-anchor="middle" fill="#00e5c7" font-size="14" font-family="Syne,sans-serif" font-weight="700" opacity="0.7">私云算力池</text>
  <text x="230" y="658" text-anchor="middle" fill="#5a6d8a" font-size="9">安全域 · 优先调度</text>
</g>

<!-- ═══ 公云节点 ═══ -->
<g id="node-pub">
  <g id="pub-a" opacity="0.25">
    <circle cx="1135" cy="335" r="32" fill="#080e1e" stroke="#ff8c42" stroke-width="1.5" filter="url(#gl)"/>
    <circle cx="1135" cy="335" r="16" fill="#ff8c42" opacity="0.1"/>
    <text x="1135" y="332" text-anchor="middle" fill="#ff8c42" font-size="9" font-family="Syne,sans-serif" font-weight="700">公云</text>
    <text x="1135" y="345" text-anchor="middle" fill="#ff8c42" font-size="11" font-weight="700">α</text>
  </g>
  <g id="pub-b" opacity="0.25">
    <circle cx="1200" cy="450" r="32" fill="#080e1e" stroke="#ff8c42" stroke-width="1.5" filter="url(#gl)"/>
    <circle cx="1200" cy="450" r="16" fill="#ff8c42" opacity="0.1"/>
    <text x="1200" y="447" text-anchor="middle" fill="#ff8c42" font-size="9" font-family="Syne,sans-serif" font-weight="700">公云</text>
    <text x="1200" y="460" text-anchor="middle" fill="#ff8c42" font-size="11" font-weight="700">β</text>
  </g>
  <g id="pub-c" opacity="0.25">
    <circle cx="1135" cy="565" r="32" fill="#080e1e" stroke="#ff8c42" stroke-width="1.5" filter="url(#gl)"/>
    <circle cx="1135" cy="565" r="16" fill="#ff8c42" opacity="0.1"/>
    <text x="1135" y="562" text-anchor="middle" fill="#ff8c42" font-size="9" font-family="Syne,sans-serif" font-weight="700">公云</text>
    <text x="1135" y="575" text-anchor="middle" fill="#ff8c42" font-size="11" font-weight="700">γ</text>
  </g>
  <text x="1170" y="640" text-anchor="middle" fill="#ff8c42" font-size="14" font-family="Syne,sans-serif" font-weight="700" opacity="0.7">公云算力池</text>
  <text x="1170" y="658" text-anchor="middle" fill="#5a6d8a" font-size="9">弹性域 · 溢出扩容</text>
</g>

<!-- ═══ 微推理元 ═══ -->
<g id="node-infer">
  <g id="inf-0"><circle cx="490" cy="750" r="12" fill="#0c1425" stroke="#c084fc" stroke-width="1" opacity="0.4"/><circle cx="490" cy="750" r="5" fill="#c084fc" opacity="0.1"/></g>
  <g id="inf-1"><circle cx="570" cy="738" r="12" fill="#0c1425" stroke="#c084fc" stroke-width="1" opacity="0.4"/><circle cx="570" cy="738" r="5" fill="#c084fc" opacity="0.1"/></g>
  <g id="inf-2"><circle cx="650" cy="730" r="12" fill="#0c1425" stroke="#c084fc" stroke-width="1" opacity="0.4"/><circle cx="650" cy="730" r="5" fill="#c084fc" opacity="0.1"/></g>
  <g id="inf-3"><circle cx="750" cy="730" r="12" fill="#0c1425" stroke="#c084fc" stroke-width="1" opacity="0.4"/><circle cx="750" cy="730" r="5" fill="#c084fc" opacity="0.1"/></g>
  <g id="inf-4"><circle cx="830" cy="738" r="12" fill="#0c1425" stroke="#c084fc" stroke-width="1" opacity="0.4"/><circle cx="830" cy="738" r="5" fill="#c084fc" opacity="0.1"/></g>
  <g id="inf-5"><circle cx="910" cy="750" r="12" fill="#0c1425" stroke="#c084fc" stroke-width="1" opacity="0.4"/><circle cx="910" cy="750" r="5" fill="#c084fc" opacity="0.1"/></g>
  <text x="700" y="790" text-anchor="middle" fill="#c084fc" font-size="13" font-family="Syne,sans-serif" font-weight="700" opacity="0.7">微推理元处理层</text>
  <text x="700" y="806" text-anchor="middle" fill="#5a6d8a" font-size="9">无状态 · 即插即用</text>
</g>

<!-- ═══ 粒子容器 ═══ -->
<g id="particles"></g>

<!-- ═══ 状态标注 ═══ -->
<g id="state-label" opacity="0">
  <rect x="630" y="510" width="140" height="30" rx="6" fill="rgba(12,20,37,0.85)" stroke="rgba(255,140,66,0.3)" stroke-width="0.8"/>
  <text id="state-text" x="700" y="530" text-anchor="middle" fill="#ff8c42" font-size="10" font-family="Syne,sans-serif" font-weight="600">公云溢出扩容中</text>
</g>

</svg>
</div>

<!-- 标题 -->
<div id="title-panel">
  <h1>算力毛细血管网</h1>
  <div class="sub">Liquid Routing · Ideal Final Result</div>
</div>

<!-- 指标 -->
<div id="metrics">
  <div class="met">
    <div class="met-dot" style="background:#00ffa3"></div>
    <div class="met-body">
      <span class="met-label">调度微周期</span>
      <span class="met-val" id="m-cycle">320ms</span>
    </div>
  </div>
  <div class="met">
    <div class="met-dot" style="background:#ff8c42"></div>
    <div class="met-body">
      <span class="met-label">跨云迁移中断</span>
      <span class="met-val" id="m-migrate">—</span>
    </div>
  </div>
  <div class="met">
    <div class="met-dot" style="background:#c084fc"></div>
    <div class="met-body">
      <span class="met-label">算力利用率</span>
      <span class="met-val" id="m-util">62%</span>
    </div>
  </div>
</div>

<!-- 控制 -->
<div id="controls">
  <div class="ctrl">
    <label>流量强度</label>
    <input type="range" id="sl-traffic" min="0" max="100" value="50">
    <span class="val" id="v-traffic">50%</span>
    <span class="hint">拖动控制业务流量</span>
  </div>
  <div class="ctrl">
    <label>调度微周期</label>
    <input type="range" id="sl-speed" min="20" max="100" value="60">
    <span class="val" id="v-speed">300ms</span>
    <span class="hint">影响路由决策频率</span>
  </div>
</div>

<!-- 图例 -->
<div id="legend">
  <div class="leg"><div class="leg-line" style="background:#00e5c7"></div>私云算力流(常驻)</div>
  <div class="leg"><div class="leg-line" style="background:#ff8c42"></div>公云算力流(弹性)</div>
  <div class="leg"><div class="leg-line" style="background:#00ffa3"></div>液态路由决策</div>
  <div class="leg"><div class="leg-line" style="background:#c084fc"></div>微推理元处理</div>
</div>

<!-- 流量状态 -->
<div id="traffic-indicator">
  <div class="lbl">当前流量状态</div>
  <div class="state" id="traffic-state" style="color:#00e5c7">平潮</div>
</div>

<script>
(function(){
  'use strict';
  const SVG_NS = 'http://www.w3.org/2000/svg';
  const svg = document.getElementById('main');
  const pGroup = document.getElementById('particles');

  /* ── 引用关键元素 ── */
  const queueFill = document.getElementById('queue-fill');
  const stateLabel = document.getElementById('state-label');
  const stateText = document.getElementById('state-text');
  const mCycle = document.getElementById('m-cycle');
  const mMigrate = document.getElementById('m-migrate');
  const mUtil = document.getElementById('m-util');
  const trafficStateEl = document.getElementById('traffic-state');

  /* 公云节点组 */
  const pubGroups = ['pub-a','pub-b','pub-c'].map(id => document.getElementById(id));
  /* 微推理元内核 */
  const infCores = [0,1,2,3,4,5].map(i => {
    const g = document.getElementById('inf-' + i);
    return g ? g.querySelectorAll('circle')[1] : null;
  });
  /* 右侧管壁 */
  const vwR = ['vw-r1','vw-r2','vw-r3'].map(id => document.getElementById(id));
  /* 右侧流动路径 */
  const fpR = ['fp-r1','fp-r2','fp-r3'].map(id => document.getElementById(id));
  /* 底部右侧路径 */
  const fpBR = document.getElementById('fp-br');

  /* ── 路径定义(用于粒子跟随) ── */
  const pathDefs = {
    topIn:    'M700,108 C700,140 700,170 700,195',
    senRtr:   'M700,240 C700,290 700,350 700,400',
    l1: 'M642,425 C530,400 380,348 265,335',
    l2: 'M632,450 C480,450 340,450 200,450',
    l3: 'M642,475 C530,500 380,552 265,565',
    r1: 'M758,425 C870,400 1020,348 1135,335',
    r2: 'M768,450 C920,450 1060,450 1200,450',
    r3: 'M758,475 C870,500 1020,552 1135,565',
    bl: 'M200,480 C200,570 440,680 580,740',
    bc: 'M700,510 C700,580 700,660 700,720',
    br: 'M1200,480 C1200,570 960,680 820,740'
  };

  /* 创建隐藏的路径元素用于 getPointAtLength */
  const pathEls = {};
  for(const [k,d] of Object.entries(pathDefs)){
    const p = document.createElementNS(SVG_NS,'path');
    p.setAttribute('d', d);
    p.setAttribute('fill','none');
    p.setAttribute('stroke','none');
    svg.appendChild(p);
    pathEls[k] = p;
  }

  /* ── 粒子类 ── */
  const allParticles = [];

  class Particle {
    constructor(pathKey, color, size, speed, group){
      this.pathKey = pathKey;
      this.pathEl = pathEls[pathKey];
      this.totalLen = this.pathEl.getTotalLength();
      this.color = color;
      this.size = size;
      this.baseSpeed = speed;
      this.group = group; // 'top','private','public','bottom'
      this.progress = Math.random();
      this.active = true;

      this.el = document.createElementNS(SVG_NS,'circle');
      this.el.setAttribute('r', size);
      this.el.setAttribute('fill', color);
      this.el.setAttribute('opacity','0');
      if(group === 'public'){
        this.el.setAttribute('filter','url(#gl)');
      }
      pGroup.appendChild(this.el);
      allParticles.push(this);
    }

    update(dt, traffic, speedFactor){
      /* 公云粒子在低流量时淡出 */
      if(this.group === 'public'){
        this.active = traffic > 0.3;
        this.el.setAttribute('opacity', this.active ? String(Math.min(1, (traffic - 0.3) / 0.3)) : '0');
        if(!this.active) return;
      }
      if(this.group === 'bottom-r'){
        this.active = traffic > 0.35;
        this.el.setAttribute('opacity', this.active ? String(Math.min(0.8, (traffic - 0.35) / 0.3)) : '0');
        if(!this.active) return;
      }

      const spd = this.baseSpeed * speedFactor;
      this.progress += spd * dt;
      if(this.progress > 1) this.progress -= 1;

      const pt = this.pathEl.getPointAtLength(this.progress * this.totalLen);
      this.el.setAttribute('cx', pt.x);
      this.el.setAttribute('cy', pt.y);

      /* 淡入淡出 */
      let op = 1;
      if(this.progress < 0.08) op = this.progress / 0.08;
      else if(this.progress > 0.92) op = (1 - this.progress) / 0.08;
      op = Math.max(0, Math.min(1, op));

      if(this.group === 'public'){
        op *= Math.min(1, (traffic - 0.3) / 0.3);
      }

      /* 非公云/底右粒子始终可见 */
      if(this.group !== 'public' && this.group !== 'bottom-r'){
        this.el.setAttribute('opacity', op.toFixed(3));
      } else if(this.active){
        this.el.setAttribute('opacity', op.toFixed(3));
      }
    }
  }

  /* ── 创建粒子 ── */
  function spawnParticles(){
    /* 顶部下行粒子 */
    for(let i=0;i<4;i++) new Particle('topIn','#e8edf5',2.2,0.35,'top');
    for(let i=0;i<5;i++) new Particle('senRtr','#e8edf5',2.5,0.4,'top');

    /* 私云路径粒子 */
    for(let i=0;i<5;i++) new Particle('l1','#00e5c7',2.8,0.3,'private');
    for(let i=0;i<5;i++) new Particle('l2','#00e5c7',2.8,0.32,'private');
    for(let i=0;i<5;i++) new Particle('l3','#00e5c7',2.8,0.3,'private');

    /* 公云路径粒子 */
    for(let i=0;i<5;i++) new Particle('r1','#ff8c42',3,0.3,'public');
    for(let i=0;i<5;i++) new Particle('r2','#ff8c42',3,0.32,'public');
    for(let i=0;i<5;i++) new Particle('r3','#ff8c42',3,0.3,'public');

    /* 底部汇聚粒子 */
    for(let i=0;i<3;i++) new Particle('bl','#00e5c7',2,0.28,'bottom-l');
    for(let i=0;i<4;i++) new Particle('bc','rgba(232,237,245,0.7)',2,0.3,'bottom-c');
    for(let i=0;i<3;i++) new Particle('br','#ff8c42',2,0.28,'bottom-r');
  }

  spawnParticles();

  /* ── 流量控制 ── */
  let manualTraffic = -1; // -1 = 自动模式
  let manualSpeed = -1;

  const slTraffic = document.getElementById('sl-traffic');
  const slSpeed = document.getElementById('sl-speed');
  const vTraffic = document.getElementById('v-traffic');
  const vSpeed = document.getElementById('v-speed');

  slTraffic.addEventListener('input', function(){
    manualTraffic = this.value / 100;
    vTraffic.textContent = this.value + '%';
  });
  slSpeed.addEventListener('input', function(){
    manualSpeed = this.value / 100;
    vSpeed.textContent = Math.round(500 * (1 - this.value / 100 * 0.8)) + 'ms';
  });

  /* ── 主动画循环 ── */
  let lastTime = 0;

  function animate(time){
    if(!lastTime) lastTime = time;
    const dt = Math.min((time - lastTime) / 1000, 0.05);
    lastTime = time;

    /* 计算流量强度(自动模式用正弦波) */
    let traffic;
    if(manualTraffic >= 0){
      traffic = manualTraffic;
    } else {
      /* 自动模式:平滑正弦振荡 */
      traffic = (Math.sin(time / 5000) + 1) / 2;
      slTraffic.value = Math.round(traffic * 100);
      vTraffic.textContent = Math.round(traffic * 100) + '%';
    }

    /* 速度因子 */
    let speedFactor;
    if(manualSpeed >= 0){
      speedFactor = 0.5 + manualSpeed * 1.2;
    } else {
      speedFactor = 0.8 + traffic * 0.6;
    }

    /* ── 更新粒子 ── */
    for(const p of allParticles){
      p.update(dt, traffic, speedFactor);
    }

    /* ── 更新潮汐感应器队列 ── */
    const queueH = Math.round(traffic * 28);
    queueFill.setAttribute('y', String(232 - queueH));
    queueFill.setAttribute('height', String(queueH));
    /* 队列颜色随压力变化 */
    if(traffic > 0.7){
      queueFill.setAttribute('fill','#ff8c42');
    } else if(traffic > 0.4){
      queueFill.setAttribute('fill','#00ffa3');
    } else {
      queueFill.setAttribute('fill','#00e5c7');
    }

    /* ── 更新公云节点可见性 ── */
    const pubOpacity = traffic > 0.25 ? Math.min(1, 0.2 + (traffic - 0.25) * 1.2) : 0.2;
    for(const g of pubGroups){
      if(g) g.setAttribute('opacity', pubOpacity.toFixed(3));
    }

    /* ── 更新右侧管壁宽度 ── */
    const vwWidth = 8 + traffic * 14;
    const vwOpacity = 0.02 + traffic * 0.09;
    for(const w of vwR){
      if(w){
        w.setAttribute('stroke-width', vwWidth.toFixed(1));
        w.setAttribute('stroke', 'rgba(255,140,66,' + vwOpacity.toFixed(3) + ')');
      }
    }

    /* ── 更新右侧流动路径 ── */
    const fpOpacity = traffic > 0.25 ? 0.15 + (traffic - 0.25) * 1.0 : 0.15;
    const fpWidth = 1.5 + traffic * 3;
    for(const fp of fpR){
      if(fp){
        fp.setAttribute('opacity', Math.min(0.85, fpOpacity).toFixed(3));
        fp.setAttribute('stroke-width', fpWidth.toFixed(1));
      }
    }

    /* 底部右侧路径 */
    if(fpBR){
      const brOp = traffic > 0.3 ? 0.1 + (traffic - 0.3) * 0.8 : 0.1;
      fpBR.setAttribute('opacity', Math.min(0.6, brOp).toFixed(3));
    }

    /* ── 溢出扩容状态标注 ── */
    if(traffic > 0.55){
      stateLabel.setAttribute('opacity', String(Math.min(1, (traffic - 0.55) * 3)));
      if(traffic > 0.8){
        stateText.textContent = '全量扩容 · 公私云协同';
        stateText.setAttribute('fill','#ff6b2b');
      } else {
        stateText.textContent = '公云溢出扩容中';
        stateText.setAttribute('fill','#ff8c42');
      }
    } else {
      stateLabel.setAttribute('opacity', '0');
    }

    /* ── 微推理元激活 ── */
    const activeInfs = Math.floor(1 + traffic * 5.5);
    for(let i = 0; i < infCores.length; i++){
      if(!infCores[i]) continue;
      const coreOpacity = i < activeInfs ? 0.3 + traffic * 0.4 : 0.05;
      infCores[i].setAttribute('opacity', coreOpacity.toFixed(3));
      const parentG = infCores[i].parentElement;
      if(parentG){
        parentG.setAttribute('opacity', i < activeInfs ? '0.8' : '0.3');
      }
    }

    /* ── 更新指标面板 ── */
    const cycleMs = Math.round(500 * (1 - traffic * 0.6));
    mCycle.textContent = cycleMs + 'ms';

    if(traffic > 0.3){
      const migrateMs = Math.round(20 + (1 - traffic) * 40);
      mMigrate.textContent = migrateMs + 'ms';
    } else {
      mMigrate.textContent = '—';
    }

    const util = Math.round(45 + traffic * 48);
    mUtil.textContent = util + '%';

    /* ── 流量状态文字 ── */
    if(traffic < 0.25){
      trafficStateEl.textContent = '退潮';
      trafficStateEl.style.color = '#00e5c7';
    } else if(traffic < 0.55){
      trafficStateEl.textContent = '平潮';
      trafficStateEl.style.color = '#00ffa3';
    } else if(traffic < 0.8){
      trafficStateEl.textContent = '涨潮';
      trafficStateEl.style.color = '#ff8c42';
    } else {
      trafficStateEl.textContent = '洪峰';
      trafficStateEl.style.color = '#ff6b2b';
    }

    requestAnimationFrame(animate);
  }

  /* ── 自动启动 ── */
  requestAnimationFrame(animate);

  /* ── 背景漂浮粒子(装饰) ── */
  function createBgDust(){
    const dustGroup = document.createElementNS(SVG_NS,'g');
    dustGroup.setAttribute('opacity','0.25');
    svg.insertBefore(dustGroup, svg.children[2]); // 插入到背景之后

    for(let i = 0; i < 40; i++){
      const c = document.createElementNS(SVG_NS,'circle');
      c.setAttribute('cx', String(Math.random() * 1400));
      c.setAttribute('cy', String(Math.random() * 900));
      c.setAttribute('r', String(0.5 + Math.random() * 1.2));
      c.setAttribute('fill', Math.random() > 0.5 ? '#00e5c7' : '#ff8c42');
      c.setAttribute('opacity', String(0.15 + Math.random() * 0.25));

      /* 缓慢漂浮动画 */
      const animX = document.createElementNS(SVG_NS,'animate');
      animX.setAttribute('attributeName','cx');
      const cx = parseFloat(c.getAttribute('cx'));
      const dx = 15 + Math.random() * 30;
      animX.setAttribute('values', cx + ';' + (cx + dx) + ';' + cx);
      animX.setAttribute('dur', (8 + Math.random() * 12) + 's');
      animX.setAttribute('repeatCount','indefinite');

      const animY = document.createElementNS(SVG_NS,'animate');
      animY.setAttribute('attributeName','cy');
      const cy = parseFloat(c.getAttribute('cy'));
      const dy = 10 + Math.random() * 20;
      animY.setAttribute('values', cy + ';' + (cy + dy) + ';' + cy);
      animY.setAttribute('dur', (10 + Math.random() * 14) + 's');
      animY.setAttribute('repeatCount','indefinite');

      c.appendChild(animX);
      c.appendChild(animY);
      dustGroup.appendChild(c);
    }
  }
  createBgDust();

})();
</script>
</body>
</html>

这个动画实现了以下核心设计:

视觉叙事聚焦 IFR(最终理想解):画面直接展示"算力毛细血管网"的理想运行状态——液态路由器如心脏般持续脉动,算力资源像血液一样在公私云之间自如流动,无需任何前后对比。

核心创新点的视觉引导

  • 毛细血管舒缩效应:当流量升高时,通向公云的路径(琥珀色)在描边宽度、透明度、粒子密度三个维度同时"舒张",低流量时自动"收缩",直接映射方案的核心机理
  • 液态路由器脉冲环:三层扩散环持续从中心向外传播,象征实时调度决策
  • 潮汐感应器队列条:颜色从青绿→翠绿→琥珀渐变,直观反映压力等级

交互控制

  • 流量强度滑块:手动干预业务流量,观察公云弹性扩缩的完整过程(也可自动正弦振荡)
  • 调度微周期滑块:调节路由决策频率,影响粒子流速与指标读数

关键参数实时呈现:右侧指标面板动态显示调度微周期(≤500ms)、跨云迁移中断时间(≤50ms)和算力利用率,均随流量状态联动变化。

积分规则:第一轮对话扣减8分,后续每轮扣6分