分享图
动画工坊
引擎就绪
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>磁流变液智能伞骨 — IFR 原理动画</title>
<link href="https://fonts.googleapis.com/css2?family=Rajdhani:wght@300;500;700&family=Noto+Sans+SC:wght@300;400;600&display=swap" rel="stylesheet">
<style>
  :root {
    --bg: #060b18;
    --fg: #d8e2ec;
    --accent: #00d4ff;
    --fluid-liq: #00b4d8;
    --fluid-sol: #ff8c00;
    --bead: #ff2d55;
    --coil: #7b68ee;
    --muted: #5a6a80;
    --card: rgba(255,255,255,0.03);
    --border: rgba(255,255,255,0.06);
  }
  *{margin:0;padding:0;box-sizing:border-box;}
  body{
    background:var(--bg);
    color:var(--fg);
    font-family:'Noto Sans SC',sans-serif;
    min-height:100vh;
    display:flex;flex-direction:column;
    align-items:center;justify-content:center;
    overflow:hidden;
    background-image:
      radial-gradient(ellipse 80% 60% at 30% 40%, rgba(0,212,255,0.04) 0%, transparent 70%),
      radial-gradient(ellipse 60% 50% at 75% 65%, rgba(255,140,0,0.03) 0%, transparent 70%);
  }
  .wrapper{width:100%;max-width:1440px;padding:0.5rem 1rem;display:flex;flex-direction:column;align-items:center;}
  .top-bar{text-align:center;margin-bottom:0.3rem;}
  .top-bar h1{
    font-family:'Rajdhani',sans-serif;font-weight:700;font-size:1.6rem;
    letter-spacing:0.12em;color:var(--accent);
    text-shadow:0 0 20px rgba(0,212,255,0.3);
  }
  .top-bar .sub{font-size:0.78rem;color:var(--muted);letter-spacing:0.05em;}
  .svg-container{width:100%;display:flex;justify-content:center;}
  .svg-container svg{width:100%;max-width:1440px;height:auto;}
  .controls{
    display:flex;align-items:center;gap:1rem;margin-top:0.5rem;
    padding:0.6rem 1.5rem;background:var(--card);
    border:1px solid var(--border);border-radius:10px;flex-wrap:wrap;justify-content:center;
  }
  .controls label{font-size:0.8rem;color:var(--muted);white-space:nowrap;}
  .controls input[type=range]{
    width:180px;accent-color:var(--accent);cursor:pointer;
  }
  .ctrl-btn{
    background:transparent;border:1px solid var(--border);color:var(--accent);
    padding:0.3rem 0.9rem;border-radius:6px;cursor:pointer;
    font-family:'Rajdhani',sans-serif;font-size:0.85rem;font-weight:600;
    letter-spacing:0.06em;transition:all 0.2s;
  }
  .ctrl-btn:hover{background:rgba(0,212,255,0.1);border-color:var(--accent);}
  .ctrl-btn.active{background:rgba(0,212,255,0.15);border-color:var(--accent);}
  .state-label{
    font-family:'Rajdhani',sans-serif;font-weight:600;font-size:0.95rem;
    min-width:110px;text-align:center;transition:color 0.3s;
  }
  @media(max-width:640px){
    .top-bar h1{font-size:1.1rem;}
    .controls{gap:0.5rem;padding:0.4rem 0.8rem;}
    .controls input[type=range]{width:120px;}
  }
</style>
</head>
<body>
<div class="wrapper">
  <header class="top-bar">
    <h1>MAGNETORHEOLOGICAL UMBRELLA RIB</h1>
    <p class="sub">磁流变液智能伞骨 — TRIZ 最终理想解 (IFR) 原理演示</p>
  </header>
  <div class="svg-container">
    <svg id="scene" viewBox="0 0 1400 860" xmlns="http://www.w3.org/2000/svg">
      <defs>
        <!-- 辉光滤镜 -->
        <filter id="glow-cyan" x="-50%" y="-50%" width="200%" height="200%">
          <feGaussianBlur in="SourceGraphic" stdDeviation="5" result="b"/>
          <feFlood flood-color="#00d4ff" flood-opacity="0.55"/><feComposite in2="b" operator="in"/>
          <feMerge><feMergeNode/><feMergeNode in="SourceGraphic"/></feMerge>
        </filter>
        <filter id="glow-amber" x="-50%" y="-50%" width="200%" height="200%">
          <feGaussianBlur in="SourceGraphic" stdDeviation="5" result="b"/>
          <feFlood flood-color="#ff8c00" flood-opacity="0.55"/><feComposite in2="b" operator="in"/>
          <feMerge><feMergeNode/><feMergeNode in="SourceGraphic"/></feMerge>
        </filter>
        <filter id="glow-red" x="-50%" y="-50%" width="200%" height="200%">
          <feGaussianBlur in="SourceGraphic" stdDeviation="3.5" result="b"/>
          <feFlood flood-color="#ff2d55" flood-opacity="0.5"/><feComposite in2="b" operator="in"/>
          <feMerge><feMergeNode/><feMergeNode in="SourceGraphic"/></feMerge>
        </filter>
        <filter id="glow-coil" x="-50%" y="-50%" width="200%" height="200%">
          <feGaussianBlur in="SourceGraphic" stdDeviation="6" result="b"/>
          <feFlood flood-color="#7b68ee" flood-opacity="0.6"/><feComposite in2="b" operator="in"/>
          <feMerge><feMergeNode/><feMergeNode in="SourceGraphic"/></feMerge>
        </filter>
        <filter id="soft-shadow" x="-20%" y="-20%" width="140%" height="140%">
          <feGaussianBlur in="SourceAlpha" stdDeviation="4" result="b"/>
          <feOffset dx="0" dy="2"/><feFlood flood-color="#000" flood-opacity="0.4"/>
          <feComposite in2="b" operator="in"/><feMerge><feMergeNode/><feMergeNode in="SourceGraphic"/></feMerge>
        </filter>
        <!-- 网格图案 -->
        <pattern id="grid" width="40" height="40" patternUnits="userSpaceOnUse">
          <path d="M 40 0 L 0 0 0 40" fill="none" stroke="rgba(255,255,255,0.025)" stroke-width="0.5"/>
        </pattern>
        <!-- 磁流变液渐变 -->
        <linearGradient id="fluid-grad" x1="0" y1="0" x2="1" y2="0">
          <stop offset="0%" stop-color="#00b4d8" stop-opacity="0.65"/>
          <stop offset="100%" stop-color="#00b4d8" stop-opacity="0.65"/>
        </linearGradient>
      </defs>
      <!-- 背景 -->
      <rect width="1400" height="860" fill="#060b18"/>
      <rect width="1400" height="860" fill="url(#grid)"/>
      <!-- 动态图层 (JS 填充) -->
      <g id="field-lines-g"></g>
      <g id="coil-g"></g>
      <g id="rib-g"></g>
      <g id="beads-g"></g>
      <g id="umbrella-g" transform="translate(70,100)"></g>
      <g id="annot-g"></g>
      <g id="state-g"></g>
    </svg>
  </div>
  <div class="controls">
    <label>电磁场强度</label>
    <input type="range" id="fieldSlider" min="0" max="100" value="0">
    <span id="fieldVal" style="font-family:Rajdhani;font-weight:600;min-width:40px;">0%</span>
    <button id="autoBtn" class="ctrl-btn active">AUTO</button>
    <span id="stateLabel" class="state-label" style="color:#00b4d8;">柔性卷曲</span>
  </div>
</div>

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

/* ============ 配置 ============ */
const RIB_PTS    = 60;
const RIB_SX     = 310, RIB_SY = 400;
const RIB_EX     = 1300, RIB_EY = 310;
const RIB_THICK  = 38;
const N_BEADS    = 14;
const BEAD_R     = 5.5;
const LOOP_SEC   = 15;  // 整个循环秒数

/* ============ 状态 ============ */
let fs = 0;            // 场强 0-1
let autoPlay = true;
let t0 = null;
let manualFs = 0;

/* ============ DOM ============ */
const svgNS = 'http://www.w3.org/2000/svg';
const slider  = document.getElementById('fieldSlider');
const fVal    = document.getElementById('fieldVal');
const autoBtn = document.getElementById('autoBtn');
const stLabel = document.getElementById('stateLabel');

const gField = document.getElementById('field-lines-g');
const gCoil  = document.getElementById('coil-g');
const gRib   = document.getElementById('rib-g');
const gBeads = document.getElementById('beads-g');
const gUmb   = document.getElementById('umbrella-g');
const gAnnot = document.getElementById('annot-g');
const gState = document.getElementById('state-g');

/* ============ 工具 ============ */
function lerp(a,b,t){return a+(b-a)*t;}
function easeIO(t){return t<.5?4*t*t*t:1-Math.pow(-2*t+2,3)/2;}
function clamp01(v){return Math.max(0,Math.min(1,v));}
function lerpColor(c1,c2,t){
  // c1,c2 = [r,g,b]
  return `rgb(${Math.round(lerp(c1[0],c2[0],t))},${Math.round(lerp(c1[1],c2[1],t))},${Math.round(lerp(c1[2],c2[2],t))})`;
}
const COL_LIQ = [0,180,216];
const COL_SOL = [255,140,0];

function svgEl(tag,attrs){
  const el = document.createElementNS(svgNS,tag);
  for(const k in attrs) el.setAttribute(k,attrs[k]);
  return el;
}

/* ============ 几何:伞骨中心线 ============ */
function centerline(fieldStr){
  const pts=[];
  for(let i=0;i<=RIB_PTS;i++){
    const t=i/RIB_PTS;
    // 刚化:微上扬直线
    const rx = lerp(RIB_SX, RIB_EX, t);
    const ry = lerp(RIB_SY, RIB_EY, t);
    // 柔性:下垂+波动
    const comp = 0.58;
    const fx = lerp(RIB_SX, RIB_EX, t)*comp + RIB_SX*(1-comp);
    const droop = t*t*200;
    const wave  = Math.sin(t*Math.PI*2.8)*35*t;
    const fy = RIB_SY + droop + wave;
    const ff = 1-fieldStr;
    pts.push({ x: lerp(rx,fx,ff), y: lerp(ry,fy,ff) });
  }
  return pts;
}

/* ============ 几何:管道路径 ============ */
function tubePath(cl, thick){
  const ht=thick/2, top=[], bot=[];
  for(let i=0;i<cl.length;i++){
    let dx,dy;
    if(i===0){dx=cl[1].x-cl[0].x;dy=cl[1].y-cl[0].y;}
    else if(i===cl.length-1){dx=cl[i].x-cl[i-1].x;dy=cl[i].y-cl[i-1].y;}
    else{dx=cl[i+1].x-cl[i-1].x;dy=cl[i+1].y-cl[i-1].y;}
    const len=Math.sqrt(dx*dx+dy*dy)||1;
    const nx=-dy/len, ny=dx/len;
    top.push({x:cl[i].x+nx*ht, y:cl[i].y+ny*ht});
    bot.push({x:cl[i].x-nx*ht, y:cl[i].y-ny*ht});
  }
  let d=`M${top[0].x.toFixed(1)},${top[0].y.toFixed(1)}`;
  for(let i=1;i<top.length;i++) d+=` L${top[i].x.toFixed(1)},${top[i].y.toFixed(1)}`;
  d+=` A${ht},${ht} 0 0 1 ${bot[bot.length-1].x.toFixed(1)},${bot[bot.length-1].y.toFixed(1)}`;
  for(let i=bot.length-2;i>=0;i--) d+=` L${bot[i].x.toFixed(1)},${bot[i].y.toFixed(1)}`;
  d+=` A${ht},${ht} 0 0 1 ${top[0].x.toFixed(1)},${top[0].y.toFixed(1)} Z`;
  return d;
}

/* ============ 磁珠位置 ============ */
function beadPos(cl, fieldStr){
  const beads=[];
  for(let i=0;i<N_BEADS;i++){
    const t=(i+0.5)/N_BEADS;
    const idx=Math.min(Math.floor(t*(cl.length-1)),cl.length-2);
    const frac=t*(cl.length-1)-idx;
    const cx=lerp(cl[idx].x,cl[idx+1].x,frac);
    const cy=lerp(cl[idx].y,cl[idx+1].y,frac);
    const seed=i*137.508;
    const scatter=14*(1-fieldStr);
    const ox=Math.cos(seed)*scatter;
    const oy=Math.sin(seed)*scatter;
    beads.push({x:cx+ox, y:cy+oy});
  }
  return beads;
}

/* ============ 创建静态元素 ============ */
// 管道填充 (MR流体)
const ribFluid = svgEl('path',{id:'ribFluid',fill:'url(#fluid-grad)',stroke:'none'});
gRib.appendChild(ribFluid);
// 管道壁
const ribWall = svgEl('path',{id:'ribWall',fill:'none',stroke:'rgba(180,200,220,0.35)',
  'stroke-width':'2.5','stroke-linejoin':'round'});
gRib.appendChild(ribWall);
// 管道高光
const ribHighlight = svgEl('path',{id:'ribHL',fill:'none',stroke:'rgba(255,255,255,0.08)',
  'stroke-width':'8','stroke-linecap':'round'});
gRib.appendChild(ribHighlight);

// 磁珠
const beadEls=[];
for(let i=0;i<N_BEADS;i++){
  const g=svgEl('g',{});
  // 磁珠辉光底层
  const glow=svgEl('circle',{r:BEAD_R+3,fill:'none',stroke:'#ff2d55','stroke-width':'1',opacity:'0'});
  g.appendChild(glow);
  // 磁珠本体
  const c=svgEl('circle',{r:BEAD_R,fill:'#ff2d55',stroke:'#cc1144','stroke-width':'0.8'});
  g.appendChild(c);
  // 高光点
  const hl=svgEl('circle',{r:1.8,fill:'rgba(255,255,255,0.7)'});
  g.appendChild(hl);
  gBeads.appendChild(g);
  beadEls.push({g,glow,c,hl});
}

// 电磁线圈
function buildCoil(){
  gCoil.innerHTML='';
  const cx=270, cy=RIB_SY, w=55, h=80, loops=8;
  // 线圈外壳
  const housing=svgEl('rect',{x:cx-w/2,y:cy-h/2,width:w,height:h,rx:6,
    fill:'rgba(40,30,80,0.6)',stroke:'#7b68ee','stroke-width':'1.5'});
  gCoil.appendChild(housing);
  // 线圈绕组
  for(let i=0;i<loops;i++){
    const yy=cy-h/2+8+i*(h-16)/(loops-1);
    const line=svgEl('line',{x1:cx-w/2+4,y1:yy,x2:cx+w/2-4,y2:yy,
      stroke:'#9b88ff','stroke-width':'1.2',opacity:'0.6'});
    gCoil.appendChild(line);
  }
  // 线圈辉光
  const coilGlow=svgEl('rect',{id:'coilGlow',x:cx-w/2-4,y:cy-h/2-4,width:w+8,height:h+8,rx:10,
    fill:'none',stroke:'#7b68ee','stroke-width':'2',opacity:'0',filter:'url(#glow-coil)'});
  gCoil.appendChild(coilGlow);
  // 连接线 (线圈到伞骨)
  const conn=svgEl('line',{x1:cx+w/2,y1:cy,x2:RIB_SX,y2:RIB_SY,
    stroke:'#7b68ee','stroke-width':'1.5','stroke-dasharray':'4,3',opacity:'0.4'});
  gCoil.appendChild(conn);
}
buildCoil();

// 磁场线
const fieldLineEls=[];
const N_FLINES=7;
for(let i=0;i<N_FLINES;i++){
  const p=svgEl('path',{fill:'none',stroke:'#00d4ff','stroke-width':'1.2',
    'stroke-dasharray':'8,6',opacity:'0',filter:'url(#glow-cyan)'});
  gField.appendChild(p);
  fieldLineEls.push(p);
}

// 伞图示
function buildUmbrella(){
  gUmb.innerHTML='';
  const sc=0.55;
  // 伞面
  const canopy=svgEl('path',{id:'umbCanopy',d:'',fill:'rgba(0,180,216,0.15)',
    stroke:'#00b4d8','stroke-width':'1.5'});
  gUmb.appendChild(canopy);
  // 伞骨线
  const ribLines=[];
  for(let i=0;i<5;i++){
    const l=svgEl('line',{stroke:'rgba(180,200,220,0.4)','stroke-width':'1'});
    gUmb.appendChild(l);
    ribLines.push(l);
  }
  // 伞柄
  const handle=svgEl('line',{id:'umbHandle',x1:0,y1:0,x2:0,y2:120,
    stroke:'rgba(180,200,220,0.5)','stroke-width':'2','stroke-linecap':'round'});
  gUmb.appendChild(handle);
  // 标签
  const lbl=svgEl('text',{x:0,y:180,fill:'#5a6a80','font-size':'11',
    'text-anchor':'middle','font-family':'Noto Sans SC'});
  lbl.textContent='整体伞态';
  gUmb.appendChild(lbl);
  return {canopy,ribLines,handle};
}
const umbEls=buildUmbrella();

// 标注
function buildAnnotations(){
  gAnnot.innerHTML='';
  const annots=[
    {text:'硅胶软管', tx:800, ty:280, px:800, py:360},
    {text:'磁流变液', tx:750, ty:450, px:750, py:400},
    {text:'钕铁硼磁珠', tx:950, ty:500, px:950, py:410},
    {text:'电磁线圈', tx:180, ty:330, px:260, py:390},
    {text:'磁场线', tx:600, ty:230, px:600, py:340},
  ];
  annots.forEach(a=>{
    const line=svgEl('line',{x1:a.tx,y1:a.ty+6,x2:a.px,y2:a.py,
      stroke:'rgba(255,255,255,0.12)','stroke-width':'0.8','stroke-dasharray':'3,2'});
    gAnnot.appendChild(line);
    const dot=svgEl('circle',{cx:a.px,cy:a.py,r:2.5,fill:'rgba(255,255,255,0.2)'});
    gAnnot.appendChild(dot);
    const t=svgEl('text',{x:a.tx,y:a.ty,fill:'#7a8a9e','font-size':'12',
      'text-anchor':'middle','font-family':'Noto Sans SC','font-weight':'400'});
    t.textContent=a.text;
    gAnnot.appendChild(t);
  });
}
buildAnnotations();

// 状态面板
function buildStatePanel(){
  gState.innerHTML='';
  const bx=950, by=680;
  const items=[
    {id:'stField', label:'电磁场', x:bx, y:by},
    {id:'stFluid', label:'磁流变液', x:bx, y:by+36},
    {id:'stBead',  label:'磁珠链', x:bx, y:by+72},
    {id:'stRib',   label:'伞骨', x:bx, y:by+108},
  ];
  items.forEach(it=>{
    const lbl=svgEl('text',{x:it.x,y:it.y,fill:'#5a6a80','font-size':'13',
      'font-family':'Noto Sans SC','font-weight':'400'});
    lbl.textContent=it.label;
    gState.appendChild(lbl);
    const val=svgEl('text',{id:it.id,x:it.x+90,y:it.y,fill:'#00b4d8','font-size':'13',
      'font-family':'Rajdhani','font-weight':'600'});
    val.textContent='—';
    gState.appendChild(val);
  });
}
buildStatePanel();

/* ============ 更新函数 ============ */
function updateScene(fieldStr, elapsed){
  const cl = centerline(fieldStr);
  const tp = tubePath(cl, RIB_THICK);

  // --- 流体填充 ---
  ribFluid.setAttribute('d', tp);
  // 更新渐变色
  const fluidCol = lerpColor(COL_LIQ, COL_SOL, fieldStr);
  const fluidAlpha = lerp(0.55, 0.82, fieldStr);
  const grad = document.getElementById('fluid-grad');
  const stops = grad.querySelectorAll('stop');
  stops.forEach(s=>{ s.setAttribute('stop-color', fluidCol); s.setAttribute('stop-opacity', fluidAlpha); });

  // --- 管道壁 ---
  ribWall.setAttribute('d', tp);
  // 高光线 (沿中心线上方偏移)
  const hlThick = RIB_THICK*0.35;
  const hlCl = cl.map(p=>({x:p.x, y:p.y - RIB_THICK*0.22}));
  let hlD=`M${hlCl[0].x.toFixed(1)},${hlCl[0].y.toFixed(1)}`;
  // 取每隔几个点做平滑
  for(let i=3;i<hlCl.length;i+=3){
    hlD+=` L${hlCl[i].x.toFixed(1)},${hlCl[i].y.toFixed(1)}`;
  }
  ribHighlight.setAttribute('d', hlD);

  // --- 磁珠 ---
  const beads = beadPos(cl, fieldStr);
  beads.forEach((b,i)=>{
    const el=beadEls[i];
    el.c.setAttribute('cx', b.x); el.c.setAttribute('cy', b.y);
    el.glow.setAttribute('cx', b.x); el.glow.setAttribute('cy', b.y);
    el.hl.setAttribute('cx', b.x-1.5); el.hl.setAttribute('cy', b.y-1.5);
    // 磁珠辉光随场强
    const glowOp = fieldStr > 0.6 ? (fieldStr-0.6)/0.4 * 0.5 : 0;
    el.glow.setAttribute('opacity', glowOp);
    // 磁珠颜色随场强微微偏亮
    const br = Math.round(lerp(255,255,fieldStr));
    const bg = Math.round(lerp(45,80,fieldStr));
    const bb = Math.round(lerp(85,60,fieldStr));
    el.c.setAttribute('fill', `rgb(${br},${bg},${bb})`);
  });

  // --- 磁场线 ---
  fieldLineEls.forEach((fl,fi)=>{
    const offset = (fi - (N_FLINES-1)/2) * 18;
    let d='';
    const step = Math.max(1, Math.floor(cl.length/30));
    for(let i=0;i<cl.length;i+=step){
      const p=cl[i];
      // 计算法线偏移
      let dx,dy;
      if(i===0){dx=cl[1].x-cl[0].x;dy=cl[1].y-cl[0].y;}
      else if(i>=cl.length-1){dx=cl[i].x-cl[i-1].x;dy=cl[i].y-cl[i-1].y;}
      else{dx=cl[i+1].x-cl[i-1].x;dy=cl[i+1].y-cl[i-1].y;}
      const len=Math.sqrt(dx*dx+dy*dy)||1;
      const nx=-dy/len, ny=dx/len;
      const px=p.x+nx*offset, py=p.y+ny*offset;
      d += (i===0?'M':' L') + px.toFixed(1)+','+py.toFixed(1);
    }
    fl.setAttribute('d', d);
    const fOp = fieldStr * 0.55 * (0.7 + 0.3*Math.sin(elapsed*3 + fi*0.8));
    fl.setAttribute('opacity', clamp01(fOp));
    // 流动动画
    const dashOff = -elapsed * 40;
    fl.setAttribute('stroke-dashoffset', dashOff.toFixed(1));
  });

  // --- 电磁线圈辉光 ---
  const coilGlow = document.getElementById('coilGlow');
  if(coilGlow){
    const cgOp = fieldStr * (0.5 + 0.3*Math.sin(elapsed*4));
    coilGlow.setAttribute('opacity', clamp01(cgOp).toFixed(2));
  }

  // --- 伞图示 ---
  updateUmbrella(fieldStr);

  // --- 状态面板 ---
  updateStatePanel(fieldStr);

  // --- UI ---
  fVal.textContent = Math.round(fieldStr*100)+'%';
  if(fieldStr > 0.7){
    stLabel.textContent = '刚化支撑';
    stLabel.style.color = '#ff8c00';
  } else if(fieldStr < 0.3){
    stLabel.textContent = '柔性卷曲';
    stLabel.style.color = '#00b4d8';
  } else {
    stLabel.textContent = '相态切换中';
    stLabel.style.color = '#7b68ee';
  }
}

function updateUmbrella(fieldStr){
  const cx=110, cy=120;
  const openW=100, closedW=12;
  const openH=70, closedH=50;
  const w = lerp(closedW, openW, fieldStr);
  const h = lerp(closedH, openH, fieldStr);

  // 伞面弧线
  const d=`M${cx-w},${cy} Q${cx},${cy-h*2} ${cx+w},${cy}`;
  umbEls.canopy.setAttribute('d', d);
  // 伞面填充色随场强
  const umbFill = lerpColor([0,180,216],[255,140,0], fieldStr);
  umbEls.canopy.setAttribute('fill', umbFill.replace('rgb','rgba').replace(')',',0.12)'));
  umbEls.canopy.setAttribute('stroke', umbFill);

  // 伞骨线
  umbEls.ribLines.forEach((l,i)=>{
    const angle = lerp(Math.PI/2, (i/(umbEls.ribLines.length-1))*Math.PI*0.8 + Math.PI*0.1, fieldStr);
    const ribLen = lerp(15, w*0.9, fieldStr);
    l.setAttribute('x1', cx);
    l.setAttribute('y1', cy);
    l.setAttribute('x2', cx + Math.cos(angle-Math.PI)*ribLen);
    l.setAttribute('y2', cy - Math.sin(angle)*ribLen*0.6);
    l.setAttribute('stroke', umbFill);
    l.setAttribute('opacity', 0.4+fieldStr*0.3);
  });

  // 伞柄
  const handleDrop = lerp(60, 100, fieldStr);
  umbEls.handle.setAttribute('x1', cx);
  umbEls.handle.setAttribute('y1', cy);
  umbEls.handle.setAttribute('x2', cx);
  umbEls.handle.setAttribute('y2', cy + handleDrop);
}

function updateStatePanel(fieldStr){
  const stField = document.getElementById('stField');
  const stFluid = document.getElementById('stFluid');
  const stBead  = document.getElementById('stBead');
  const stRib   = document.getElementById('stRib');

  if(stField){
    stField.textContent = fieldStr > 0.5 ? 'ON  激活' : 'OFF 关闭';
    stField.setAttribute('fill', fieldStr > 0.5 ? '#00d4ff' : '#5a6a80');
  }
  if(stFluid){
    if(fieldStr > 0.7) { stFluid.textContent = '固态 · 刚化'; stFluid.setAttribute('fill','#ff8c00'); }
    else if(fieldStr < 0.3) { stFluid.textContent = '液态 · 流动'; stFluid.setAttribute('fill','#00b4d8'); }
    else { stFluid.textContent = '相变中...'; stFluid.setAttribute('fill','#7b68ee'); }
  }
  if(stBead){
    if(fieldStr > 0.7) { stBead.textContent = '耦合锁定'; stBead.setAttribute('fill','#ff2d55'); }
    else if(fieldStr < 0.3) { stBead.textContent = '解耦散布'; stBead.setAttribute('fill','#5a6a80'); }
    else { stBead.textContent = '链化中...'; stBead.setAttribute('fill','#9b88ff'); }
  }
  if(stRib){
    if(fieldStr > 0.7) { stRib.textContent = '刚性支撑'; stRib.setAttribute('fill','#ff8c00'); }
    else if(fieldStr < 0.3) { stRib.textContent = '柔性卷曲'; stRib.setAttribute('fill','#00b4d8'); }
    else { stRib.textContent = '刚化中...'; stRib.setAttribute('fill','#7b68ee'); }
  }
}

/* ============ 粒子效果(磁流变液内部微粒) ============ */
const particles=[];
const N_PARTICLES=40;
for(let i=0;i<N_PARTICLES;i++){
  const c=svgEl('circle',{r:1.2+Math.random()*1.2, fill:'rgba(255,255,255,0.15)',opacity:'0'});
  gRib.appendChild(c);
  particles.push({
    el:c,
    t: Math.random(),        // 沿伞骨的位置 0-1
    offset: (Math.random()-0.5)*0.7, // 横向偏移 -0.5~0.5
    speed: 0.02+Math.random()*0.04,
    phase: Math.random()*Math.PI*2
  });
}

function updateParticles(cl, fieldStr, elapsed){
  const ht=RIB_THICK/2-4;
  particles.forEach(p=>{
    // 液态时粒子流动,固态时粒子冻结
    if(fieldStr < 0.7){
      p.t += p.speed * (1-fieldStr) * 0.016; // 大约60fps
      if(p.t > 1) p.t -= 1;
    }
    const idx = Math.min(Math.floor(p.t*(cl.length-1)), cl.length-2);
    const frac = p.t*(cl.length-1)-idx;
    const cx = lerp(cl[idx].x, cl[idx+1].x, frac);
    const cy = lerp(cl[idx].y, cl[idx+1].y, frac);

    // 法线方向
    let dx,dy;
    if(idx===0){dx=cl[1].x-cl[0].x;dy=cl[1].y-cl[0].y;}
    else{dx=cl[idx+1].x-cl[idx-1].x;dy=cl[idx+1].y-cl[idx-1].y;}
    const len=Math.sqrt(dx*dx+dy*dy)||1;
    const nx=-dy/len, ny=dx/len;

    const lateralOff = p.offset * ht * (1-fieldStr*0.8);
    const wobble = Math.sin(elapsed*2+p.phase)*3*(1-fieldStr);
    const px = cx + nx*(lateralOff+wobble);
    const py = cy + ny*(lateralOff+wobble);

    p.el.setAttribute('cx', px.toFixed(1));
    p.el.setAttribute('cy', py.toFixed(1));

    // 粒子可见度:液态时可见,固态时微弱
    const vis = lerp(0.25, 0.05, fieldStr);
    p.el.setAttribute('opacity', vis.toFixed(2));
    // 固态时粒子颜色偏暖
    const pCol = lerpColor([200,230,255],[255,200,120],fieldStr);
    p.el.setAttribute('fill', pCol);
  });
}

/* ============ 磁链连线效果 ============ */
const chainLine = svgEl('path',{id:'chainLine',fill:'none',stroke:'#ff2d55',
  'stroke-width':'1.5','stroke-dasharray':'4,3',opacity:'0'});
gBeads.insertBefore(chainLine, gBeads.firstChild);

function updateChainLine(beads, fieldStr){
  if(fieldStr < 0.3){
    chainLine.setAttribute('opacity','0');
    return;
  }
  let d='';
  beads.forEach((b,i)=>{
    d += (i===0?'M':'L') + b.x.toFixed(1)+','+b.y.toFixed(1);
  });
  chainLine.setAttribute('d', d);
  const op = lerp(0, 0.6, (fieldStr-0.3)/0.7);
  chainLine.setAttribute('opacity', clamp01(op).toFixed(2));
}

/* ============ 磁珠间引力线效果 ============ */
const attractLines=[];
for(let i=0;i<N_BEADS-1;i++){
  const l=svgEl('line',{stroke:'#ff2d55','stroke-width':'0.8',opacity:'0',
    'stroke-dasharray':'2,4'});
  gBeads.insertBefore(l, gBeads.firstChild);
  attractLines.push(l);
}

function updateAttractLines(beads, fieldStr){
  attractLines.forEach((l,i)=>{
    if(i >= beads.length-1) return;
    l.setAttribute('x1', beads[i].x);
    l.setAttribute('y1', beads[i].y);
    l.setAttribute('x2', beads[i+1].x);
    l.setAttribute('y2', beads[i+1].y);
    const op = fieldStr > 0.5 ? lerp(0, 0.4, (fieldStr-0.5)/0.5) : 0;
    l.setAttribute('opacity', op.toFixed(2));
  });
}

/* ============ 动画主循环 ============ */
function animate(ts){
  if(t0===null) t0=ts;
  const elapsed=(ts-t0)/1000;

  if(autoPlay){
    const t=elapsed%LOOP_SEC;
    if(t<2) fs=0;
    else if(t<5) fs=easeIO((t-2)/3);
    else if(t<8) fs=1;
    else if(t<11) fs=1-easeIO((t-8)/3);
    else fs=0;
    slider.value=Math.round(fs*100);
  } else {
    fs=manualFs;
  }

  const cl=centerline(fs);
  const beads=beadPos(cl, fs);

  updateScene(fs, elapsed);
  updateParticles(cl, fs, elapsed);
  updateChainLine(beads, fs);
  updateAttractLines(beads, fs);

  requestAnimationFrame(animate);
}

/* ============ 事件 ============ */
slider.addEventListener('input', e=>{
  autoPlay=false;
  manualFs=e.target.value/100;
  autoBtn.classList.remove('active');
  autoBtn.textContent='MANUAL';
});
autoBtn.addEventListener('click', ()=>{
  autoPlay=!autoPlay;
  if(autoPlay){
    t0=null;
    autoBtn.classList.add('active');
    autoBtn.textContent='AUTO';
  } else {
    manualFs=fs;
    autoBtn.classList.remove('active');
    autoBtn.textContent='MANUAL';
  }
});

/* ============ 启动 ============ */
requestAnimationFrame(animate);

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

实现说明

本动画聚焦展示磁流变液智能伞骨的 TRIZ 最终理想解原理,核心视觉逻辑如下:

  1. 双态切换动画:页面加载后自动循环播放「电磁场关闭 → 激活 → 刚化撑伞 → 关闭 → 柔性卷曲」的完整时序,场强由 easeInOutCubic 缓动驱动,所有视觉元素(管道形态、流体色彩、磁珠排列、场线可见度)均随场强 0→1 连续插值。

  2. 伞骨管道形变:刚化态为微上扬直线,柔性态为下垂+正弦波动的鞭状曲线,60 个采样点保证路径平滑,管壁通过法线偏移生成封闭轮廓。

  3. 磁流变液相变:填充色从冷调青蓝(液态)平滑过渡至暖调琥珀橙(固态),透明度同步变化;40 个微粒在液态时沿管内流动、固态时冻结,直观呈现"液态 ↔ 固态"的宏观可控切换。

  4. 钕铁硼磁珠链锁:14 颗磁珠在场强上升时从散布状态逐步归位至中心线一条直链,链连线与珠间引力线渐显,辅以红色辉光强调"链锁刚化"这一破除矛盾的关键动作。

  5. 电磁场可视化:7 条磁场线沿管道法向分布,通过 stroke-dashoffset 持续流动并脉动透明度,线圈外壳同步辉光,清晰展示"电磁场驱动相变"的资源利用逻辑。

  6. 交互控制:底部滑块可手动接管场强,AUTO/MANUAL 按钮切换自动/手动模式,状态面板实时标注电磁场、磁流变液、磁珠链、伞骨的四维状态。

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