分享图
动画工坊
引擎就绪
<!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 rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Chakra+Petch:wght@300;500;700&family=Noto+Sans+SC:wght@300;400;700&display=swap" rel="stylesheet">
<style>
  :root {
    --bg: #060a12;
    --fg: #c4d3e4;
    --muted: #4e6178;
    --accent: #00e5ff;
    --copper: #c89050;
    --warm: #ff9800;
    --success: #00e676;
    --card: #0b1120;
    --border: #162030;
  }
  *{margin:0;padding:0;box-sizing:border-box}
  body{
    background:var(--bg);color:var(--fg);
    font-family:'Noto Sans SC',sans-serif;
    height:100vh;overflow:hidden;
    display:flex;flex-direction:column;
  }
  header{
    padding:10px 24px;display:flex;align-items:center;gap:20px;
    border-bottom:1px solid var(--border);
    background:rgba(11,17,32,.92);backdrop-filter:blur(10px);
    flex-shrink:0;
  }
  header h1{
    font-family:'Chakra Petch',sans-serif;font-weight:700;
    font-size:1.15rem;letter-spacing:2px;color:var(--accent);
  }
  header .sub{font-size:.75rem;color:var(--muted);font-weight:300}
  .badge{
    font-family:'Chakra Petch',sans-serif;font-size:.65rem;
    padding:2px 10px;border-radius:3px;
    border:1px solid var(--copper);color:var(--copper);
    letter-spacing:1px;
  }
  .main-area{flex:1;position:relative;min-height:0}
  canvas{display:block;width:100%;height:100%}
  .controls{
    padding:10px 24px;
    background:rgba(11,17,32,.95);border-top:1px solid var(--border);
    display:flex;align-items:center;gap:28px;flex-wrap:wrap;flex-shrink:0;
  }
  .cg{display:flex;align-items:center;gap:8px}
  .cg label{
    font-family:'Chakra Petch',sans-serif;font-size:.8rem;
    color:var(--muted);white-space:nowrap;
  }
  .cg input[type=range]{
    -webkit-appearance:none;width:110px;height:3px;
    background:var(--border);border-radius:2px;outline:none;
  }
  .cg input[type=range]::-webkit-slider-thumb{
    -webkit-appearance:none;width:13px;height:13px;
    background:var(--accent);border-radius:50%;cursor:pointer;
    box-shadow:0 0 8px rgba(0,229,255,.5);
  }
  .cg .val{
    font-family:'Chakra Petch',sans-serif;font-size:.85rem;
    color:var(--accent);min-width:26px;text-align:center;
  }
  .tbtn{
    background:var(--border);border:1px solid var(--muted);
    color:var(--muted);padding:3px 12px;border-radius:3px;
    cursor:pointer;font-family:'Chakra Petch',sans-serif;
    font-size:.8rem;transition:all .3s;
  }
  .tbtn.on{
    background:rgba(0,229,255,.12);border-color:var(--accent);
    color:var(--accent);box-shadow:0 0 8px rgba(0,229,255,.15);
  }
  .sbar{display:flex;gap:20px;margin-left:auto}
  .si{display:flex;flex-direction:column;align-items:center;gap:3px}
  .si .lb{font-size:.65rem;color:var(--muted);font-weight:300}
  .si .bar{width:54px;height:3px;background:var(--border);border-radius:2px;overflow:hidden}
  .si .bf{height:100%;border-radius:2px;transition:width .4s ease}
  .si .vt{font-family:'Chakra Petch',sans-serif;font-size:.7rem;color:var(--success)}

  @media(max-width:700px){
    .sbar{display:none}
    .controls{gap:14px;padding:8px 14px}
    header h1{font-size:.95rem}
  }
</style>
</head>
<body>
<header>
  <h1>微流道降噪 · IFR 原理动画</h1>
  <span class="sub">梳齿导流分割 + 微穿孔板吸声 — 双路径协同消声</span>
  <span class="badge">IDEAL FINAL RESULT</span>
</header>
<div class="main-area"><canvas id="cv"></canvas></div>
<div class="controls">
  <div class="cg">
    <label>导流片数</label>
    <input type="range" id="rPlate" min="2" max="8" value="5" step="1">
    <span class="val" id="vPlate">5</span>
  </div>
  <div class="cg">
    <label>气流速度</label>
    <input type="range" id="rSpeed" min="25" max="100" value="65" step="5">
    <span class="val" id="vSpeed">65%</span>
  </div>
  <div class="cg">
    <label>声波可视化</label>
    <button class="tbtn on" id="btnSnd">ON</button>
  </div>
  <div class="sbar">
    <div class="si"><span class="lb">涡流尺度</span><div class="bar"><div class="bf" id="bVtx" style="width:22%;background:var(--success)"></div></div><span class="vt" id="tVtx">极小</span></div>
    <div class="si"><span class="lb">噪声频率</span><div class="bar"><div class="bf" id="bFrq" style="width:72%;background:var(--warm)"></div></div><span class="vt" id="tFrq">高频·易吸收</span></div>
    <div class="si"><span class="lb">吸声效率</span><div class="bar"><div class="bf" id="bAbs" style="width:86%;background:var(--success)"></div></div><span class="vt" id="tAbs">高效</span></div>
  </div>
</div>

<script>
(function(){
'use strict';

/* ========== 配置 ========== */
const CFG={numPlates:5,flowSpeed:.65,showSnd:true,particleBase:260};

/* ========== 全局状态 ========== */
let cv,cx,W,H,G={},particles=[],sndWaves=[],heatGlows=[],frame=0,sndTimer=0;

/* ========== 几何计算 ========== */
function calcGeom(){
  G.dL=W*.06; G.dR=W*.94;
  G.dT=H*.16; G.dB=H*.84;
  G.dW=G.dR-G.dL; G.dH=G.dB-G.dT;
  G.wT=Math.max(10,G.dH*.065);
  G.cD=Math.max(7,G.dH*.04);
  G.fT=G.dT+G.wT; G.fB=G.dB-G.wT; G.fH=G.fB-G.fT;
  G.pS=G.dL+G.dW*.26; G.pE=G.dL+G.dW*.60; G.pL=G.pE-G.pS;
  updCh();
}

function updCh(){
  const n=CFG.numPlates,cnt=n+1,ch=G.fH/cnt;
  G.chs=[];for(let i=0;i<cnt;i++)G.chs.push({t:G.fT+i*ch,b:G.fT+(i+1)*ch,cy:G.fT+(i+.5)*ch,h:ch});
  G.pls=[];for(let i=1;i<=n;i++)G.pls.push({y:G.fT+i*ch});
}

/* ========== Canvas 尺寸 ========== */
function resize(){
  const r=cv.parentElement.getBoundingClientRect(),dpr=window.devicePixelRatio||1;
  cv.width=r.width*dpr;cv.height=r.height*dpr;
  cv.style.width=r.width+'px';cv.style.height=r.height+'px';
  cx.setTransform(dpr,0,0,dpr,0,0);
  W=r.width;H=r.height;
  calcGeom();initParts();
}

/* ========== 粒子系统 ========== */
function mkPart(rx){
  const ci=Math.floor(Math.random()*G.chs.length),c=G.chs[ci];
  return{x:rx?G.dL+Math.random()*G.dW:G.dL-Math.random()*30,
    y:c.cy+(Math.random()-.5)*c.h*.7,ch:ci,
    sp:.7+Math.random()*.5,sz:1+Math.random()*1.6,
    al:.45+Math.random()*.55,ph:Math.random()*Math.PI*2,
    ta:.3+Math.random()*.5};
}

function initParts(){
  const cnt=Math.round(CFG.particleBase*(W/1200));
  particles=[];for(let i=0;i<cnt;i++)particles.push(mkPart(true));
}

function updParts(){
  const bs=CFG.flowSpeed*G.dW*.0028;
  for(let p of particles){
    p.x+=bs*p.sp;
    const c=G.chs[p.ch],ty=c.cy;
    if(p.x<G.pS){
      /* 入口:湍流 */
      const tb=p.ta*c.h*.38;
      p.y=ty+Math.sin(frame*.028+p.ph)*tb+Math.sin(frame*.065+p.ph*1.7)*tb*.45;
    }else if(p.x<G.pE){
      /* 导流区:逐渐平滑 */
      const pr=(p.x-G.pS)/G.pL,sm=Math.min(1,pr*1.3);
      const tb=p.ta*c.h*.38*(1-sm*.88);
      p.y=ty+Math.sin(frame*.028+p.ph)*tb;
    }else{
      /* 出口:层流 */
      const tb=p.ta*c.h*.04;
      p.y=ty+Math.sin(frame*.018+p.ph)*tb;
    }
    p.y=Math.max(c.t+2,Math.min(c.b-2,p.y));
    if(p.x>G.dR+10){p.x=G.dL-Math.random()*20;p.ch=Math.floor(Math.random()*G.chs.length);p.ph=Math.random()*Math.PI*2;p.ta=.3+Math.random()*.5;}
  }
}

/* ========== 声波系统 ========== */
function emitSnd(){
  const x=G.pS+Math.random()*G.pL;
  const ci=Math.floor(Math.random()*G.chs.length),c=G.chs[ci];
  sndWaves.push({x,y:c.cy,r:0,mr:c.h*1.4,al:.55,sp:.7+Math.random()*.4,ch:ci});
}

function updSnd(){
  if(!CFG.showSnd)return;
  sndTimer++;
  const interval=Math.max(8,28/CFG.flowSpeed);
  if(sndTimer>interval){emitSnd();sndTimer=0;}
  for(let i=sndWaves.length-1;i>=0;i--){
    const s=sndWaves[i];s.r+=s.sp;s.al-=.006;
    /* 声波到达壁面 → 产生吸声发光 */
    const c=G.chs[s.ch];
    if(s.r>c.h*.42&&s.al>.1){
      if(Math.random()<.15){
        heatGlows.push({x:s.x+(Math.random()-.5)*6,wall:s.y<c.cy?'top':'bot',al:s.al*.7,decay:.015+Math.random()*.01,r:4+Math.random()*4,ch:s.ch});
      }
    }
    if(s.al<=0||s.r>s.mr)sndWaves.splice(i,1);
  }
  /* 热辉光衰减 */
  for(let i=heatGlows.length-1;i>=0;i--){
    heatGlows[i].al-=heatGlows[i].decay;
    if(heatGlows[i].al<=0)heatGlows.splice(i,1);
  }
}

/* ========== 绘制 ========== */
function drawBg(){
  const g=cx.createRadialGradient(W/2,H/2,0,W/2,H/2,W*.7);
  g.addColorStop(0,'#0c1222');g.addColorStop(1,'#050810');
  cx.fillStyle=g;cx.fillRect(0,0,W,H);
  /* 网格 */
  cx.strokeStyle='rgba(22,38,58,.35)';cx.lineWidth=.5;
  const gs=Math.max(20,Math.round(W/50));
  for(let x=gs;x<W;x+=gs){cx.beginPath();cx.moveTo(x,0);cx.lineTo(x,H);cx.stroke();}
  for(let y=gs;y<H;y+=gs){cx.beginPath();cx.moveTo(0,y);cx.lineTo(W,y);cx.stroke();}
}

function drawWalls(){
  /* 外壁面 */
  const gT=cx.createLinearGradient(0,G.dT,0,G.dT+G.wT);
  gT.addColorStop(0,'#2a3850');gT.addColorStop(1,'#182030');
  cx.fillStyle=gT;cx.fillRect(G.dL,G.dT,G.dW,G.wT);
  const gB=cx.createLinearGradient(0,G.dB-G.wT,0,G.dB);
  gB.addColorStop(0,'#182030');gB.addColorStop(1,'#2a3850');
  cx.fillStyle=gB;cx.fillRect(G.dL,G.dB-G.wT,G.dW,G.wT);

  /* 背腔 */
  const cW=G.wT*.55,cG=G.wT*.45;
  cx.fillStyle='#080e18';
  for(let x=G.pS;x<G.pE;x+=cW+cG){
    cx.fillRect(x,G.dT+G.wT*.25,cW,G.cD);
    cx.fillRect(x,G.dB-G.wT*.25-G.cD,cW,G.cD);
  }

  /* 微穿孔点 */
  cx.fillStyle='rgba(90,115,145,.55)';
  const ds=Math.max(3,W/280),dr=.8;
  const tY=G.fT-1.5,bY=G.fB+1.5;
  for(let x=G.pS;x<G.pE;x+=ds){
    cx.beginPath();cx.arc(x,tY,dr,0,Math.PI*2);cx.fill();
    cx.beginPath();cx.arc(x,bY,dr,0,Math.PI*2);cx.fill();
  }

  /* 壁面边线 */
  cx.strokeStyle='#3a4e68';cx.lineWidth=1;
  cx.strokeRect(G.dL,G.dT,G.dW,G.wT);
  cx.strokeRect(G.dL,G.dB-G.wT,G.dW,G.wT);
  /* 内壁线高亮 */
  cx.strokeStyle='rgba(90,130,170,.25)';cx.lineWidth=.5;
  cx.beginPath();cx.moveTo(G.dL,G.fT);cx.lineTo(G.dR,G.fT);cx.stroke();
  cx.beginPath();cx.moveTo(G.dL,G.fB);cx.lineTo(G.dR,G.fB);cx.stroke();
}

function drawPlates(){
  const pt=Math.max(2.5,G.chs[0].h*.055);
  const lr=pt*2.2;
  for(let pl of G.pls){
    const y=pl.y;
    cx.save();
    const gp=.25+Math.sin(frame*.018)*.12;
    cx.shadowColor='rgba(200,144,80,'+gp+')';cx.shadowBlur=14;
    const gr=cx.createLinearGradient(0,y-pt,0,y+pt);
    gr.addColorStop(0,'#9a6830');gr.addColorStop(.5,'#d4a060');gr.addColorStop(1,'#8a5828');
    cx.fillStyle=gr;cx.beginPath();
    cx.moveTo(G.pS,y);
    cx.quadraticCurveTo(G.pS+lr,y-pt,G.pS+lr*2,y-pt);
    cx.lineTo(G.pE-3,y-pt*.28);
    cx.lineTo(G.pE+1,y);
    cx.lineTo(G.pE-3,y+pt*.28);
    cx.lineTo(G.pS+lr*2,y+pt);
    cx.quadraticCurveTo(G.pS+lr,y+pt,G.pS,y);
    cx.closePath();cx.fill();
    /* 高光线 */
    cx.strokeStyle='rgba(255,220,160,.25)';cx.lineWidth=.5;
    cx.beginPath();cx.moveTo(G.pS+lr*2,y-pt*.5);cx.lineTo(G.pE-6,y-pt*.15);cx.stroke();
    cx.restore();
  }
}

function drawParts(){
  cx.save();
  for(let p of particles){
    const pr=(p.x-G.dL)/G.dW;
    /* 入口偏暖 → 出口纯青 */
    let r=0,g,b=255;
    if(pr<.28){g=180+pr*200;b=255;}
    else{g=229;b=255;}
    const col='rgba('+Math.round(r)+','+Math.round(g)+','+b+','+p.al+')';
    /* 光晕 */
    cx.shadowColor=col;cx.shadowBlur=3;
    cx.fillStyle=col;
    cx.beginPath();cx.arc(p.x,p.y,p.sz,0,Math.PI*2);cx.fill();
    /* 尾迹短线 */
    if(p.x>G.dL+5){
      cx.strokeStyle='rgba('+Math.round(r)+','+Math.round(g)+','+b+','+(p.al*.3)+')';
      cx.lineWidth=p.sz*.6;
      const vxBased=CFG.flowSpeed*4;
      cx.beginPath();cx.moveTo(p.x,p.y);cx.lineTo(p.x-vxBased*p.sp,p.y);cx.stroke();
    }
  }
  cx.shadowBlur=0;cx.restore();
}

function drawSnd(){
  if(!CFG.showSnd)return;
  cx.save();
  for(let s of sndWaves){
    cx.strokeStyle='rgba(255,152,0,'+s.al*.6+')';
    cx.lineWidth=1.2;
    cx.beginPath();cx.arc(s.x,s.y,s.r,0,Math.PI*2);cx.stroke();
    /* 内圈更亮 */
    if(s.r>4){
      cx.strokeStyle='rgba(255,200,50,'+s.al*.3+')';
      cx.lineWidth=.6;
      cx.beginPath();cx.arc(s.x,s.y,s.r*.6,0,Math.PI*2);cx.stroke();
    }
  }
  /* 壁面吸声热辉光 */
  for(let h of heatGlows){
    const c=G.chs[h.ch];
    const wy=h.wall==='top'?c.t:c.b;
    const gr=cx.createRadialGradient(h.x,wy,0,h.x,wy,h.r*2);
    gr.addColorStop(0,'rgba(255,193,7,'+h.al*.5+')');
    gr.addColorStop(.5,'rgba(255,120,0,'+h.al*.2+')');
    gr.addColorStop(1,'rgba(255,80,0,0)');
    cx.fillStyle=gr;cx.beginPath();cx.arc(h.x,wy,h.r*2,0,Math.PI*2);cx.fill();
  }
  cx.restore();
}

function drawVortex(){
  for(let pl of G.pls){
    const x=G.pE+4,y=pl.y,sr=Math.max(4,G.chs[0].h*.08),rot=frame*.06;
    cx.save();cx.strokeStyle='rgba(0,230,118,.45)';cx.lineWidth=.8;
    cx.beginPath();
    for(let t=0;t<Math.PI*3.5;t+=.15){
      const rr=sr*(1-t/(Math.PI*3.5));
      const px=x+Math.cos(t+rot)*rr,py=y+Math.sin(t+rot)*rr;
      t===0?cx.moveTo(px,py):cx.lineTo(px,py);
    }
    cx.stroke();cx.restore();
  }
}

function drawArrows(){
  const my=(G.fT+G.fB)/2;
  /* 入口箭头 */
  cx.fillStyle='rgba(0,229,255,.5)';
  const ax=G.dL+12;
  cx.beginPath();cx.moveTo(ax,my-9);cx.lineTo(ax+16,my);cx.lineTo(ax,my+9);cx.closePath();cx.fill();
  /* 多条小箭头 */
  for(let c of G.chs){
    const ay=c.cy;
    cx.fillStyle='rgba(0,229,255,.2)';
    cx.beginPath();cx.moveTo(ax+22,ay-4);cx.lineTo(ax+30,ay);cx.lineTo(ax+22,ay+4);cx.closePath();cx.fill();
  }
  /* 出口箭头 */
  cx.fillStyle='rgba(0,230,118,.5)';
  const ox=G.dR-12;
  cx.beginPath();cx.moveTo(ox-16,my-9);cx.lineTo(ox,my);cx.lineTo(ox-16,my+9);cx.closePath();cx.fill();
  for(let c of G.chs){
    const ay=c.cy;
    cx.fillStyle='rgba(0,230,118,.2)';
    cx.beginPath();cx.moveTo(ox-30,ay-4);cx.lineTo(ox-22,ay);cx.lineTo(ox-30,ay+4);cx.closePath();cx.fill();
  }
}

function drawAnno(){
  cx.save();
  const fs1=Math.max(10,Math.min(13,W/100));
  const fs2=Math.max(9,Math.min(11,W/120));
  const cx1=(G.pS+G.pE)/2;

  /* 导流梳齿片 */
  cx.font='500 '+fs1+'px "Chakra Petch",sans-serif';cx.textAlign='center';
  cx.fillStyle='rgba(200,144,80,.85)';cx.fillText('导流梳齿片',cx1,G.dT-18);
  cx.strokeStyle='rgba(200,144,80,.35)';cx.lineWidth=.8;
  cx.beginPath();cx.moveTo(cx1,G.dT-13);cx.lineTo(cx1,G.dT+2);cx.stroke();

  /* 微穿孔板+背腔 */
  cx.fillStyle='rgba(100,125,155,.8)';cx.fillText('微穿孔板 + 背腔',cx1,G.dB+G.wT+18);
  cx.strokeStyle='rgba(100,125,155,.3)';
  cx.beginPath();cx.moveTo(cx1,G.dB+G.wT+3);cx.lineTo(cx1,G.dB+G.wT+12);cx.stroke();

  /* 边界层切割 */
  cx.font='300 '+fs2+'px "Noto Sans SC",sans-serif';
  cx.fillStyle='rgba(0,229,255,.6)';cx.fillText('边界层切割',G.pS-5,G.dB+G.wT+34);

  /* 声能→热能 */
  if(CFG.showSnd){
    cx.fillStyle='rgba(255,152,0,.65)';
    cx.fillText('声能 → 热能',cx1+60,G.dT-6);
    /* 小箭头指向壁面 */
    cx.strokeStyle='rgba(255,152,0,.3)';cx.lineWidth=.6;
    cx.beginPath();cx.moveTo(cx1+60,G.dT-2);cx.lineTo(cx1+60,G.fT+3);cx.stroke();
  }

  /* 均匀层流 */
  const ox2=G.pE+(G.dR-G.pE)/2;
  cx.fillStyle='rgba(0,230,118,.6)';cx.fillText('均匀层流输出',ox2,G.dB+G.wT+18);

  /* 入口/出口标签 */
  cx.font='700 '+fs1+'px "Chakra Petch",sans-serif';
  cx.fillStyle='rgba(0,229,255,.45)';cx.textAlign='left';
  cx.fillText('INLET',G.dL+4,G.dT-6);
  cx.fillStyle='rgba(0,230,118,.45)';cx.textAlign='right';
  cx.fillText('OUTLET',G.dR-4,G.dT-6);

  /* 参数标注 */
  cx.font='300 '+(fs2-1)+'px "Chakra Petch",sans-serif';cx.textAlign='left';
  cx.fillStyle='rgba(200,144,80,.4)';
  const spacing=(5+3*(CFG.numPlates-2)).toFixed(0);
  cx.fillText('间距 ≈ '+spacing+'mm',G.pE+10,G.pls.length>0?G.pls[0].y-6:G.fT+14);
  cx.fillText('孔径 0.5mm · 穿孔率 1~2%',G.pS,G.dB+G.wT+48);

  cx.restore();
}

/* 流动色彩渐变叠加 */
function drawFlowOverlay(){
  cx.save();
  /* 入口区微红(湍流暗示) */
  const ig=cx.createLinearGradient(G.dL,0,G.pS,0);
  ig.addColorStop(0,'rgba(255,80,40,.06)');ig.addColorStop(1,'rgba(255,80,40,0)');
  cx.fillStyle=ig;cx.fillRect(G.dL,G.fT,G.pS-G.dL,G.fH);
  /* 出口区微绿(层流暗示) */
  const og=cx.createLinearGradient(G.pE,0,G.dR,0);
  og.addColorStop(0,'rgba(0,230,118,0)');og.addColorStop(1,'rgba(0,230,118,.04)');
  cx.fillStyle=og;cx.fillRect(G.pE,G.fT,G.dR-G.pE,G.fH);
  cx.restore();
}

/* IFR 说明浮层 */
function drawIFRLabel(){
  cx.save();
  const fs=Math.max(9,Math.min(11,W/110));
  cx.font='300 '+fs+'px "Noto Sans SC",sans-serif';cx.textAlign='right';
  cx.fillStyle='rgba(0,230,118,.35)';
  cx.fillText('最终理想解:以极低复杂度同时破除流场与声场矛盾',W-16,H-8);
  cx.restore();
}

/* ========== 状态面板 ========== */
function updStatus(){
  const n=CFG.numPlates;
  const vl=Math.max(8,100-n*13),fl=Math.min(95,38+n*9),al=Math.min(95,55+n*6);
  document.getElementById('bVtx').style.width=vl+'%';
  document.getElementById('bFrq').style.width=fl+'%';
  document.getElementById('bAbs').style.width=al+'%';
  document.getElementById('bVtx').style.background=vl<35?'var(--success)':'var(--warm)';
  document.getElementById('bFrq').style.background=fl>60?'var(--warm)':'var(--muted)';
  document.getElementById('bAbs').style.background=al>70?'var(--success)':'var(--warm)';
  document.getElementById('tVtx').textContent=vl<25?'极小':vl<45?'小尺度':'中等';
  document.getElementById('tFrq').textContent=fl>65?'高频·易吸收':'中高频';
  document.getElementById('tAbs').textContent=al>80?'高效':al>60?'中效':'低效';
}

/* ========== 主循环 ========== */
function loop(){
  cx.clearRect(0,0,W,H);
  drawBg();
  drawFlowOverlay();
  drawWalls();
  drawPlates();
  updParts();drawParts();
  updSnd();drawSnd();
  drawVortex();
  drawArrows();
  drawAnno();
  drawIFRLabel();
  frame++;
  requestAnimationFrame(loop);
}

/* ========== 初始化 ========== */
function init(){
  cv=document.getElementById('cv');
  cx=cv.getContext('2d');
  resize();updStatus();loop();

  window.addEventListener('resize',()=>{resize();});

  document.getElementById('rPlate').addEventListener('input',function(){
    CFG.numPlates=+this.value;
    document.getElementById('vPlate').textContent=this.value;
    updCh();initParts();updStatus();
  });
  document.getElementById('rSpeed').addEventListener('input',function(){
    CFG.flowSpeed=+this.value/100;
    document.getElementById('vSpeed').textContent=this.value+'%';
  });
  document.getElementById('btnSnd').addEventListener('click',function(){
    CFG.showSnd=!CFG.showSnd;
    this.classList.toggle('on');this.textContent=CFG.showSnd?'ON':'OFF';
    if(!CFG.showSnd){sndWaves=[];heatGlows=[];}
  });
}

if(document.readyState==='loading')document.addEventListener('DOMContentLoaded',init);
else init();

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

实现说明

本动画聚焦于 IFR(最终理想解) 状态的直接呈现,不做前后对比,而是让观众看到系统"已经在理想状态下运行":

  1. 双路径协同消声的可视化

    • 流场路径:入口区粒子带有明显湍流振荡(偏暖色调暗示不稳定),经过导流梳齿片后被强制分层,出口区变为高度有序的层流(纯青色),直观体现"边界层切割→大涡碎小涡"的核心机理。
    • 声场路径:琥珀色声波圆环从流场中辐射,到达微穿孔壁面时被"吞噬"——壁面处产生暖黄热辉光(声能→热能),清晰展示微穿孔板吸声过程。
  2. 资源利用的视觉暗示

    • 导流片以铜金色高亮并带脉冲光晕,强调其"极简插入即可破除矛盾"的 IFR 特性。
    • 微穿孔壁面上的细密点阵和背后背腔同时可见,展示"在现有壁面资源上做微结构改造"的思路。
  3. 交互控制

    • 导流片数量滑块(2-8片):实时改变流道分割数,底部状态条同步更新涡流尺度/噪声频率/吸声效率。
    • 气流速度滑块:调节粒子流速和声波发射频率。
    • 声波可视化开关:可关闭声波层以专注观察流场。
  4. 自动播放:页面加载后动画立即启动,无需任何手动触发;重新加载同样自动开始。

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