独立渲染引擎就绪引擎就绪
<!DOCTYPE html>
<html lang="zh">
<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=Orbitron:wght@400;700;900&family=Share+Tech+Mono&display=swap" rel="stylesheet">
<style>
*{margin:0;padding:0;box-sizing:border-box}
body{
background:#050910;
display:flex;flex-direction:column;align-items:center;justify-content:center;
min-height:100vh;
font-family:'Share Tech Mono',monospace;
color:#b0c8e8;
overflow:hidden;
}
.scene-wrap{
width:96vw;max-width:1280px;position:relative;
display:flex;align-items:center;justify-content:center;
}
svg#scene{width:100%;height:auto;display:block}
.ctrl-bar{
margin-top:18px;display:flex;align-items:center;gap:24px;
background:rgba(8,16,32,.85);padding:14px 36px;border-radius:14px;
border:1px solid rgba(0,232,192,.18);
backdrop-filter:blur(8px);
}
.ctrl-bar label{font-size:13px;color:#5e8aaa;letter-spacing:.5px}
.ctrl-bar input[type=range]{
-webkit-appearance:none;width:180px;height:4px;border-radius:2px;
background:linear-gradient(90deg,#0d3040,#00e8c0);outline:none;cursor:pointer;
}
.ctrl-bar input[type=range]::-webkit-slider-thumb{
-webkit-appearance:none;width:16px;height:16px;border-radius:50%;
background:#00e8c0;box-shadow:0 0 10px #00e8c088;cursor:pointer;
}
.ctrl-val{font-family:'Orbitron',sans-serif;font-size:14px;color:#00e8c0;min-width:42px}
.phase-label{
font-family:'Orbitron',sans-serif;font-size:13px;color:#4d8eff;letter-spacing:1px;
min-width:180px;text-align:right;
}
/* SVG 内部动画 */
@keyframes pulse-ring{0%{r:28;opacity:.6}100%{r:52;opacity:0}}
@keyframes core-breathe{0%,100%{opacity:.55;r:14}50%{opacity:.9;r:18}}
@keyframes dash-flow{0%{stroke-dashoffset:24}100%{stroke-dashoffset:0}}
@keyframes orbit-spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}
@keyframes fade-pulse{0%,100%{opacity:.3}50%{opacity:.85}}
@keyframes ring-pulse{0%{r:60;opacity:.5}100%{r:110;opacity:0}}
.path-flow{
stroke-dasharray:6 18;
animation:dash-flow 1.2s linear infinite;
}
.node-core{animation:core-breathe 2.4s ease-in-out infinite}
.center-ring-anim{animation:ring-pulse 3s ease-out infinite}
</style>
</head>
<body>
<div class="scene-wrap">
<svg id="scene" viewBox="0 0 1200 800" xmlns="http://www.w3.org/2000/svg">
<defs>
<!-- 背景径向渐变 -->
<radialGradient id="bgGrad" cx="50%" cy="50%" r="70%">
<stop offset="0%" stop-color="#0c1528"/>
<stop offset="100%" stop-color="#050910"/>
</radialGradient>
<!-- 中心节点渐变 -->
<radialGradient id="centerGrad" cx="50%" cy="40%">
<stop offset="0%" stop-color="#3d7aff"/>
<stop offset="100%" stop-color="#1a3a80"/>
</radialGradient>
<radialGradient id="centerGlow" cx="50%" cy="50%">
<stop offset="0%" stop-color="#4d8eff" stop-opacity=".35"/>
<stop offset="100%" stop-color="#4d8eff" stop-opacity="0"/>
</radialGradient>
<!-- 探针健康渐变 -->
<radialGradient id="probeOkGrad" cx="50%" cy="40%">
<stop offset="0%" stop-color="#00e8c0"/>
<stop offset="100%" stop-color="#007a60"/>
</radialGradient>
<!-- 探针告警渐变 -->
<radialGradient id="probeAlertGrad" cx="50%" cy="40%">
<stop offset="0%" stop-color="#ff2d55"/>
<stop offset="100%" stop-color="#8a0020"/>
</radialGradient>
<!-- 进化渐变 -->
<radialGradient id="evoGrad" cx="50%" cy="50%">
<stop offset="0%" stop-color="#ffb800" stop-opacity=".4"/>
<stop offset="100%" stop-color="#ffb800" stop-opacity="0"/>
</radialGradient>
<!-- 反馈渐变 -->
<radialGradient id="fbGrad" cx="50%" cy="50%">
<stop offset="0%" stop-color="#b366ff" stop-opacity=".5"/>
<stop offset="100%" stop-color="#b366ff" stop-opacity="0"/>
</radialGradient>
<!-- 滤镜 -->
<filter id="glowSm"><feGaussianBlur stdDeviation="3" result="b"/>
<feMerge><feMergeNode in="b"/><feMergeNode in="SourceGraphic"/></feMerge>
</filter>
<filter id="glowMd"><feGaussianBlur stdDeviation="6" result="b"/>
<feMerge><feMergeNode in="b"/><feMergeNode in="SourceGraphic"/></feMerge>
</filter>
<filter id="glowLg"><feGaussianBlur stdDeviation="14" result="b"/>
<feMerge><feMergeNode in="b"/><feMergeNode in="SourceGraphic"/></feMerge>
</filter>
<filter id="glowRed"><feGaussianBlur stdDeviation="8" result="b"/>
<feFlood flood-color="#ff2d55" flood-opacity=".5" result="c"/>
<feComposite in="c" in2="b" operator="in" result="d"/>
<feMerge><feMergeNode in="d"/><feMergeNode in="SourceGraphic"/></feMerge>
</filter>
<filter id="glowGold"><feGaussianBlur stdDeviation="6" result="b"/>
<feFlood flood-color="#ffb800" flood-opacity=".45" result="c"/>
<feComposite in="c" in2="b" operator="in" result="d"/>
<feMerge><feMergeNode in="d"/><feMergeNode in="SourceGraphic"/></feMerge>
</filter>
<!-- 箭头标记 -->
<marker id="arrTeal" markerWidth="8" markerHeight="6" refX="8" refY="3" orient="auto">
<path d="M0,0 L8,3 L0,6" fill="#00e8c0" opacity=".7"/>
</marker>
<marker id="arrBlue" markerWidth="8" markerHeight="6" refX="8" refY="3" orient="auto">
<path d="M0,0 L8,3 L0,6" fill="#4d8eff" opacity=".7"/>
</marker>
<marker id="arrPurple" markerWidth="8" markerHeight="6" refX="8" refY="3" orient="auto">
<path d="M0,0 L8,3 L0,6" fill="#b366ff" opacity=".7"/>
</marker>
</defs>
<!-- ===== 背景层 ===== -->
<rect width="1200" height="800" fill="url(#bgGrad)"/>
<!-- 网格 -->
<g opacity=".06" stroke="#3a6090" stroke-width=".5">
<line x1="0" y1="200" x2="1200" y2="200"/><line x1="0" y1="400" x2="1200" y2="400"/>
<line x1="0" y1="600" x2="1200" y2="600"/>
<line x1="300" y1="0" x2="300" y2="800"/><line x1="600" y1="0" x2="600" y2="800"/>
<line x1="900" y1="0" x2="900" y2="800"/>
</g>
<!-- 环境粒子 -->
<g id="ambientParticles" opacity=".35"></g>
<!-- ===== 连接路径层 ===== -->
<g id="paths">
<!-- 网关→中心 -->
<path id="pathGW" d="M 250,250 Q 380,290 540,380" fill="none" stroke="#12283e" stroke-width="2.5" class="path-flow" marker-end="url(#arrTeal)"/>
<!-- 中心→网关(自愈脉冲) -->
<path id="pathGW_rev" d="M 540,380 Q 380,290 250,250" fill="none" stroke="#12283e" stroke-width="1.5" opacity=".4"/>
<!-- 推理→中心 -->
<path id="pathIF" d="M 950,250 Q 820,290 660,380" fill="none" stroke="#12283e" stroke-width="2.5" class="path-flow" marker-end="url(#arrTeal)"/>
<path id="pathIF_rev" d="M 660,380 Q 820,290 950,250" fill="none" stroke="#12283e" stroke-width="1.5" opacity=".4"/>
<!-- 算力→中心 -->
<path id="pathCMP" d="M 600,630 Q 590,560 600,460" fill="none" stroke="#12283e" stroke-width="2.5" class="path-flow" marker-end="url(#arrTeal)"/>
<path id="pathCMP_rev" d="M 600,460 Q 590,560 600,630" fill="none" stroke="#12283e" stroke-width="1.5" opacity=".4"/>
<!-- 业务→中心(反馈轴突) -->
<path id="pathFB" d="M 600,120 Q 615,220 600,370" fill="none" stroke="#2a1a40" stroke-width="2" class="path-flow" stroke-dasharray="4 12" marker-end="url(#arrPurple)"/>
<!-- 进化轨道 -->
<ellipse id="evoOrbit" cx="600" cy="400" rx="130" ry="50" fill="none" stroke="#ffb80022" stroke-width="1.5" stroke-dasharray="4 8"/>
</g>
<!-- ===== 动态粒子容器 ===== -->
<g id="dynamicParticles"></g>
<!-- ===== 节点层 ===== -->
<!-- 业务前端 -->
<g id="bizNode" transform="translate(600,85)">
<rect x="-90" y="-30" width="180" height="60" rx="12" fill="#0e0a20" stroke="#b366ff55" stroke-width="1.5"/>
<rect x="-80" y="-22" width="160" height="44" rx="8" fill="none" stroke="#b366ff33" stroke-width="1"/>
<!-- 屏幕内容 -->
<circle cx="-50" cy="-4" r="6" fill="#b366ff" opacity=".4"/>
<rect x="-38" y="-8" width="30" height="3" rx="1.5" fill="#b366ff" opacity=".3"/>
<rect x="-38" y="-1" width="20" height="3" rx="1.5" fill="#b366ff" opacity=".2"/>
<circle cx="20" cy="-4" r="6" fill="#b366ff" opacity=".4"/>
<rect x="32" y="-8" width="30" height="3" rx="1.5" fill="#b366ff" opacity=".3"/>
<!-- 用户反馈指示 -->
<g id="fbIndicator" opacity="0">
<circle cx="55" cy="-12" r="8" fill="#ff6b9d" opacity=".7"/>
<text x="55" y="-8" text-anchor="middle" fill="#fff" font-size="10" font-weight="bold">↓</text>
</g>
<text y="30" text-anchor="middle" fill="#b366ff" font-size="11" font-family="'Orbitron',sans-serif" letter-spacing="1">业务反馈轴突</text>
</g>
<!-- 网关探针 -->
<g id="probeGW" transform="translate(250,250)">
<circle r="46" fill="url(#probeOkGrad)" opacity=".08"/>
<polygon id="hexGW" points="0,-34 29.4,-17 29.4,17 0,34 -29.4,17 -29.4,-17" fill="none" stroke="#00e8c0" stroke-width="1.8" opacity=".7" filter="url(#glowSm)"/>
<circle r="22" fill="#00e8c0" opacity=".06"/>
<circle id="coreGW" class="node-core" r="10" fill="#00e8c0" opacity=".7" filter="url(#glowSm)"/>
<!-- 状态环 -->
<circle id="statusGW" r="30" fill="none" stroke="#00e8c0" stroke-width="2.5" opacity=".5" stroke-dasharray="8 6"/>
<!-- 告警环 -->
<circle id="alertRingGW" r="28" fill="none" stroke="#ff2d55" stroke-width="3" opacity="0"/>
<text y="52" text-anchor="middle" fill="#5ea898" font-size="11" font-family="'Orbitron',sans-serif" letter-spacing="1">网关探针</text>
<text y="66" text-anchor="middle" fill="#3a6858" font-size="9">GATEWAY</text>
</g>
<!-- 推理引擎探针 -->
<g id="probeIF" transform="translate(950,250)">
<circle r="46" fill="url(#probeOkGrad)" opacity=".08"/>
<polygon id="hexIF" points="0,-34 29.4,-17 29.4,17 0,34 -29.4,17 -29.4,-17" fill="none" stroke="#00e8c0" stroke-width="1.8" opacity=".7" filter="url(#glowSm)"/>
<circle r="22" fill="#00e8c0" opacity=".06"/>
<circle id="coreIF" class="node-core" r="10" fill="#00e8c0" opacity=".7" filter="url(#glowSm)"/>
<circle id="statusIF" r="30" fill="none" stroke="#00e8c0" stroke-width="2.5" opacity=".5" stroke-dasharray="8 6"/>
<circle id="alertRingIF" r="28" fill="none" stroke="#ff2d55" stroke-width="3" opacity="0"/>
<text y="52" text-anchor="middle" fill="#5ea898" font-size="11" font-family="'Orbitron',sans-serif" letter-spacing="1">推理引擎探针</text>
<text y="66" text-anchor="middle" fill="#3a6858" font-size="9">INFERENCE</text>
</g>
<!-- 算力探针 -->
<g id="probeCMP" transform="translate(600,640)">
<circle r="46" fill="url(#probeOkGrad)" opacity=".08"/>
<polygon id="hexCMP" points="0,-34 29.4,-17 29.4,17 0,34 -29.4,17 -29.4,-17" fill="none" stroke="#00e8c0" stroke-width="1.8" opacity=".7" filter="url(#glowSm)"/>
<circle r="22" fill="#00e8c0" opacity=".06"/>
<circle id="coreCMP" class="node-core" r="10" fill="#00e8c0" opacity=".7" filter="url(#glowSm)"/>
<circle id="statusCMP" r="30" fill="none" stroke="#00e8c0" stroke-width="2.5" opacity=".5" stroke-dasharray="8 6"/>
<circle id="alertRingCMP" r="28" fill="none" stroke="#ff2d55" stroke-width="3" opacity="0"/>
<text y="52" text-anchor="middle" fill="#5ea898" font-size="11" font-family="'Orbitron',sans-serif" letter-spacing="1">算力探针</text>
<text y="66" text-anchor="middle" fill="#3a6858" font-size="9">COMPUTE</text>
</g>
<!-- ===== 免疫自愈中心(大脑) ===== -->
<g id="centerNode" transform="translate(600,400)">
<!-- 外层光晕 -->
<circle r="100" fill="url(#centerGlow)" opacity=".4">
<animate attributeName="r" values="90;110;90" dur="3s" repeatCount="indefinite"/>
<animate attributeName="opacity" values=".3;.5;.3" dur="3s" repeatCount="indefinite"/>
</circle>
<!-- 自愈脉冲环 -->
<circle id="healRing1" r="60" fill="none" stroke="#4d8eff" stroke-width="2" opacity="0"/>
<circle id="healRing2" r="60" fill="none" stroke="#00e8c0" stroke-width="2" opacity="0"/>
<!-- 外六边形 -->
<polygon points="0,-62 53.7,-31 53.7,31 0,62 -53.7,31 -53.7,-31" fill="#0a1428" stroke="#4d8eff" stroke-width="2" opacity=".8" filter="url(#glowMd)"/>
<!-- 内环 -->
<circle r="42" fill="none" stroke="#4d8eff" stroke-width="1" opacity=".3" stroke-dasharray="4 8">
<animateTransform attributeName="transform" type="rotate" from="0" to="360" dur="20s" repeatCount="indefinite"/>
</circle>
<circle r="32" fill="none" stroke="#4d8eff" stroke-width="1.5" opacity=".25" stroke-dasharray="6 10">
<animateTransform attributeName="transform" type="rotate" from="360" to="0" dur="15s" repeatCount="indefinite"/>
</circle>
<!-- 核心 -->
<circle id="centerCore" r="20" fill="url(#centerGrad)" filter="url(#glowMd)">
<animate attributeName="r" values="18;22;18" dur="2.5s" repeatCount="indefinite"/>
</circle>
<!-- 核心图标(DNA/免疫符号) -->
<g opacity=".9">
<path d="M-7,-10 Q0,-5 7,-10" fill="none" stroke="#aaccff" stroke-width="1.5"/>
<path d="M-7,0 Q0,5 7,0" fill="none" stroke="#aaccff" stroke-width="1.5"/>
<path d="M-7,10 Q0,5 7,10" fill="none" stroke="#aaccff" stroke-width="1.5"/>
<line x1="-7" y1="-10" x2="-7" y2="10" stroke="#aaccff" stroke-width="1" opacity=".5"/>
<line x1="7" y1="-10" x2="7" y2="10" stroke="#aaccff" stroke-width="1" opacity=".5"/>
</g>
<!-- 标签 -->
<text y="80" text-anchor="middle" fill="#7aabff" font-size="13" font-family="'Orbitron',sans-serif" font-weight="700" letter-spacing="2">免疫自愈中心</text>
<text y="96" text-anchor="middle" fill="#4a7099" font-size="9" letter-spacing="1">IMMUNE CENTER</text>
</g>
<!-- ===== 进化轨道元素 ===== -->
<g id="evoGroup" opacity="0">
<circle id="evoParticle1" r="5" fill="#ffb800" filter="url(#glowGold)"/>
<circle id="evoParticle2" r="4" fill="#ffc940" filter="url(#glowGold)" opacity=".7"/>
<circle id="evoParticle3" r="3" fill="#ffe080" filter="url(#glowGold)" opacity=".5"/>
<text id="evoLabel" x="600" y="400" text-anchor="middle" fill="#ffb800" font-size="10" font-family="'Orbitron',sans-serif" opacity="0">LoRA 微调中</text>
</g>
<!-- ===== 指标面板 ===== -->
<g id="metricsPanel" transform="translate(1020,30)">
<rect x="0" y="0" width="160" height="130" rx="10" fill="#080e1a" stroke="#1a3050" stroke-width="1" opacity=".9"/>
<text x="80" y="22" text-anchor="middle" fill="#5e8aaa" font-size="10" font-family="'Orbitron',sans-serif" letter-spacing="1">METRICS</text>
<line x1="15" y1="30" x2="145" y2="30" stroke="#1a3050" stroke-width=".5"/>
<text x="15" y="50" fill="#5e8aaa" font-size="9">自愈延迟</text>
<text id="metricLatency" x="145" y="50" text-anchor="end" fill="#00e8c0" font-size="11" font-family="'Orbitron',sans-serif">--ms</text>
<text x="15" y="72" fill="#5e8aaa" font-size="9">微调周期</text>
<text id="metricCycle" x="145" y="72" text-anchor="end" fill="#ffb800" font-size="11" font-family="'Orbitron',sans-serif">--h</text>
<text x="15" y="94" fill="#5e8aaa" font-size="9">系统健康度</text>
<text id="metricHealth" x="145" y="94" text-anchor="end" fill="#4d8eff" font-size="11" font-family="'Orbitron',sans-serif">--%</text>
<text x="15" y="116" fill="#5e8aaa" font-size="9">自愈次数</text>
<text id="metricHeals" x="145" y="116" text-anchor="end" fill="#b366ff" font-size="11" font-family="'Orbitron',sans-serif">0</text>
</g>
<!-- 阶段指示 -->
<g id="phaseIndicator" transform="translate(20,30)">
<rect x="0" y="0" width="200" height="36" rx="8" fill="#080e1a" stroke="#1a3050" stroke-width="1" opacity=".85"/>
<circle id="phaseDot" cx="18" cy="18" r="5" fill="#00e8c0"/>
<text id="phaseText" x="32" y="22" fill="#7aabff" font-size="12" font-family="'Orbitron',sans-serif" letter-spacing="1">STEADY</text>
</g>
<!-- 病原体标记(隐藏,异常时显示) -->
<g id="pathogenGW" transform="translate(250,250)" opacity="0">
<polygon points="0,-18 5,-6 18,-6 8,3 12,16 0,8 -12,16 -8,3 -18,-6 -5,-6" fill="#ff2d55" filter="url(#glowRed)"/>
<text y="-24" text-anchor="middle" fill="#ff6b8a" font-size="9" font-family="'Orbitron',sans-serif">!</text>
</g>
<g id="pathogenIF" transform="translate(950,250)" opacity="0">
<polygon points="0,-18 5,-6 18,-6 8,3 12,16 0,8 -12,16 -8,3 -18,-6 -5,-6" fill="#ff2d55" filter="url(#glowRed)"/>
<text y="-24" text-anchor="middle" fill="#ff6b8a" font-size="9" font-family="'Orbitron',sans-serif">!</text>
</g>
<g id="pathogenCMP" transform="translate(600,640)" opacity="0">
<polygon points="0,-18 5,-6 18,-6 8,3 12,16 0,8 -12,16 -8,3 -18,-6 -5,-6" fill="#ff2d55" filter="url(#glowRed)"/>
<text y="-24" text-anchor="middle" fill="#ff6b8a" font-size="9" font-family="'Orbitron',sans-serif">!</text>
</g>
</svg>
</div>
<!-- 控制栏 -->
<div class="ctrl-bar">
<label>异常频率</label>
<input type="range" id="freqSlider" min="0" max="100" value="50"/>
<span class="ctrl-val" id="freqVal">50%</span>
<div style="width:1px;height:20px;background:#1a3050"></div>
<span class="phase-label" id="phaseLabel">STEADY · 稳态监控</span>
</div>
<script>
(function(){
/* ===== 常量与状态 ===== */
const SVG_NS = 'http://www.w3.org/2000/svg';
const PROBE_IDS = ['GW','IF','CMP'];
const PROBE_LABELS = {GW:'网关',IF:'推理引擎',CMP:'算力'};
const STATE = {IDLE:0, DETECT:1, SIGNAL:2, HEAL:3, FEEDBACK:4, EVOLVE:5};
const STATE_NAMES = ['STEADY · 稳态监控','DETECT · 异常捕获','SIGNAL · 上报中心','HEAL · 免疫自愈','FEEDBACK · 反馈采集','EVOLVE · 进化微调'];
const STATE_COLORS = ['#00e8c0','#ff2d55','#ff6b35','#4d8eff','#b366ff','#ffb800'];
let state = STATE.IDLE;
let stateTime = 0;
let globalTime = 0;
let anomalyFreq = 0.5;
let currentTarget = 0;
let healCount = 0;
let particles = [];
let animId = null;
/* ===== DOM 引用 ===== */
const svg = document.getElementById('scene');
const dynG = document.getElementById('dynamicParticles');
const freqSlider = document.getElementById('freqSlider');
const freqVal = document.getElementById('freqVal');
const phaseLabel = document.getElementById('phaseLabel');
const phaseDot = document.getElementById('phaseDot');
const metricLatency = document.getElementById('metricLatency');
const metricCycle = document.getElementById('metricCycle');
const metricHealth = document.getElementById('metricHealth');
const metricHeals = document.getElementById('metricHeals');
const probes = {};
PROBE_IDS.forEach(id => {
probes[id] = {
hex: document.getElementById('hex'+id),
core: document.getElementById('core'+id),
status: document.getElementById('status'+id),
alert: document.getElementById('alertRing'+id),
pathogen: document.getElementById('pathogen'+id),
pathFwd: document.getElementById('path'+id),
pathRev: document.getElementById('path'+id+'_rev'),
group: document.getElementById('probe'+id)
};
});
const centerHealRing1 = document.getElementById('healRing1');
const centerHealRing2 = document.getElementById('healRing2');
const evoGroup = document.getElementById('evoGroup');
const evoLabel = document.getElementById('evoLabel');
const fbIndicator = document.getElementById('fbIndicator');
/* ===== 环境粒子初始化 ===== */
function initAmbient(){
const g = document.getElementById('ambientParticles');
for(let i=0;i<40;i++){
const c = document.createElementNS(SVG_NS,'circle');
c.setAttribute('cx', Math.random()*1200);
c.setAttribute('cy', Math.random()*800);
c.setAttribute('r', Math.random()*1.5+0.5);
c.setAttribute('fill','#4d8eff');
c.setAttribute('opacity', Math.random()*.3+.05);
// 缓慢漂移动画
const an = document.createElementNS(SVG_NS,'animate');
an.setAttribute('attributeName','cy');
const y0 = parseFloat(c.getAttribute('cy'));
an.setAttribute('values', y0+','+(y0-30)+','+y0);
an.setAttribute('dur', (8+Math.random()*12)+'s');
an.setAttribute('repeatCount','indefinite');
c.appendChild(an);
g.appendChild(c);
}
}
/* ===== 粒子系统 ===== */
function spawnParticle(pathId, color, speed, size, onDone){
const pathEl = document.getElementById(pathId);
if(!pathEl) return;
const len = pathEl.getTotalLength();
const c = document.createElementNS(SVG_NS,'circle');
c.setAttribute('r', size||3);
c.setAttribute('fill', color);
c.setAttribute('opacity','0.9');
c.setAttribute('filter','url(#glowSm)');
dynG.appendChild(c);
const p = {el:c, path:pathEl, len:len, progress:0, speed:speed||0.4, onDone:onDone, alive:true};
particles.push(p);
return p;
}
function updateParticles(dt){
for(let i=particles.length-1;i>=0;i--){
const p = particles[i];
p.progress += p.speed * dt;
if(p.progress >= 1){
p.el.remove();
particles.splice(i,1);
if(p.onDone) p.onDone();
} else {
const pt = p.path.getPointAtLength(p.progress * p.len);
p.el.setAttribute('cx', pt.x);
p.el.setAttribute('cy', pt.y);
}
}
}
/* ===== 状态机 ===== */
// 时间参数(秒)
const IDLE_DUR = 2.5;
const DETECT_DUR = 0.8;
const SIGNAL_DUR = 0.6;
const HEAL_DUR = 1.5;
const FEEDBACK_DUR = 1.2;
const EVOLVE_DUR = 2.0;
function setState(s){
state = s;
stateTime = 0;
phaseLabel.textContent = STATE_NAMES[s];
phaseDot.setAttribute('fill', STATE_COLORS[s]);
const tid = PROBE_IDS[currentTarget];
const pr = probes[tid];
switch(s){
case STATE.IDLE:
// 重置所有探针外观
PROBE_IDS.forEach(id => {
const p = probes[id];
p.hex.setAttribute('stroke','#00e8c0');
p.core.setAttribute('fill','#00e8c0');
p.core.setAttribute('opacity','.7');
p.status.setAttribute('stroke','#00e8c0');
p.status.setAttribute('opacity','.5');
p.alert.setAttribute('opacity','0');
p.pathogen.setAttribute('opacity','0');
p.pathFwd.setAttribute('stroke','#12283e');
});
centerHealRing1.setAttribute('opacity','0');
centerHealRing2.setAttribute('opacity','0');
evoGroup.setAttribute('opacity','0');
fbIndicator.setAttribute('opacity','0');
// 更新指标
metricLatency.textContent = '≤100ms';
metricCycle.textContent = '≤24h';
metricHealth.textContent = (95+Math.random()*5).toFixed(1)+'%';
metricHeals.textContent = healCount;
break;
case STATE.DETECT:
// 目标探针变红
pr.hex.setAttribute('stroke','#ff2d55');
pr.core.setAttribute('fill','#ff2d55');
pr.core.setAttribute('opacity','1');
pr.status.setAttribute('stroke','#ff2d55');
pr.status.setAttribute('opacity','.8');
pr.alert.setAttribute('opacity','0.8');
pr.pathogen.setAttribute('opacity','1');
pr.pathFwd.setAttribute('stroke','#3a1020');
// 告警环脉动
animateAlertRing(pr.alert);
break;
case STATE.SIGNAL:
// 信号粒子飞向中心
spawnParticle('path'+tid, '#ff6b35', 1.8, 4, null);
// 快速小粒子
spawnParticle('path'+tid, '#ff2d55', 2.2, 2.5, null);
break;
case STATE.HEAL:
// 中心激活 - 发出治愈脉冲
pr.pathogen.setAttribute('opacity','0');
animateHealRings();
// 治愈粒子飞向探针
setTimeout(()=>{
spawnParticle('path'+tid+'_rev', '#00e8c0', 1.6, 5, ()=>{
// 探针恢复
pr.hex.setAttribute('stroke','#00e8c0');
pr.core.setAttribute('fill','#00e8c0');
pr.core.setAttribute('opacity','.7');
pr.status.setAttribute('stroke','#00e8c0');
pr.status.setAttribute('opacity','.5');
pr.alert.setAttribute('opacity','0');
pr.pathFwd.setAttribute('stroke','#12283e');
healCount++;
});
}, 300);
break;
case STATE.FEEDBACK:
// 业务反馈指示
fbIndicator.setAttribute('opacity','1');
// 反馈粒子沿路径下行
spawnParticle('pathFB', '#b366ff', 0.8, 4, null);
setTimeout(()=> spawnParticle('pathFB', '#9b4dff', 1.0, 3, null), 200);
setTimeout(()=> spawnParticle('pathFB', '#d88fff', 1.2, 2, null), 400);
break;
case STATE.EVOLVE:
// 进化轨道动画
evoGroup.setAttribute('opacity','1');
animateEvolution();
break;
}
}
function advanceState(){
switch(state){
case STATE.IDLE:
currentTarget = (currentTarget + 1) % 3;
setState(STATE.DETECT);
break;
case STATE.DETECT:
setState(STATE.SIGNAL);
break;
case STATE.SIGNAL:
setState(STATE.HEAL);
break;
case STATE.HEAL:
setState(STATE.FEEDBACK);
break;
case STATE.FEEDBACK:
setState(STATE.EVOLVE);
break;
case STATE.EVOLVE:
setState(STATE.IDLE);
break;
}
}
function getStateDuration(){
const freq = anomalyFreq;
switch(state){
case STATE.IDLE: return IDLE_DUR * (1.5 - freq);
case STATE.DETECT: return DETECT_DUR;
case STATE.SIGNAL: return SIGNAL_DUR * (0.6 + 0.4*(1-freq));
case STATE.HEAL: return HEAL_DUR;
case STATE.FEEDBACK: return FEEDBACK_DUR;
case STATE.EVOLVE: return EVOLVE_DUR;
default: return 2;
}
}
/* ===== 视觉特效 ===== */
function animateAlertRing(ring){
let start = null;
function tick(ts){
if(!start) start = ts;
const p = ((ts - start) % 800) / 800;
const r = 28 + p * 30;
const op = 0.8 * (1 - p);
ring.setAttribute('r', r);
ring.setAttribute('opacity', op);
if(state === STATE.DETECT || state === STATE.SIGNAL) requestAnimationFrame(tick);
else ring.setAttribute('opacity','0');
}
requestAnimationFrame(tick);
}
function animateHealRings(){
let start = null;
function tick(ts){
if(!start) start = ts;
const elapsed = ts - start;
// 环1
const p1 = (elapsed % 1200) / 1200;
centerHealRing1.setAttribute('r', 60 + p1*60);
centerHealRing1.setAttribute('opacity', 0.6*(1-p1));
// 环2
const p2 = ((elapsed+400) % 1200) / 1200;
centerHealRing2.setAttribute('r', 60 + p2*60);
centerHealRing2.setAttribute('opacity', 0.5*(1-p2));
if(state === STATE.HEAL) requestAnimationFrame(tick);
else {
centerHealRing1.setAttribute('opacity','0');
centerHealRing2.setAttribute('opacity','0');
}
}
requestAnimationFrame(tick);
}
let evoAngle = 0;
function animateEvolution(){
let start = null;
const dur = EVOLVE_DUR * 1000;
function tick(ts){
if(!start) start = ts;
const elapsed = ts - start;
const progress = elapsed / dur;
evoAngle = progress * Math.PI * 4;
const cx = 600 + 130 * Math.cos(evoAngle);
const cy = 400 + 50 * Math.sin(evoAngle);
const cx2 = 600 + 130 * Math.cos(evoAngle + Math.PI*0.6);
const cy2 = 400 + 50 * Math.sin(evoAngle + Math.PI*0.6);
const cx3 = 600 + 130 * Math.cos(evoAngle + Math.PI*1.2);
const cy3 = 400 + 50 * Math.sin(evoAngle + Math.PI*1.2);
document.getElementById('evoParticle1').setAttribute('cx', cx);
document.getElementById('evoParticle1').setAttribute('cy', cy);
document.getElementById('evoParticle2').setAttribute('cx', cx2);
document.getElementById('evoParticle2').setAttribute('cy', cy2);
document.getElementById('evoParticle3').setAttribute('cx', cx3);
document.getElementById('evoParticle3').setAttribute('cy', cy3);
// 标签渐显
const labelOp = Math.min(1, progress * 3) * (progress < 0.8 ? 1 : (1-progress)/0.2);
evoLabel.setAttribute('opacity', labelOp);
evoLabel.setAttribute('y', 400 + 70);
if(state === STATE.EVOLVE) requestAnimationFrame(tick);
else evoGroup.setAttribute('opacity','0');
}
requestAnimationFrame(tick);
}
/* ===== 持续数据流粒子 ===== */
let flowTimer = 0;
function spawnFlowParticles(dt){
flowTimer += dt;
if(flowTimer > 0.6){
flowTimer = 0;
// 每条路径偶尔产生一个流动粒子
PROBE_IDS.forEach((id,i) => {
if(Math.random() < 0.5){
spawnParticle('path'+id, '#00e8c044', 0.35, 2, null);
}
});
if(Math.random() < 0.3){
spawnParticle('pathFB', '#b366ff33', 0.25, 1.5, null);
}
}
}
/* ===== 主循环 ===== */
let lastTs = 0;
function loop(ts){
if(!lastTs) lastTs = ts;
const dt = Math.min((ts - lastTs)/1000, 0.1);
lastTs = ts;
globalTime += dt;
stateTime += dt;
// 状态推进
if(stateTime >= getStateDuration()){
advanceState();
}
// 更新粒子
updateParticles(dt);
spawnFlowParticles(dt);
// 实时更新指标
if(state === STATE.SIGNAL){
metricLatency.textContent = (40 + Math.random()*55).toFixed(0)+'ms';
}
animId = requestAnimationFrame(loop);
}
/* ===== 交互控件 ===== */
freqSlider.addEventListener('input', function(){
anomalyFreq = this.value / 100;
freqVal.textContent = this.value + '%';
});
/* ===== 初始化与启动 ===== */
function init(){
initAmbient();
setState(STATE.IDLE);
animId = requestAnimationFrame(loop);
}
// 页面加载后自动启动
if(document.readyState === 'loading'){
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
// 确保 iframe 重新加载时也自动播放
window.addEventListener('load', ()=>{
if(!animId) init();
});
})();
</script>
</body>
</html>
实现说明
这是一个基于 TRIZ 最终理想解 (IFR) 思想设计的 SVG 原理动画,直接展示数字免疫自愈系统的理想运作状态:
视觉设计
- 深空生物发光风格:深蓝黑底色搭配青色(探针健康态)、红色(异常)、蓝色(免疫中心)、金色(进化)、紫色(反馈轴突),营造"数字生命体"的视觉隐喻
- 六边形探针节点 + 中心大脑结构,免疫中心内含 DNA 双螺旋符号,强化生物免疫意象
- 多层辉光滤镜(glowSm/Md/Lg/Red/Gold)营造霓虹深度感
动画循环(自动播放,6 阶段状态机)
- STEADY — 稳态监控,探针柔和脉动,数据流粒子缓慢流动
- DETECT — 异常捕获,目标探针变红,病原体星形标记出现,告警环脉动
- SIGNAL — 上报中心,橙红信号粒子沿路径飞向免疫中心
- HEAL — 免疫自愈,中心发出蓝绿治愈脉冲环,治愈粒子返回探针,探针恢复青色
- FEEDBACK — 反馈采集,业务前端发送紫色反馈粒子
- EVOLVE — 进化微调,金色粒子沿轨道环绕中心旋转,"LoRA 微调中"标签渐显
IFR 体现
- 无对比、直展示:不展示故障前状态,直接呈现"异常出现即被消灭"的理想态
- 资源极简:探针轻量嵌入(小六边形),免疫中心独立运作(不增加业务复杂度)
- 视觉引导:红色异常→快速橙红信号→蓝绿治愈脉冲→金色进化,色彩序列清晰引导关注破局关键
交互
- 异常频率滑块:控制异常出现间隔与信号传播速度,直观体验不同压力下的自愈能力
- 实时指标面板显示自愈延迟(≤100ms)、微调周期、系统健康度、自愈次数
积分规则:第一轮对话扣减8分,后续每轮扣6分
等待动画代码生成...
