分享图
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=Orbitron:wght@400;600;700;900&family=Noto+Sans+SC:wght@300;400;500;700&display=swap" rel="stylesheet">
<style>
:root{--bg:#070b12;--fg:#dfe6f0;--muted:#5a6a84;--accent:#00ffc8;--accent2:#00d4aa;--tobacco:#c49038;--tobacco-lt:#e2b85a;--danger:#ff5240;--card:#0c1220;--border:#172038;--steel:#3e5272;--steel-lt:#6888aa}
*{margin:0;padding:0;box-sizing:border-box}
body{background:var(--bg);background-image:radial-gradient(ellipse at 30% 40%,rgba(0,255,200,.025) 0%,transparent 55%),radial-gradient(ellipse at 75% 60%,rgba(0,180,140,.015) 0%,transparent 50%);color:var(--fg);font-family:'Noto Sans SC',sans-serif;min-height:100vh;display:flex;flex-direction:column;align-items:center;padding:22px 14px 32px}
.header{text-align:center;margin-bottom:20px;max-width:960px}
.header h1{font-family:'Orbitron',monospace;font-weight:900;font-size:clamp(16px,2.8vw,28px);background:linear-gradient(135deg,var(--accent) 0%,#80ffdb 45%,var(--accent2) 100%);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;letter-spacing:3px;margin-bottom:8px}
.header .sub{font-size:13px;color:var(--muted);font-weight:300;line-height:1.7}
.header .sub b{color:var(--accent2);font-weight:500}
.wrap{width:100%;max-width:1440px;background:linear-gradient(175deg,#0a1018 0%,#0d1424 100%);border-radius:18px;border:1px solid var(--border);overflow:hidden;box-shadow:0 0 90px rgba(0,255,200,.035),0 24px 64px rgba(0,0,0,.55),inset 0 1px 0 rgba(255,255,255,.025)}
.wrap svg{width:100%;height:auto;display:block}
.ctrls{width:100%;max-width:1440px;margin-top:20px;display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:14px}
.ctrl{background:linear-gradient(140deg,#0d1524,#101c2e);border:1px solid var(--border);border-radius:13px;padding:16px 20px;transition:border-color .3s,box-shadow .3s}
.ctrl:hover{border-color:rgba(0,255,200,.18);box-shadow:0 0 20px rgba(0,255,200,.04)}
.ctrl-lbl{font-family:'Orbitron',monospace;font-size:9.5px;letter-spacing:2px;color:var(--accent);text-transform:uppercase;margin-bottom:5px;display:flex;align-items:center;gap:6px}
.ctrl-lbl .dot{width:5px;height:5px;border-radius:50%;background:var(--accent);box-shadow:0 0 8px var(--accent);animation:pulse 2s ease-in-out infinite}
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.4}}
.ctrl-val{font-family:'Orbitron',monospace;font-size:20px;font-weight:700;color:var(--fg);margin-bottom:10px}
.ctrl-val .u{font-size:11px;color:var(--muted);font-weight:400;margin-left:3px}
input[type=range]{width:100%;-webkit-appearance:none;appearance:none;height:3px;background:var(--border);border-radius:2px;outline:none;cursor:pointer}
input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:14px;height:14px;border-radius:50%;background:var(--accent);cursor:pointer;box-shadow:0 0 12px rgba(0,255,200,.55);transition:transform .2s}
input[type=range]::-webkit-slider-thumb:hover{transform:scale(1.3)}
input[type=range]::-moz-range-thumb{width:14px;height:14px;border:none;border-radius:50%;background:var(--accent);cursor:pointer;box-shadow:0 0 12px rgba(0,255,200,.55)}
.legend{width:100%;max-width:1440px;margin-top:18px;display:flex;flex-wrap:wrap;gap:20px;justify-content:center;font-size:11.5px;color:var(--muted)}
.legend-item{display:flex;align-items:center;gap:7px}
.legend-c{width:14px;height:10px;border-radius:2px}
.note{width:100%;max-width:1440px;margin-top:14px;text-align:center;font-size:12px;color:var(--muted);line-height:1.7;opacity:.7}
@media(max-width:768px){body{padding:10px 6px 24px}.ctrls{grid-template-columns:1fr 1fr;gap:8px}.ctrl{padding:12px 13px}.ctrl-val{font-size:16px}}
</style>
</head>
<body>

<header class="header">
  <h1>PNEUMATIC PRE-LEVELING · IFR</h1>
  <p class="sub">将控制重心从<b>「事后修削」</b>前移至<b>「事前均平」</b>——在平准器上游增设气动预均平辊与高频微幅振动梳,<br>于源头消除密度波动,使平准器趋近<b>零修削</b>,实现重量反馈的终极稳定。</p>
</header>

<div class="wrap">
  <svg id="mainSvg" viewBox="0 0 1400 800" xmlns="http://www.w3.org/2000/svg"></svg>
</div>

<section class="ctrls">
  <div class="ctrl">
    <div class="ctrl-lbl"><span class="dot"></span>预均平辊转速比</div>
    <div class="ctrl-val" id="vRoller">1.10<span class="u">&times;吸丝带</span></div>
    <input type="range" id="sRoller" min="0.90" max="1.30" step="0.01" value="1.10">
  </div>
  <div class="ctrl">
    <div class="ctrl-lbl"><span class="dot"></span>振动梳频率</div>
    <div class="ctrl-val" id="vFreq">200<span class="u">Hz</span></div>
    <input type="range" id="sFreq" min="50" max="300" step="5" value="200">
  </div>
  <div class="ctrl">
    <div class="ctrl-lbl"><span class="dot"></span>振动梳振幅</div>
    <div class="ctrl-val" id="vAmp">0.50<span class="u">mm</span></div>
    <input type="range" id="sAmp" min="0.10" max="1.50" step="0.05" value="0.50">
  </div>
  <div class="ctrl">
    <div class="ctrl-lbl"><span class="dot"></span>烟丝含水率</div>
    <div class="ctrl-val" id="vMoist">25<span class="u">%</span></div>
    <input type="range" id="sMoist" min="10" max="50" step="1" value="25">
  </div>
</section>

<div class="legend">
  <div class="legend-item"><div class="legend-c" style="background:#d06030"></div>密度偏高区</div>
  <div class="legend-item"><div class="legend-c" style="background:#c89038"></div>密度正常区</div>
  <div class="legend-item"><div class="legend-c" style="background:#e8c060"></div>密度偏低区</div>
  <div class="legend-item"><div class="legend-c" style="background:#00ffc8"></div>核心创新区</div>
  <div class="legend-item"><div class="legend-c" style="background:#3e5272"></div>机械部件</div>
</div>

<p class="note">含水率超过 35% 时烟丝结团加剧,振动梳打散效果显著下降——拖动滑块体验失效边界</p>

<script>
(function(){
/* ===================== 常量与配置 ===================== */
const NS='http://www.w3.org/2000/svg';
const svg=document.getElementById('mainSvg');

const cfg={beltSpeed:1.0,rollerRatio:1.1,combFreq:200,combAmp:0.5,moisture:0.25,target:0.58};

/* 布局坐标 */
const P={
  beltY:470,beltH:13,bx0:65,bx1:1335,
  rollerX:410,rollerCY:388,rollerR:52,
  combX:670,trimX:875,detX:1080,
  streamMinH:14,streamMaxH:58,
  gx0:100,gx1:1300,gy0:605,gy1:755
};

/* ===================== 工具函数 ===================== */
function mkEl(tag,a,p){const e=document.createElementNS(NS,tag);if(a)for(const[k,v]of Object.entries(a))e.setAttribute(k,v);if(p)p.appendChild(e);return e}
function lerp(a,b,t){return a+(b-a)*t}
function clamp(v,lo,hi){return Math.max(lo,Math.min(hi,v))}

/* 多频叠加模拟烟丝密度自然波动 */
function rawNoise(x){
  return Math.sin(x*2.3+.4)*.28+Math.sin(x*5.1+1.9)*.2+Math.sin(x*9.7+3.3)*.12+Math.sin(x*17.1+.7)*.06+Math.sin(x*28.3+5.1)*.03;
}

let scrollOff=0,t=0;

/* 原始密度函数 */
function rawD(x){
  const n=(x+scrollOff)*.018;
  const mf=1+cfg.moisture*2.2;
  return clamp(.5+rawNoise(n)*.38*mf,.08,.98);
}

/* 经各站处理后的密度 */
function procD(x){
  let d=rawD(x);
  /* 预均平辊:压实+延展,向目标靠拢 */
  if(x>P.rollerX){
    const pr=clamp((x-P.rollerX)/90,0,1);
    const eff=clamp(cfg.rollerRatio-.9,0,.4)/.4*.55;
    d=lerp(d,cfg.target,eff*pr);
  }
  /* 振动梳:打散+重分布 */
  if(x>P.combX){
    const pr=clamp((x-P.combX)/70,0,1);
    const moistPen=clamp(1-(cfg.moisture-.28)*3,.15,1);
    const freqEff=clamp((cfg.combFreq-80)/220,0,1);
    const ampEff=clamp(cfg.combAmp/.6,0,1.3);
    const eff=.85*pr*moistPen*freqEff*ampEff;
    d=lerp(d,cfg.target,eff);
  }
  /* 平准器:微量修削 */
  if(x>P.trimX){
    d=Math.min(d,cfg.target+.015);
    d=lerp(d,cfg.target,.6);
  }
  return clamp(d,.05,.99);
}

function d2h(d){return lerp(P.streamMinH,P.streamMaxH,d)}
function d2color(d){
  if(d>cfg.target+.08)return lerp_color([208,96,48],[200,144,56],clamp((d-cfg.target-.08)/.25,0,1));
  if(d<cfg.target-.08)return lerp_color([232,192,96],[240,220,140],clamp((cfg.target-.08-d)/.2,0,1));
  return lerp_color([200,144,56],[210,170,80],clamp((d-cfg.target+.08)/.16,0,1));
}
function lerp_color(a,b,t){return`rgb(${Math.round(lerp(a[0],b[0],t))},${Math.round(lerp(a[1],b[1],t))},${Math.round(lerp(a[2],b[2],t))})`}

/* ===================== 构建SVG场景 ===================== */

/* -- defs -- */
const defs=mkEl('defs',null,svg);

/* 背景渐变 */
const bgGrad=mkEl('linearGradient',{id:'bgG',x1:'0',y1:'0',x2:'0',y2:'1'},defs);
mkEl('stop',{offset:'0%','stop-color':'#0a0f1a'},bgGrad);
mkEl('stop',{offset:'100%','stop-color':'#060a12'},bgGrad);

/* 烟丝流渐变 */
const stGrad=mkEl('linearGradient',{id:'stG',x1:'0',y1:'0',x2:'1',y2:'0'},defs);
mkEl('stop',{offset:'0%','stop-color':'#c06830'},stGrad);
mkEl('stop',{offset:'30%','stop-color':'#c89038'},stGrad);
mkEl('stop',{offset:'55%','stop-color':'#cca050'},stGrad);
mkEl('stop',{offset:'80%','stop-color':'#b8a050'},stGrad);
mkEl('stop',{offset:'100%','stop-color':'#a0a848'},stGrad);

/* 发光滤镜 */
const glow=mkEl('filter',{id:'glow',x:'-50%',y:'-50%',width:'200%',height:'200%'},defs);
mkEl('feGaussianBlur',{in:'SourceGraphic',stdDeviation:'6',result:'b'},glow);
const gm=mkEl('feMerge',null,glow);
mkEl('feMergeNode',{in:'b'},gm);
mkEl('feMergeNode',{in:'SourceGraphic'},gm);

const glow2=mkEl('filter',{id:'glow2',x:'-50%',y:'-50%',width:'200%',height:'200%'},defs);
mkEl('feGaussianBlur',{in:'SourceGraphic',stdDeviation:'12',result:'b'},glow2);
mkEl('feMergeNode',null,glow2).append(mkEl('feMergeNode',{in:'b'}),mkEl('feMergeNode',{in:'SourceGraphic'}));

/* 皮带纹理 */
const beltPat=mkEl('pattern',{id:'beltP',width:'24',height:P.beltH,patternUnits:'userSpaceOnUse'},defs);
mkEl('rect',{width:'24',height:P.beltH,fill:'#1e2a3e'},beltPat);
mkEl('line',{x1:'0',y1:'0',x2:'0',y2:P.beltH,stroke:'#283852','stroke-width':'1'},beltPat);
mkEl('line',{x1:'12',y1:'2',x2:'12',y2:P.beltH-2,stroke:'#243348','stroke-width':'.5'},beltPat);

/* 密度图渐变 */
const dgGrad=mkEl('linearGradient',{id:'dgG',x1:'0',y1:'0',x2:'1',y2:'0'},defs);
mkEl('stop',{offset:'0%','stop-color':'#d06030'},dgGrad);
mkEl('stop',{offset:'35%','stop-color':'#c89038'},dgGrad);
mkEl('stop',{offset:'70%','stop-color':'#00ffc8'},dgGrad);
mkEl('stop',{offset:'100%','stop-color':'#00ffc8'},dgGrad);

/* 网格图案 */
const gridP=mkEl('pattern',{id:'gridP',width:'40',height:'40',patternUnits:'userSpaceOnUse'},defs);
mkEl('path',{d:'M 40 0 L 0 0 0 40',fill:'none',stroke:'rgba(30,50,80,.18)','stroke-width':'.5'},gridP);

/* -- 背景 -- */
mkEl('rect',{width:'1400',height:'800',fill:'url(#bgG)'},svg);
mkEl('rect',{width:'1400',height:'800',fill:'url(#gridP)',opacity:'.4'},svg);

/* -- 创新区高亮 -- */
const innovZone=mkEl('rect',{x:P.rollerX-70,y:310,width:P.combX-P.rollerX+140,height:200,rx:16,fill:'none',stroke:'rgba(0,255,200,.12)','stroke-width':'1.5','stroke-dasharray':'6 4',filter:'url(#glow2)'},svg);
const innovFill=mkEl('rect',{x:P.rollerX-70,y:310,width:P.combX-P.rollerX+140,height:200,rx:16,fill:'rgba(0,255,200,.018)'},svg);

/* -- 吸丝带 -- */
mkEl('rect',{x:P.bx0,y:P.beltY,width:P.bx1-P.bx0,height:P.beltH,fill:'url(#beltP)',rx:2},svg);
/* 皮带运动线 */
const beltLinesG=mkEl('g',{opacity:'.35'},svg);
const beltLines=[];
for(let i=0;i<40;i++){
  const lx=P.bx0+i*32;
  const bl=mkEl('line',{x1:lx,y1:P.beltY+3,x2:lx+12,y2:P.beltY+3,stroke:'#4a6888','stroke-width':'1.5','stroke-linecap':'round'},beltLinesG);
  beltLines.push(bl);
}

/* 皮带标注 */
mkEl('text',{x:P.bx0+10,y:P.beltY+P.beltH+18,fill:'#3e5272','font-size':'11','font-family':'Orbitron,monospace','letter-spacing':'1.5'},svg).textContent='SUCTION BELT →';

/* -- 烟丝流(用多个窄矩形拼接) -- */
const streamG=mkEl('g',null,svg);
const STREAM_STEP=6;
const streamRects=[];
for(let x=P.bx0;x<P.bx1;x+=STREAM_STEP){
  const r=mkEl('rect',{x:x,y:P.beltY-d2h(.5),width:STREAM_STEP+1,height:d2h(.5),fill:'#c89038',opacity:'.88'},streamG);
  streamRects.push({el:r,x:x});
}

/* 烟丝纤维纹理 */
const fiberG=mkEl('g',{opacity:'.45'},svg);
const fibers=[];
for(let i=0;i<55;i++){
  const fx=P.bx0+Math.random()*(P.bx1-P.bx0);
  const l=mkEl('line',{x1:fx,y1:P.beltY-20,x2:fx+4,y2:P.beltY-18,stroke:'#a07828','stroke-width':'1.2','stroke-linecap':'round'},fiberG);
  fibers.push({el:l,x:fx,off:Math.random()*8,phase:Math.random()*6.28});
}

/* -- 预均平辊 -- */
const rollerG=mkEl('g',null,svg);
/* 辊体阴影 */
mkEl('ellipse',{cx:P.rollerX,cy:P.beltY+4,rx:P.rollerR+6,ry:8,fill:'rgba(0,0,0,.25)'},rollerG);
/* 辊体 */
mkEl('circle',{cx:P.rollerX,cy:P.rollerCY,r:P.rollerR,fill:'#2a3a52',stroke:'#4a6a8a','stroke-width':'2'},rollerG);
/* 辊面纹理(旋转标记) */
const rollerMarksG=mkEl('g',null,rollerG);
const rMarks=[];
for(let i=0;i<8;i++){
  const a=i*Math.PI/4;
  const m=mkEl('line',{
    x1:P.rollerX+Math.cos(a)*20,y1:P.rollerCY+Math.sin(a)*20,
    x2:P.rollerX+Math.cos(a)*P.rollerR*.88,y2:P.rollerCY+Math.sin(a)*P.rollerR*.88,
    stroke:'#5a7a9a','stroke-width':'1.5','stroke-linecap':'round'
  },rollerMarksG);
  rMarks.push({el:m,a:i*Math.PI/4});
}
/* 中心轴 */
mkEl('circle',{cx:P.rollerX,cy:P.rollerCY,r:8,fill:'#1a2a3e',stroke:'#5a7a9a','stroke-width':'1.5'},rollerG);
mkEl('circle',{cx:P.rollerX,cy:P.rollerCY,r:3,fill:'#6a8aaa'},rollerG);
/* 旋转方向箭头 */
const rotArrow=mkEl('path',{d:`M${P.rollerX+P.rollerR+10} ${P.rollerCY-8} l8 4 l-8 4`,fill:'none',stroke:'rgba(0,255,200,.5)','stroke-width':'1.5','stroke-linecap':'round','stroke-linejoin':'round'},rollerG);

/* -- 振动梳 -- */
const combBaseY=P.beltY-12;
const combG=mkEl('g',null,svg);
/* 梳座 */
mkEl('rect',{x:P.combX-22,y:combBaseY-80,width:44,height:18,rx:4,fill:'#2a3a52',stroke:'#4a6a8a','stroke-width':'1.5'},combG);
/* 梳齿组 */
const teethG=mkEl('g',null,combG);
const teeth=[];
for(let i=0;i<7;i++){
  const tx=P.combX-15+i*5;
  const tooth=mkEl('rect',{x:tx,y:combBaseY-62,width:2.5,height:62,rx:1,fill:'#5a8aaa'},teethG);
  teeth.push(tooth);
}
/* 振动波纹 */
const vibRings=[];
for(let i=0;i<3;i++){
  const r=mkEl('ellipse',{cx:P.combX,cy:combBaseY-30,rx:30+i*14,ry:12+i*6,fill:'none',stroke:`rgba(0,255,200,${.12-i*.035})`,'stroke-width':'1','stroke-dasharray':'3 3'},combG);
  vibRings.push(r);
}
/* 振幅标注线 */
const ampLine1=mkEl('line',{x1:P.combX+30,y1:combBaseY-35,x2:P.combX+30,y2:combBaseY-25,stroke:'rgba(0,255,200,.5)','stroke-width':'1','stroke-dasharray':'2 2'},combG);
const ampLine2=mkEl('line',{x1:P.combX+28,y1:combBaseY-35,x2:P.combX+32,y2:combBaseY-35,stroke:'rgba(0,255,200,.5)','stroke-width':'1'},combG);
const ampLine3=mkEl('line',{x1:P.combX+28,y1:combBaseY-25,x2:P.combX+32,y2:combBaseY-25,stroke:'rgba(0,255,200,.5)','stroke-width':'1'},combG);

/* 飞溅粒子(振动梳打散效果) */
const sprayG=mkEl('g',null,svg);
const sprayParts=[];
for(let i=0;i<12;i++){
  const sp=mkEl('circle',{cx:P.combX,cy:combBaseY-30,r:1.5+Math.random(),fill:`rgba(200,144,56,${.3+Math.random()*.3})`},sprayG);
  sprayParts.push({el:sp,phase:Math.random()*6.28,speed:.5+Math.random()*1.5,radius:8+Math.random()*18});
}

/* -- 平准器 -- */
const trimG=mkEl('g',null,svg);
mkEl('rect',{x:P.trimX-18,y:P.beltY-55,width:36,height:12,rx:3,fill:'#2a3a52',stroke:'#4a6a8a','stroke-width':'1.5'},trimG);
/* 刀口 */
mkEl('path',{d:`M${P.trimX-14} ${P.beltY-43} L${P.trimX} ${P.beltY-32} L${P.trimX+14} ${P.beltY-43}`,fill:'#3e5272',stroke:'#7a9aba','stroke-width':'1.5','stroke-linejoin':'round'},trimG);
/* 微量修削标注 */
const trimSpark=mkEl('circle',{cx:P.trimX,cy:P.beltY-28,r:2,fill:'rgba(0,255,200,.6)',filter:'url(#glow)'},trimG);

/* -- 重量检测仪 -- */
const detG=mkEl('g',null,svg);
mkEl('rect',{x:P.detX-24,y:P.beltY-65,width:48,height:52,rx:6,fill:'#1a2840',stroke:'#3e5a7a','stroke-width':'1.5'},detG);
mkEl('rect',{x:P.detX-18,y:P.beltY-58,width:36,height:24,rx:3,fill:'#0a1420'},detG);
/* 检测仪屏幕数值 */
const detVal=mkEl('text',{x:P.detX,y:P.beltY-40,fill:'#00ffc8','font-size':'13','font-family':'Orbitron,monospace','text-anchor':'middle','font-weight':'700'},detG);
detVal.textContent='0.58';
/* 检测仪指示灯 */
const detLed=mkEl('circle',{cx:P.detX,cy:P.beltY-22,r:4,fill:'#00ffc8',filter:'url(#glow)'},detG);
mkEl('text',{x:P.detX,y:P.beltY-8,fill:'#5a7a9a','font-size':'8','font-family':'Orbitron,monospace','text-anchor':'middle','letter-spacing':'1'},detG).textContent='WEIGHT';

/* -- 标注系统 -- */
const labelG=mkEl('g',null,svg);
function addLabel(x,y,text,color,size){
  const tx=mkEl('text',{x:x,y:y,fill:color||'#8898b0','font-size':size||'12','font-family':'Noto Sans SC,sans-serif','text-anchor':'middle','font-weight':'500'},labelG);
  tx.textContent=text;return tx;
}
function addTag(x,y,text,color){
  const g=mkEl('g',null,labelG);
  const t=mkEl('text',{x:x,y:y,fill:color||'#8898b0','font-size':'10.5','font-family':'Noto Sans SC,sans-serif','text-anchor':'middle'},g);
  t.textContent=text;
  return g;
}

addLabel(P.rollerX,P.rollerCY-P.rollerR-16,'预均平辊','#6aaaca',13);
addTag(P.rollerX,P.rollerCY-P.rollerR-3,'转速比吸丝带快10%','rgba(0,255,200,.6)');

addLabel(P.combX,combBaseY-98,'高频微幅振动梳','#6aaaca',13);
addTag(P.combX,combBaseY-85,'200Hz / 0.5mm','rgba(0,255,200,.6)');

addLabel(P.trimX,P.beltY-72,'平准器','#8898b0',12);
addTag(P.trimX,P.beltY-60,'趋近零修削','rgba(0,255,200,.5)');

addLabel(P.detX,P.beltY-82,'重量检测','#8898b0',12);

/* 步骤序号 */
const steps=[
  {x:180,y:P.beltY-75,n:'1',t:'吸丝带输送'},
  {x:P.rollerX,y:P.rollerCY+P.rollerR+26,n:'2',t:'粗平:压实延展'},
  {x:P.combX,y:combBaseY+30,n:'3',t:'精平:打散重分布'},
  {x:P.trimX,y:P.beltY+32,n:'4',t:'微量修削'},
  {x:P.detX,y:P.beltY+32,n:'5',t:'稳定反馈'}
];
steps.forEach(s=>{
  const c=mkEl('circle',{cx:s.x-30,cy:s.y-4,r:10,fill:'none',stroke:'rgba(0,255,200,.35)','stroke-width':'1.2'},labelG);
  mkEl('text',{x:s.x-30,y:s.y,sfill:'#00ffc8','font-size':'10','font-family':'Orbitron,monospace','text-anchor':'middle','font-weight':'700',fill:'#00ffc8'},labelG).textContent=s.n;
  mkEl('text',{x:s.x-14,y:s.y,fill:'#8898b0','font-size':'11','font-family':'Noto Sans SC,sans-serif'},labelG).textContent=s.t;
});

/* 创新区顶部标注 */
const ifrLabel=mkEl('text',{x:(P.rollerX+P.combX)/2,y:300,fill:'rgba(0,255,200,.55)','font-size':'11','font-family':'Orbitron,monospace','text-anchor':'middle','letter-spacing':'3'},labelG);
ifrLabel.textContent='◆ CORE INNOVATION ZONE ◆';

/* IFR理念标注 */
mkEl('text',{x:P.trimX+5,y:P.beltY+58,fill:'rgba(0,255,200,.4)','font-size':'10','font-family':'Noto Sans SC,sans-serif','text-anchor':'middle'},labelG).textContent='事前均平 → 接近零修削 = IFR';

/* -- 密度分布图 -- */
const graphG=mkEl('g',null,svg);
/* 图区背景 */
mkEl('rect',{x:P.gx0-10,y:P.gy0-25,width:P.gx1-P.gx0+20,height:P.gy1-P.gy0+40,rx:8,fill:'rgba(10,16,28,.7)',stroke:'rgba(30,50,80,.3)','stroke-width':'1'},graphG);
mkEl('text',{x:P.gx0,y:P.gy0-8,fill:'#5a7a9a','font-size':'10','font-family':'Orbitron,monospace','letter-spacing':'2'},graphG).textContent='DENSITY PROFILE';
/* 目标线 */
const tgtY=lerp(P.gy1,P.gy0,cfg.target);
mkEl('line',{x1:P.gx0,y1:tgtY,x2:P.gx1,y2:tgtY,stroke:'rgba(0,255,200,.2)','stroke-width':'1','stroke-dasharray':'4 4'},graphG);
mkEl('text',{x:P.gx1+5,y:tgtY+3,fill:'rgba(0,255,200,.4)','font-size':'8','font-family':'Orbitron,monospace'},graphG).textContent='TARGET';

/* 站区标记 */
const stations=[
  {x:P.rollerX,label:'辊',c:'rgba(0,255,200,.15)'},
  {x:P.combX,label:'梳',c:'rgba(0,255,200,.12)'},
  {x:P.trimX,label:'削',c:'rgba(100,140,180,.1)'}
];
stations.forEach(s=>{
  const sx=lerp(P.gx0,P.gx1,(s.x-P.bx0)/(P.bx1-P.bx0));
  mkEl('line',{x1:sx,y1:P.gy0,x2:sx,y2:P.gy1,stroke:s.c,'stroke-width':'1','stroke-dasharray':'3 3'},graphG);
});

/* 密度曲线 */
const densityPath=mkEl('path',{d:'',fill:'none',stroke:'url(#dgG)','stroke-width':'2','stroke-linecap':'round','stroke-linejoin':'round'},graphG);
/* 密度填充区 */
const densityFill=mkEl('path',{d:'',fill:'url(#dgG)',opacity:'.08'},graphG);

/* -- 重量稳定性指示器 -- */
const weightHist=[];
const WHIST_LEN=80;
const wHistG=mkEl('g',null,svg);
const wHistPath=mkEl('path',{d:'',fill:'none',stroke:'#00ffc8','stroke-width':'1.5','stroke-linecap':'round'},wHistG);
mkEl('text',{x:1320,y:P.gy0-8,fill:'#5a7a9a','font-size':'9','font-family':'Orbitron,monospace','text-anchor':'end','letter-spacing':'1.5'},svg).textContent='WEIGHT STABILITY';

/* 稳定性数值 */
const stabilityVal=mkEl('text',{x:1340,y:P.gy1,fill:'#00ffc8','font-size':'18','font-family':'Orbitron,monospace','text-anchor':'end','font-weight':'700'},svg);
const stabilityUnit=mkEl('text',{x:1340,y:P.gy1+14,fill:'#5a7a9a','font-size':'8','font-family':'Orbitron,monospace','text-anchor':'end','letter-spacing':'1'},svg);
stabilityUnit.textContent='STABILITY INDEX';

/* ===================== 动画循环 ===================== */
function animate(){
  t+=1/60;
  scrollOff+=cfg.beltSpeed;

  /* --- 皮带运动线 --- */
  beltLines.forEach((bl,i)=>{
    let lx=P.bx0+((i*32+scrollOff*1.8)%(P.bx1-P.bx0));
    bl.setAttribute('x1',lx);
    bl.setAttribute('x2',lx+12);
  });

  /* --- 烟丝流形状与颜色 --- */
  streamRects.forEach(sr=>{
    const d=procD(sr.x);
    const h=d2h(d);
    const c=d2color(d);
    sr.el.setAttribute('y',P.beltY-h);
    sr.el.setAttribute('height',h);
    sr.el.setAttribute('fill',c);
  });

  /* --- 烟丝纤维 --- */
  fibers.forEach(f=>{
    f.x+=cfg.beltSpeed;
    if(f.x>P.bx1)f.x=P.bx0;
    const d=procD(f.x);
    const h=d2h(d);
    const fy=P.beltY-h*.3-f.off+Math.sin(t*3+f.phase)*1.5;
    f.el.setAttribute('x1',f.x);
    f.el.setAttribute('y1',fy);
    f.el.setAttribute('x2',f.x+3+Math.sin(f.phase)*2);
    f.el.setAttribute('y2',fy-2+Math.cos(f.phase));
    const c=d2color(d);
    f.el.setAttribute('stroke',c);
  });

  /* --- 预均平辊旋转 --- */
  const rotAngle=t*cfg.rollerRatio*2.5;
  rMarks.forEach(rm=>{
    const a=rm.a+rotAngle;
    const c=P.rollerR*.88;
    rm.el.setAttribute('x1',P.rollerX+Math.cos(a)*18);
    rm.el.setAttribute('y1',P.rollerCY+Math.sin(a)*18);
    rm.el.setAttribute('x2',P.rollerX+Math.cos(a)*c);
    rm.el.setAttribute('y2',P.rollerCY+Math.sin(a)*c);
  });

  /* --- 振动梳振动 --- */
  const combVisFreq=clamp(cfg.combFreq/30,2,10);
  const combVisAmp=clamp(cfg.combAmp*6,1,9);
  const vibY=Math.sin(t*combVisFreq*6.28)*combVisAmp;
  teeth.forEach((tooth,i)=>{
    const phase=i*.15;
    const ty=vibY*Math.sin(t*combVisFreq*6.28+phase);
    tooth.setAttribute('y',combBaseY-62+ty*.3);
    tooth.setAttribute('height',62-ty*.3);
  });
  /* 波纹动画 */
  vibRings.forEach((r,i)=>{
    const scale=1+.15*Math.sin(t*combVisFreq*3+i);
    r.setAttribute('rx',30+i*14*scale);
    r.setAttribute('ry',12+i*6*scale);
    r.setAttribute('opacity',Math.max(0,.12-i*.035+.04*Math.sin(t*4+i)));
  });
  /* 振幅标注 */
  ampLine1.setAttribute('y1',combBaseY-35+vibY);
  ampLine1.setAttribute('y2',combBaseY-35-vibY);
  ampLine2.setAttribute('y1',combBaseY-35+vibY);
  ampLine3.setAttribute('y1',combBaseY-35-vibY);
  ampLine3.setAttribute('y2',combBaseY-35-vibY);

  /* --- 飞溅粒子 --- */
  sprayParts.forEach(sp=>{
    const sx=P.combX+Math.cos(sp.phase+t*3)*sp.radius*.6;
    const sy=combBaseY-30+Math.sin(sp.phase+t*sp.speed*4)*sp.radius*.4;
    sp.el.setAttribute('cx',sx);
    sp.el.setAttribute('cy',sy);
    sp.el.setAttribute('opacity',clamp(.15+cfg.combAmp-.2+Math.sin(t*5+sp.phase)*.15,0,.5));
  });

  /* --- 平准器微光 --- */
  const sparkOp=.3+.3*Math.sin(t*8);
  trimSpark.setAttribute('opacity',sparkOp);
  trimSpark.setAttribute('r',1.5+Math.sin(t*6)*.8);

  /* --- 检测仪 --- */
  const detSampleD=procD(P.detX);
  const weightReading=(detSampleD*.98+.01).toFixed(3);
  detVal.textContent=weightReading;
  /* 指示灯闪烁 */
  const ledOp=clamp(cfg.moisture<.35?.7+.3*Math.sin(t*2):.3+.3*Math.sin(t*8),.1,1);
  detLed.setAttribute('opacity',ledOp);
  detLed.setAttribute('fill',cfg.moisture<.35?'#00ffc8':'#ff8844');

  /* --- 密度分布图 --- */
  let dPath='';let dFill=`M${P.gx0} ${P.gy1}`;
  const graphW=P.gx1-P.gx0;
  for(let i=0;i<=200;i++){
    const frac=i/200;
    const sx=P.bx0+frac*(P.bx1-P.bx0);
    const d=procD(sx);
    const px=lerp(P.gx0,P.gx1,frac);
    const py=lerp(P.gy1,P.gy0,d);
    dPath+=(i===0?'M':'L')+px.toFixed(1)+' '+py.toFixed(1);
    dFill+=' L'+px.toFixed(1)+' '+py.toFixed(1);
  }
  dFill+=` L${P.gx1} ${P.gy1} Z`;
  densityPath.setAttribute('d',dPath);
  densityFill.setAttribute('d',dFill);

  /* --- 重量稳定性历史 --- */
  weightHist.push(detSampleD);
  if(weightHist.length>WHIST_LEN)weightHist.shift();
  let wPath='';
  const wBaseX=P.gx1-WHIST_LEN*2;
  const wMidY=(P.gy0+P.gy1)/2;
  weightHist.forEach((v,i)=>{
    const px=wBaseX+i*2;
    const py=lerp(P.gy1,P.gy0,v);
    wPath+=(i===0?'M':'L')+px.toFixed(1)+' '+py.toFixed(1);
  });
  wHistPath.setAttribute('d',wPath);

  /* 稳定性指数:最近N帧的标准差越小越稳定 */
  if(weightHist.length>10){
    const recent=weightHist.slice(-30);
    const mean=recent.reduce((a,b)=>a+b,0)/recent.length;
    const std=Math.sqrt(recent.reduce((a,b)=>a+(b-mean)**2,0)/recent.length);
    const stability=clamp(1-std*12,0,1);
    stabilityVal.textContent=(stability*100).toFixed(1)+'%';
    stabilityVal.setAttribute('fill',stability>.7?'#00ffc8':stability>.4?'#e8b84a':'#ff5240');
  }

  /* --- 创新区呼吸 --- */
  const breathOp=.015+.008*Math.sin(t*1.5);
  innovFill.setAttribute('fill',`rgba(0,255,200,${breathOp})`);

  requestAnimationFrame(animate);
}

/* ===================== 控件绑定 ===================== */
const sRoller=document.getElementById('sRoller');
const sFreq=document.getElementById('sFreq');
const sAmp=document.getElementById('sAmp');
const sMoist=document.getElementById('sMoist');

sRoller.addEventListener('input',function(){
  cfg.rollerRatio=parseFloat(this.value);
  document.getElementById('vRoller').innerHTML=cfg.rollerRatio.toFixed(2)+'<span class="u">&times;吸丝带</span>';
});
sFreq.addEventListener('input',function(){
  cfg.combFreq=parseInt(this.value);
  document.getElementById('vFreq').innerHTML=cfg.combFreq+'<span class="u">Hz</span>';
});
sAmp.addEventListener('input',function(){
  cfg.combAmp=parseFloat(this.value);
  document.getElementById('vAmp').innerHTML=cfg.combAmp.toFixed(2)+'<span class="u">mm</span>';
});
sMoist.addEventListener('input',function(){
  cfg.moisture=parseInt(this.value)/100;
  document.getElementById('vMoist').innerHTML=parseInt(this.value)+'<span class="u">%</span>';
});

/* 启动动画 */
animate();

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

这段代码实现了一个完整的 IFR 最终理想解原理动画,以下是关键设计与实现说明:

视觉叙事设计

  • 密度色彩映射:烟丝流颜色随密度实时变化——偏高区呈红橙色、正常区呈金琥珀色、偏低区呈浅黄色,让密度波动一目了然
  • 创新区高亮:预均平辊与振动梳之间以呼吸式青绿发光边框标注"CORE INNOVATION ZONE",引导视线聚焦核心创新
  • 五步流程标注:从吸丝带输送到稳定反馈,每一步都有序号与中文说明,形成清晰的机理作用链

动态机理呈现

  • 烟丝流形变:基于多频叠加噪声函数生成自然密度波动,经各站处理后高度逐渐趋于均匀,视觉上直观展现"熨平"过程
  • 预均平辊旋转:转速与滑块联动,内部纹理标记实时旋转,转速比可视
  • 振动梳微幅振动:梳齿以缩放频率振动(实际200Hz缩放到可视范围),配合扩散波纹和飞溅粒子展示流态化打散效果
  • 平准器微光:刃口处仅显示微弱闪烁光点,象征趋近零修削

交互体验

  • 四个滑块分别控制转速比、振动频率、振幅、含水率
  • 含水率超过 35% 时,指示灯变色、稳定性指数骤降,直观展示失效边界
  • 底部实时密度分布曲线 + 重量稳定性指数,量化呈现 IFR 效果
积分规则:第一轮对话扣减6分,后续每轮扣4分