分享图
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=Rajdhani:wght@300;500;700&family=Share+Tech+Mono&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<style>
:root {
  --bg:#080c14;--bg2:#0e1422;--card:#131b2a;--border:#1e2d42;
  --text:#d8e0ec;--muted:#6b7a90;--accent:#00e5ff;
  --gold:#ffb300;--gold-dim:#5c4a10;--red:#ff5252;
  --seg:#1a2636;--seg-s:#2e4058;--teeth-off:#3a4a5e;
}
*{margin:0;padding:0;box-sizing:border-box}
body{
  background:var(--bg);color:var(--text);
  font-family:'Share Tech Mono',monospace;
  min-height:100vh;display:flex;flex-direction:column;align-items:center;
  overflow-x:hidden;
  background-image:
    radial-gradient(ellipse 80% 60% at 50% 30%,rgba(0,229,255,.04),transparent),
    radial-gradient(ellipse 60% 50% at 80% 70%,rgba(255,179,0,.03),transparent);
}
header{
  text-align:center;padding:28px 20px 10px;width:100%;max-width:1100px;
}
header h1{
  font-family:'Rajdhani',sans-serif;font-weight:700;font-size:2.2rem;
  letter-spacing:.08em;color:var(--text);
  background:linear-gradient(90deg,var(--accent),var(--gold));
  -webkit-background-clip:text;-webkit-text-fill-color:transparent;
}
header p{
  font-size:.82rem;color:var(--muted);margin-top:4px;letter-spacing:.15em;
  text-transform:uppercase;
}
.main-wrap{
  width:100%;max-width:1100px;padding:0 16px;
  display:flex;flex-direction:column;gap:14px;
}
.svg-box{
  background:var(--bg2);border:1px solid var(--border);border-radius:12px;
  overflow:hidden;position:relative;
}
.svg-box svg{display:block;width:100%;height:auto}
.panels{
  display:grid;grid-template-columns:1fr 1fr;gap:14px;
}
@media(max-width:700px){.panels{grid-template-columns:1fr}}
.panel{
  background:var(--card);border:1px solid var(--border);border-radius:10px;
  padding:16px;position:relative;overflow:hidden;
}
.panel::before{
  content:'';position:absolute;top:0;left:0;right:0;height:2px;
  background:linear-gradient(90deg,transparent,var(--accent),transparent);opacity:.4;
}
.panel-title{
  font-family:'Rajdhani',sans-serif;font-weight:600;font-size:.85rem;
  color:var(--accent);letter-spacing:.12em;text-transform:uppercase;margin-bottom:10px;
}
.info-row{
  display:flex;justify-content:space-between;align-items:center;
  padding:6px 0;border-bottom:1px solid rgba(255,255,255,.04);font-size:.78rem;
}
.info-row:last-child{border:none}
.info-label{color:var(--muted)}
.info-value{font-weight:600}
.info-value.lock{color:var(--gold)}
.info-value.unlock{color:var(--red)}
.info-value.trans{color:var(--accent)}
.controls-bar{
  background:var(--card);border:1px solid var(--border);border-radius:10px;
  padding:16px 20px;display:flex;flex-wrap:wrap;align-items:center;gap:14px;
}
.slider-group{flex:1;min-width:200px;display:flex;flex-direction:column;gap:4px}
.slider-group label{font-size:.72rem;color:var(--muted);letter-spacing:.1em;text-transform:uppercase}
input[type=range]{
  -webkit-appearance:none;width:100%;height:6px;border-radius:3px;
  background:linear-gradient(90deg,var(--red),var(--accent),var(--gold));
  outline:none;cursor:pointer;
}
input[type=range]::-webkit-slider-thumb{
  -webkit-appearance:none;width:20px;height:20px;border-radius:50%;
  background:var(--text);border:3px solid var(--accent);cursor:grab;
  box-shadow:0 0 10px rgba(0,229,255,.4);
}
.btn{
  font-family:'Rajdhani',sans-serif;font-weight:600;font-size:.82rem;
  letter-spacing:.08em;padding:8px 18px;border:1px solid var(--border);
  border-radius:6px;background:var(--bg2);color:var(--text);cursor:pointer;
  transition:all .2s;display:flex;align-items:center;gap:6px;
}
.btn:hover{border-color:var(--accent);color:var(--accent);background:rgba(0,229,255,.06)}
.btn.active{border-color:var(--gold);color:var(--gold);background:rgba(255,179,0,.08)}
.speed-group{display:flex;align-items:center;gap:8px;font-size:.72rem;color:var(--muted)}
.speed-group input[type=range]{width:80px;height:4px;
  background:linear-gradient(90deg,var(--muted),var(--accent))}
.ifr-footer{
  max-width:1100px;width:100%;padding:14px 20px 30px;
  font-size:.74rem;color:var(--muted);line-height:1.7;
}
.ifr-footer strong{color:var(--gold);font-weight:600}
@keyframes pulse-glow{0%,100%{opacity:.6}50%{opacity:1}}
</style>
</head>
<body>

<header>
  <h1>连环体伞骨 · 最终理想解原理动画</h1>
  <p>Chain-Link Umbrella Rib &mdash; Ideal Final Result Demonstration</p>
</header>

<div class="main-wrap">
  <!-- 主动画 -->
  <div class="svg-box">
    <svg id="mainSvg" viewBox="0 0 1060 440" xmlns="http://www.w3.org/2000/svg"></svg>
  </div>

  <!-- 面板 -->
  <div class="panels">
    <!-- 细节视图 -->
    <div class="panel">
      <div class="panel-title"><i class="fa fa-search-plus"></i> 锯齿啮合细节</div>
      <svg id="detailSvg" viewBox="0 0 480 220" xmlns="http://www.w3.org/2000/svg"></svg>
    </div>
    <!-- 状态信息 -->
    <div class="panel">
      <div class="panel-title"><i class="fa fa-info-circle"></i> 机构状态</div>
      <div id="infoPanel">
        <div class="info-row"><span class="info-label">当前状态</span><span class="info-value" id="stState">刚性锁定</span></div>
        <div class="info-row"><span class="info-label">锯齿啮合度</span><span class="info-value" id="stEngage">100%</span></div>
        <div class="info-row"><span class="info-label">轴向力方向</span><span class="info-value" id="stForce">压缩</span></div>
        <div class="info-row"><span class="info-label">镍钛超弹丝</span><span class="info-value" id="stWire">张紧承压</span></div>
        <div class="info-row"><span class="info-label">链节自由度</span><span class="info-value" id="stDof">0° (锁定)</span></div>
        <div class="info-row"><span class="info-label">环体参数</span><span class="info-value" style="color:var(--muted)">5mm×⌀3mm Ti</span></div>
        <div class="info-row"><span class="info-label">丝径 / 齿高</span><span class="info-value" style="color:var(--muted)">⌀1.0mm / 0.3mm</span></div>
        <div class="info-row"><span class="info-label">锯齿互锁角</span><span class="info-value" style="color:var(--muted)">120°</span></div>
      </div>
    </div>
  </div>

  <!-- 控制栏 -->
  <div class="controls-bar">
    <div class="slider-group">
      <label>开合度 (Openness)</label>
      <input type="range" id="openSlider" min="0" max="100" value="100">
    </div>
    <button class="btn" id="btnAuto"><i class="fa fa-play"></i> 自动演示</button>
    <button class="btn" id="btnReset"><i class="fa fa-undo"></i> 重置</button>
    <div class="speed-group">
      <span>速度</span>
      <input type="range" id="speedSlider" min="20" max="200" value="80">
    </div>
  </div>
</div>

<div class="ifr-footer">
  <strong>IFR 核心原理:</strong>利用开合运动中推块自身的轴向推力驱动锯齿啮合,以零额外动力源、极小增重实现「受压刚化、卸载柔化」的可逆刚柔转换——矛盾在系统内部自消除,理想解即资源自足。
</div>

<script>
// ============ 常量 ============
const NS='http://www.w3.org/2000/svg';
const C={
  N:10,SEG_W:52,SEG_H:22,TOOTH_H:6,TOOTH_N:5,
  GAP_MAX:5,FIXED_X:115,FIXED_Y:195,RAIL_Y:195
};

// ============ 状态 ============
let openness=1, autoPlay=false, autoDir=-1, autoTime=0, speed=80;
let mainSvg, detailSvg, segGroups=[], jointGlows=[], wirePath, pushBlock, pushRod;
let forceArrows=[], stateLabel, engageIndicators=[];
let detSegGroups=[], detTeethPaths=[], detWirePath;

// ============ 工具函数 ============
function el(tag,attrs,parent){
  const e=document.createElementNS(NS,tag);
  for(const[k,v] of Object.entries(attrs||{})) e.setAttribute(k,v);
  if(parent) parent.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 computeChain(open){
  const joints=[{x:C.FIXED_X,y:C.FIXED_Y}];
  const segs=[];
  let x=C.FIXED_X, y=C.FIXED_Y;
  for(let i=0;i<C.N;i++){
    const progress=i/(C.N-1);
    const maxAng=82;
    // 角度沿链条逐渐增大(闭合时形成自然悬垂曲线)
    const ang=open*0+(1-open)*maxAng*progress;
    const gap=(1-open)*C.GAP_MAX;
    const total=C.SEG_W+gap;
    const rad=ang*Math.PI/180;
    const cx=x+total/2*Math.cos(rad);
    const cy=y+total/2*Math.sin(rad);
    segs.push({x:cx,y:cy,angle:ang,engage:open});
    x+=total*Math.cos(rad);
    y+=total*Math.sin(rad);
    joints.push({x,y});
  }
  return {joints,segs,lastX:x,lastY:y};
}

// ============ 创建主视图 ============
function createMainView(){
  mainSvg=document.getElementById('mainSvg');
  // 背景网格
  const gridG=el('g',{opacity:.08},mainSvg);
  for(let x=0;x<=1060;x+=40) el('line',{x1:x,y1:0,x2:x,y2:440,stroke:'#4af'},gridG);
  for(let y=0;y<=440;y+=40) el('line',{x1:0,y1:y,x2:1060,y2:y,stroke:'#4af'},gridG);

  // 定义滤镜和渐变
  const defs=el('defs',{},mainSvg);
  // 发光滤镜
  const fGlow=el('filter',{id:'glow',x:-50%,y:-50%,width:200%,height:200%},defs);
  el('feGaussianBlur',{in:'SourceGraphic',stdDeviation:4,result:'b'},fGlow);
  const fMerge=el('feMerge',{},fGlow);
  el('feMergeNode',{in:'b'},fMerge);
  el('feMergeNode',{in:'SourceGraphic'},fMerge);
  // 金色发光
  const fGold=el('filter',{id:'glowGold',x:-50%,y:-50%,width:200%,height:200%},defs);
  el('feGaussianBlur',{in:'SourceGraphic',stdDeviation:3,result:'b'},fGold);
  const fMerge2=el('feMerge',{},fGold);
  el('feMergeNode',{in:'b'},fMerge2);
  el('feMergeNode',{in:'SourceGraphic'},fMerge2);
  // 箭头标记
  const mArrow=el('marker',{id:'arrowR',markerWidth:8,markerHeight:6,refX:8,refY:3,orient:'auto'},defs);
  el('polygon',{points:'0 0,8 3,0 6',fill:'var(--red)'},mArrow);
  const mArrowC=el('marker',{id:'arrowC',markerWidth:8,markerHeight:6,refX:8,refY:3,orient:'auto'},defs);
  el('polygon',{points:'0 0,8 3,0 6',fill:'var(--accent)'},mArrowC);

  // 导轨
  el('line',{x1:80,y1:C.RAIL_Y,x2:1020,y2:C.RAIL_Y,stroke:'#1e2d42','stroke-width':2,'stroke-dasharray':'6 4'},mainSvg);

  // 固定端(伞尖端)
  const fixG=el('g',{},mainSvg);
  el('rect',{x:C.FIXED_X-14,y:C.RAIL_Y-30,width:14,height:60,rx:3,fill:'#1e2d42',stroke:'#2e4058','stroke-width':1.5},fixG);
  el('polygon',{points:`${C.FIXED_X-7},${C.RAIL_Y-38} ${C.FIXED_X-18},${C.RAIL_Y-30} ${C.FIXED_X+4},${C.RAIL_Y-30}`,fill:'var(--accent)',opacity:.6},fixG);
  el('text',{x:C.FIXED_X-7,y:C.RAIL_Y+50,'text-anchor':'middle',fill:'var(--muted)','font-size':'10','font-family':'Share Tech Mono'},fixG).textContent='固定端(伞尖)';

  // 推杆和推块(稍后更新位置)
  pushRod=el('line',{x1:0,y1:C.RAIL_Y,x2:0,y2:C.RAIL_Y,stroke:'#3a5068','stroke-width':3},mainSvg);
  pushBlock=el('g',{},mainSvg);
  el('rect',{x:-15,y:C.RAIL_Y-28,width:30,height:56,rx:4,fill:'#1a2a3c',stroke:'var(--accent)','stroke-width':1.5},pushBlock);
  el('line',{x1:-6,y1:C.RAIL_Y-8,x2:6,y2:C.RAIL_Y-8,stroke:'var(--accent)','stroke-width':1.5,opacity:.5},pushBlock);
  el('line',{x1:-6,y1:C.RAIL_Y,x2:6,y2:C.RAIL_Y,stroke:'var(--accent)','stroke-width':1.5,opacity:.5},pushBlock);
  el('line',{x1:-6,y1:C.RAIL_Y+8,x2:6,y2:C.RAIL_Y+8,stroke:'var(--accent)','stroke-width':1.5,opacity:.5},pushBlock);

  // 镍钛丝
  wirePath=el('path',{fill:'none',stroke:'var(--accent)','stroke-width':2,'stroke-linecap':'round'},mainSvg);

  // 链节
  for(let i=0;i<C.N;i++){
    const g=el('g',{},mainSvg);
    // 节段主体
    el('rect',{x:-C.SEG_W/2,y:-C.SEG_H/2,width:C.SEG_W,height:C.SEG_H,rx:3,
      fill:'#1a2636',stroke:'#2e4058','stroke-width':1.2,'class':'seg-body'},g);
    // 左侧锯齿指示(简化三角)
    for(let t=0;t<3;t++){
      const ty=-C.SEG_H/2+C.SEG_H*(t+0.5)/3;
      el('polygon',{points:`${-C.SEG_W/2},${ty-3} ${-C.SEG_W/2-5},${ty} ${-C.SEG_W/2},${ty+3}`,
        fill:'#2e4058','class':'tooth-l',opacity:.7},g);
    }
    // 右侧锯齿指示
    for(let t=0;t<3;t++){
      const ty=-C.SEG_H/2+C.SEG_H*(t+0.5)/3;
      el('polygon',{points:`${C.SEG_W/2},${ty-3} ${C.SEG_W/2+5},${ty} ${C.SEG_W/2},${ty+3}`,
        fill:'#2e4058','class':'tooth-r',opacity:.7},g);
    }
    // 中心丝孔
    el('circle',{cx:0,cy:0,r:2.5,fill:'none',stroke:'#2e4058','stroke-width':.8},g);
    // 编号
    el('text',{x:0,y:1,'text-anchor':'middle','dominant-baseline':'middle',
      fill:'#4a5a70','font-size':'8','font-family':'Share Tech Mono'},g).textContent=i+1;
    segGroups.push(g);
  }

  // 啮合高亮点(节点处)
  for(let i=0;i<C.N-1;i++){
    const glow=el('circle',{r:8,fill:'var(--gold)',opacity:0,filter:'url(#glowGold)'},mainSvg);
    jointGlows.push(glow);
  }

  // 力方向箭头
  for(let i=0;i<3;i++){
    const arr=el('line',{'stroke-width':2,'marker-end':'url(#arrowR)',opacity:0},mainSvg);
    forceArrows.push(arr);
  }
  // 张力箭头
  const tensArr=el('line',{'stroke-width':2,'marker-end':'url(#arrowC)',opacity:0,stroke:'var(--accent)'},mainSvg);
  forceArrows.push(tensArr);

  // 状态标签
  stateLabel=el('text',{x:530,y:40,'text-anchor':'middle',
    'font-family':'Rajdhani, sans-serif','font-weight':700,'font-size':'22',
    fill:'var(--gold)',letterSpacing:3},mainSvg);

  // 推块标签
  const pbLabel=el('text',{x:0,y:C.RAIL_Y+50,'text-anchor':'middle',fill:'var(--accent)',
    'font-size':'10','font-family':'Share Tech Mono'},mainSvg);
  pbLabel.textContent='推块';
  pushBlock._label=pbLabel;

  // 伞形轮廓上下文
  const umbG=el('g',{transform:'translate(940,60)',opacity:.35},mainSvg);
  el('line',{x1:0,y1:-25,x2:0,y2:45,stroke:'#4a6a8a','stroke-width':2},umbG);
  const dPath='M -50 10 Q -50 -20 0 -30 Q 50 -20 50 10';
  el('path',{d:dPath,fill:'none',stroke:'#4a6a8a','stroke-width':1.5},umbG);
  el('line',{x1:0,y1:-30,x2:-50,y2:10,stroke:'#4a6a8a','stroke-width':1,'stroke-dasharray':'3 2'},umbG);
  el('line',{x1:0,y1:-30,x2:50,y2:10,stroke:'#4a6a8a','stroke-width':1,'stroke-dasharray':'3 2'},umbG);
  // 高亮伞骨
  el('line',{x1:0,y1:5,x2:-42,y2:8,stroke:'var(--gold)','stroke-width':2.5},umbG);
  el('text',{x:0,y:60,'text-anchor':'middle',fill:'#4a6a8a','font-size':'9','font-family':'Share Tech Mono'},umbG).textContent='应用场景';

  // IFR 资源利用注释
  const noteG=el('g',{transform:'translate(530,400)'},mainSvg);
  el('rect',{x:-220,y:-16,width:440,height:30,rx:6,fill:'rgba(0,229,255,.06)',stroke:'rgba(0,229,255,.15)','stroke-width':1},noteG);
  el('text',{x:0,y:2,'text-anchor':'middle',fill:'var(--accent)','font-size':'11','font-family':'Share Tech Mono'},noteG)
    .textContent='IFR: 推力即资源 → 受压刚化 / 卸载柔化 / 零额外驱动';
}

// ============ 创建细节视图 ============
function createDetailView(){
  detailSvg=document.getElementById('detailSvg');
  const defs=el('defs',{},detailSvg);
  const fG=el('filter',{id:'dGlow',x:-50%,y:-50%,width:200%,height:200%},defs);
  el('feGaussianBlur',{in:'SourceGraphic',stdDeviation:2.5,result:'b'},fG);
  const fm=el('feMerge',{},fG);
  el('feMergeNode',{in:'b'},fm);
  el('feMergeNode',{in:'SourceGraphic'},fm);

  // 标注线和标签
  const labels=[
    {x:30,y:20,text:'锯齿面 (120°)'},
    {x:240,y:20,text:'镍钛诺超弹丝 ⌀1.0mm'},
    {x:400,y:20,text:'钛合金环体 5mm'},
  ];
  labels.forEach(l=>{
    el('text',{x:l.x,y:l.y,fill:'var(--muted)','font-size':'9','font-family':'Share Tech Mono'},detailSvg).textContent=l.text;
  });

  // 3个放大的节段
  const detW=90, detH=48, detTH=14;
  const baseX=70, baseY=115, spacing=10;
  for(let i=0;i<3;i++){
    const bx=baseX+i*(detW+spacing);
    const g=el('g',{},detailSvg);
    // 节段体
    el('rect',{x:0,y:0,width:detW,height:detH,rx:4,fill:'#1a2636',stroke:'#2e4058','stroke-width':1.2},g);
    // 左侧锯齿
    const leftTeeth=el('path',{fill:'none',stroke:'#2e4058','stroke-width':1.5,'class':'dt-path'},g);
    // 右侧锯齿
    const rightTeeth=el('path',{fill:'none',stroke:'#2e4058','stroke-width':1.5,'class':'dt-path'},g);
    // 中心丝
    el('circle',{cx:detW/2,cy:detH/2,r:4,fill:'none',stroke:'#2e4058','stroke-width':1},g);
    // 丝线
    el('line',{x1:-20,y1:detH/2,x2:detW+20,y2:detH/2,stroke:'var(--accent)','stroke-width':1.5,opacity:.5,'stroke-dasharray':'4 3'},g);

    detSegGroups.push({g,leftTeeth,rightTeeth,bx,by:baseY,detW,detH});
    g.setAttribute('transform',`translate(${bx},${baseY})`);
    detailSvg.appendChild(g);

    // 生成锯齿路径
    const nTeeth=5;
    const toothW=detH/nTeeth;
    // 右侧锯齿(从上到下)
    let rd=`M ${detW} 0`;
    for(let t=0;t<nTeeth;t++){
      const y1=t*toothW, y2=y1+toothW/2, y3=y1+toothW;
      rd+=` L ${detW+detTH} ${y2} L ${detW} ${y3}`;
    }
    rightTeeth.setAttribute('d',rd);
    // 左侧锯齿(从下到上,偏移半个齿距实现互锁)
    let ld=`M 0 ${detH}`;
    for(let t=0;t<nTeeth;t++){
      const y3=detH-t*toothW, y2=y3-toothW/2, y1=y3-toothW;
      ld+=` L ${-detTH} ${y2} L 0 ${y1}`;
    }
    leftTeeth.setAttribute('d',ld);
    detTeethPaths.push({left:leftTeeth,right:rightTeeth});
  }

  // 啮合状态指示
  const engRect1=el('rect',{x:baseX+detW-2,y:baseY-5,width:spacing+4,height:detH+10,rx:2,
    fill:'none',stroke:'var(--gold)','stroke-width':1.5,opacity:0,'stroke-dasharray':'3 2'},detailSvg);
  const engRect2=el('rect',{x:baseX+2*(detW+spacing)-2,y:baseY-5,width:spacing+4,height:detH+10,rx:2,
    fill:'none',stroke:'var(--gold)','stroke-width':1.5,opacity:0,'stroke-dasharray':'3 2'},detailSvg);
  detailSvg._engRects=[engRect1,engRect2];

  // 啮合/脱开标签
  const engLabel=el('text',{x:240,y:190,'text-anchor':'middle',fill:'var(--gold)',
    'font-size':'12','font-family':'Rajdhani, sans-serif','font-weight':600},detailSvg);
  detailSvg._engLabel=engLabel;

  // 力方向指示
  const fArr1=el('line',{x1:baseX-18,y1:baseY+detH/2,x2:baseX-4,y2:baseY+detH/2,
    stroke:'var(--red)','stroke-width':2,'marker-end':'url(#arrowR)'},detailSvg);
  const fArr2=el('line',{x1:baseX+3*(detW+spacing)+4,y1:baseY+detH/2,x2:baseX+3*(detW+spacing)+18,y2:baseY+detH/2,
    stroke:'var(--red)','stroke-width':2,'marker-end':'url(#arrowR)'},detailSvg);
  // 箭头标记(细节视图内)
  const dm=el('marker',{id:'arrowR2',markerWidth:8,markerHeight:6,refX:8,refY:3,orient:'auto'},detailSvg.querySelector('defs'));
  el('polygon',{points:'0 0,8 3,0 6',fill:'var(--red)'},dm);
  fArr1.setAttribute('marker-end','url(#arrowR2)');
  fArr2.setAttribute('marker-end','url(#arrowR2)');
  detailSvg._fArrs=[fArr1,fArr2];
}

// ============ 更新主视图 ============
function updateMain(){
  const chain=computeChain(openness);
  const {joints,segs,lastX,lastY}=chain;

  // 更新节段位置
  for(let i=0;i<C.N;i++){
    const s=segs[i];
    const g=segGroups[i];
    g.setAttribute('transform',`translate(${s.x},${s.y}) rotate(${s.angle})`);

    // 更新锯齿颜色
    const eng=s.engage;
    const teethColor=eng>0.5?
      `rgba(255,179,0,${lerp(0.3,0.9,(eng-0.5)*2)})`:
      `rgba(46,64,88,${lerp(0.5,0.3,eng*2)})`;
    g.querySelectorAll('.tooth-l,.tooth-r').forEach(t=>t.setAttribute('fill',teethColor));

    // 节段体边框
    const body=g.querySelector('.seg-body');
    if(eng>0.7){
      body.setAttribute('stroke','#5a6a3c');
      body.setAttribute('fill','#1e2a28');
    }else if(eng<0.3){
      body.setAttribute('stroke','#2e4058');
      body.setAttribute('fill','#1a2636');
    }else{
      body.setAttribute('stroke','#3a5a5e');
      body.setAttribute('fill','#1c2a30');
    }
  }

  // 更新啮合高亮
  for(let i=0;i<C.N-1;i++){
    const j=joints[i+1];
    const glow=jointGlows[i];
    glow.setAttribute('cx',j.x);
    glow.setAttribute('cy',j.y);
    glow.setAttribute('opacity',openness>0.6?lerp(0,0.5,(openness-0.6)/0.4):0);
  }

  // 更新镍钛丝
  let wd=`M ${C.FIXED_X} ${C.FIXED_Y}`;
  for(let i=0;i<C.N;i++){
    wd+=` L ${segs[i].x} ${segs[i].y}`;
  }
  wd+=` L ${lastX} ${lastY}`;
  wirePath.setAttribute('d',wd);
  wirePath.setAttribute('stroke',openness>0.5?'var(--accent)':'#2a4a5e');
  wirePath.setAttribute('stroke-width',openness>0.5?2:1.5);

  // 更新推块
  pushBlock.setAttribute('transform',`translate(${lastX},${0})`);
  pushBlock._label.setAttribute('x',lastX);

  // 推杆
  pushRod.setAttribute('x1',lastX+15);
  pushRod.setAttribute('y1',C.RAIL_Y);
  pushRod.setAttribute('x2',lastX+80);
  pushRod.setAttribute('y2',C.RAIL_Y);

  // 力方向箭头(压缩时显示)
  const showForce=openness>0.3;
  if(showForce){
    const arrOpacity=lerp(0,0.7,(openness-0.3)/0.7);
    // 沿链方向画3个箭头
    for(let a=0;a<3;a++){
      const idx=Math.floor((a+1)*C.N/4);
      const s=segs[Math.min(idx,C.N-1)];
      const rad=s.angle*Math.PI/180;
      const ax=s.x-20*Math.cos(rad);
      const ay=s.y-20*Math.sin(rad);
      const bx=s.x+20*Math.cos(rad);
      const by=s.y+20*Math.sin(rad);
      forceArrows[a].setAttribute('x1',ax);
      forceArrows[a].setAttribute('y1',ay);
      forceArrows[a].setAttribute('x2',bx);
      forceArrows[a].setAttribute('y2',by);
      forceArrows[a].setAttribute('opacity',arrOpacity);
      forceArrows[a].setAttribute('stroke','var(--red)');
      forceArrows[a].setAttribute('marker-end','url(#arrowR)');
    }
  }else{
    for(let a=0;a<3;a++) forceArrows[a].setAttribute('opacity',0);
  }
  // 张力箭头(拉力方向)
  const showTension=openness<0.5;
  if(showTension){
    const tOp=lerp(0,0.6,(0.5-openness)/0.5);
    forceArrows[3].setAttribute('x1',lastX+20);
    forceArrows[3].setAttribute('y1',C.RAIL_Y);
    forceArrows[3].setAttribute('x2',lastX+60);
    forceArrows[3].setAttribute('y2',C.RAIL_Y);
    forceArrows[3].setAttribute('opacity',tOp);
  }else{
    forceArrows[3].setAttribute('opacity',0);
  }

  // 状态标签
  if(openness>0.75){
    stateLabel.textContent='刚性锁定 RIGID LOCK';
    stateLabel.setAttribute('fill','var(--gold)');
  }else if(openness<0.25){
    stateLabel.textContent='柔性释放 FLEXIBLE RELEASE';
    stateLabel.setAttribute('fill','var(--red)');
  }else{
    stateLabel.textContent='过渡中 TRANSITIONING';
    stateLabel.setAttribute('fill','var(--accent)');
  }
}

// ============ 更新细节视图 ============
function updateDetail(){
  const eng=openness;
  const detW=90,detH=48,detTH=14;
  const baseX=70,baseY=115,spacing=10;
  // 节段间距随啮合度变化
  const gap=lerp(12,2,eng);
  for(let i=0;i<3;i++){
    const bx=baseX+i*(detW+gap);
    detSegGroups[i].g.setAttribute('transform',`translate(${bx},${baseY})`);
    // 锯齿颜色
    const teethCol=eng>0.5?`rgba(255,179,0,${lerp(0.4,1,(eng-0.5)*2)})`:'rgba(46,64,88,0.6)';
    detTeethPaths[i].left.setAttribute('stroke',teethCol);
    detTeethPaths[i].right.setAttribute('stroke',teethCol);
    detTeethPaths[i].left.setAttribute('stroke-width',eng>0.5?2:1.2);
    detTeethPaths[i].right.setAttribute('stroke-width',eng>0.5?2:1.2);
    if(eng>0.6){
      detTeethPaths[i].left.setAttribute('filter','url(#dGlow)');
      detTeethPaths[i].right.setAttribute('filter','url(#dGlow)');
    }else{
      detTeethPaths[i].left.removeAttribute('filter');
      detTeethPaths[i].right.removeAttribute('filter');
    }
  }
  // 啮合指示框
  const rects=detailSvg._engRects;
  rects.forEach((r,idx)=>{
    const rx=baseX+(idx+1)*(detW+gap)-2;
    r.setAttribute('x',rx);
    r.setAttribute('opacity',eng>0.5?lerp(0,0.8,(eng-0.5)*2):0);
  });
  // 啮合标签
  const engLabel=detailSvg._engLabel;
  if(eng>0.7){
    engLabel.textContent='锯齿紧密啮合 → 刚性传递弯矩';
    engLabel.setAttribute('fill','var(--gold)');
  }else if(eng<0.3){
    engLabel.textContent='锯齿脱开 → 链节自由旋转';
    engLabel.setAttribute('fill','var(--red)');
  }else{
    engLabel.textContent='啮合度变化中...';
    engLabel.setAttribute('fill','var(--accent)');
  }
  // 力箭头
  const fArrs=detailSvg._fArrs;
  fArrs[0].setAttribute('x1',baseX-18);
  fArrs[0].setAttribute('x2',baseX-4);
  fArrs[1].setAttribute('x1',baseX+2*(detW+gap)+detW+4);
  fArrs[1].setAttribute('x2',baseX+2*(detW+gap)+detW+18);
  const fOp=eng>0.4?lerp(0,0.8,(eng-0.4)/0.6):0;
  fArrs.forEach(a=>a.setAttribute('opacity',fOp));
}

// ============ 更新信息面板 ============
function updateInfo(){
  const eng=openness;
  const stState=document.getElementById('stState');
  const stEngage=document.getElementById('stEngage');
  const stForce=document.getElementById('stForce');
  const stWire=document.getElementById('stWire');
  const stDof=document.getElementById('stDof');

  if(eng>0.75){
    stState.textContent='刚性锁定';stState.className='info-value lock';
  }else if(eng<0.25){
    stState.textContent='柔性释放';stState.className='info-value unlock';
  }else{
    stState.textContent='过渡中';stState.className='info-value trans';
  }
  stEngage.textContent=Math.round(eng*100)+'%';
  stEngage.className='info-value '+(eng>0.5?'lock':'unlock');

  if(eng>0.5){
    stForce.textContent='压缩 (推块上顶)';stForce.className='info-value lock';
  }else{
    stForce.textContent='拉伸 (推块下拉)';stForce.className='info-value unlock';
  }

  stWire.textContent=eng>0.5?'张紧承压':'松弛随动';
  stWire.className='info-value '+(eng>0.5?'lock':'unlock');

  const dof=eng<0.1?360:Math.round(lerp(360,0,clamp(eng*1.5,0,1)));
  stDof.textContent=dof>=350?'全自由度':dof+'°';
  stDof.className='info-value '+(dof>10?'unlock':'lock');
}

// ============ 全量更新 ============
function updateAll(){
  updateMain();
  updateDetail();
  updateInfo();
}

// ============ 动画循环 ============
let lastT=0;
function animate(ts){
  if(autoPlay){
    const dt=(ts-lastT)/1000;
    autoTime+=dt*speed/100;
    // 使用平滑的三角波
    openness=(Math.cos(autoTime*Math.PI*2)*0.5+0.5);
    document.getElementById('openSlider').value=Math.round(openness*100);
    updateAll();
  }
  lastT=ts;
  requestAnimationFrame(animate);
}

// ============ 事件绑定 ============
function bindEvents(){
  const slider=document.getElementById('openSlider');
  slider.addEventListener('input',e=>{
    openness=parseInt(e.target.value)/100;
    updateAll();
  });

  document.getElementById('btnAuto').addEventListener('click',function(){
    autoPlay=!autoPlay;
    this.classList.toggle('active',autoPlay);
    this.innerHTML=autoPlay?'<i class="fa fa-pause"></i> 暂停演示':'<i class="fa fa-play"></i> 自动演示';
    if(autoPlay) autoTime=Math.acos(openness*2-1)/(Math.PI*2);
  });

  document.getElementById('btnReset').addEventListener('click',()=>{
    openness=1;autoPlay=false;
    document.getElementById('openSlider').value=100;
    document.getElementById('btnAuto').classList.remove('active');
    document.getElementById('btnAuto').innerHTML='<i class="fa fa-play"></i> 自动演示';
    updateAll();
  });

  document.getElementById('speedSlider').addEventListener('input',e=>{
    speed=parseInt(e.target.value);
  });
}

// ============ 初始化 ============
function init(){
  createMainView();
  createDetailView();
  updateAll();
  bindEvents();
  requestAnimationFrame(animate);
}

document.addEventListener('DOMContentLoaded',init);
</script>
</body>
</html>

实现说明:

本动画完整呈现了连环体伞骨的 IFR 原理,核心亮点如下:

  1. 主视图动态链节:10 节钛合金连环体在 SVG 中实时计算位置与旋转角——开合度 100% 时锯齿啮合、链条笔直呈刚性杆;开合度降至 0% 时锯齿脱开、链条在模拟重力下自然悬垂,呈现全柔性链节形态。过渡过程平滑流畅,锯齿颜色从金色(啮合)渐变至灰蓝色(脱开),直观映射力学状态。

  2. 锯齿啮合细节面板:3 节放大视图清晰展示 120° 互锁锯齿的啮合/脱开微观过程——齿面偏移半齿距实现交错互锁,啮合时齿形发金光并带辉光滤镜,脱开时节段间距拉大、齿形变暗。

  3. 力的可视化引导:压缩态下沿链节绘制红色力箭头展示轴向压力传递路径;拉伸态下显示青色张力箭头指示超弹丝回拉方向,帮助理解"推力即资源"的 IFR 核心。

  4. 交互控制:滑块可手动无级调节开合度;自动演示按钮触发平滑周期动画;速度滑块控制演示节奏,便于深入观察过渡瞬态。

  5. 状态面板实时反馈:当前刚性/柔性状态、啮合百分比、轴向力方向、丝线张紧度、链节自由度等关键参数随开合度同步更新,将工程机理量化呈现。

动画不会动
<!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=Share+Tech+Mono&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<style>
:root{
  --bg:#060a10;--bg2:#0c1220;--card:#111a2c;--border:#1a2c44;
  --text:#d0daea;--muted:#5a6e88;--accent:#00dce8;
  --gold:#f0a800;--gold-dim:#4a3800;--red:#ff4444;--green:#40e070;
}
*{margin:0;padding:0;box-sizing:border-box}
body{
  background:var(--bg);color:var(--text);font-family:'Share Tech Mono',monospace;
  min-height:100vh;display:flex;flex-direction:column;align-items:center;
  overflow-x:hidden;
}
header{text-align:center;padding:24px 20px 8px;width:100%;max-width:1100px}
header h1{
  font-family:'Rajdhani',sans-serif;font-weight:700;font-size:2rem;letter-spacing:.1em;
  background:linear-gradient(135deg,var(--accent),var(--gold));
  -webkit-background-clip:text;-webkit-text-fill-color:transparent;
}
header p{font-size:.78rem;color:var(--muted);margin-top:2px;letter-spacing:.18em;text-transform:uppercase}
.wrap{width:100%;max-width:1100px;padding:0 12px;display:flex;flex-direction:column;gap:10px}
.svg-box{background:var(--bg2);border:1px solid var(--border);border-radius:10px;overflow:hidden}
.svg-box svg{display:block;width:100%;height:auto}
.row{display:grid;grid-template-columns:1fr 1fr;gap:10px}
@media(max-width:720px){.row{grid-template-columns:1fr}}
.panel{background:var(--card);border:1px solid var(--border);border-radius:8px;padding:14px;position:relative}
.panel::before{content:'';position:absolute;top:0;left:0;right:0;height:2px;background:linear-gradient(90deg,transparent,var(--accent),transparent);opacity:.35}
.pt{font-family:'Rajdhani',sans-serif;font-weight:600;font-size:.8rem;color:var(--accent);letter-spacing:.12em;text-transform:uppercase;margin-bottom:8px;display:flex;align-items:center;gap:6px}
.ir{display:flex;justify-content:space-between;padding:5px 0;border-bottom:1px solid rgba(255,255,255,.03);font-size:.74rem}
.ir:last-child{border:none}
.il{color:var(--muted)}
.iv{font-weight:600}
.iv.gd{color:var(--gold)}.iv.rd{color:var(--red)}.iv.cy{color:var(--accent)}.iv.gn{color:var(--green)}
.ctrl{background:var(--card);border:1px solid var(--border);border-radius:8px;padding:14px 18px;display:flex;flex-wrap:wrap;align-items:center;gap:14px}
.sg{flex:1;min-width:180px;display:flex;flex-direction:column;gap:3px}
.sg label{font-size:.68rem;color:var(--muted);letter-spacing:.1em;text-transform:uppercase}
input[type=range]{-webkit-appearance:none;width:100%;height:5px;border-radius:3px;outline:none;cursor:pointer}
#openSlider{background:linear-gradient(90deg,var(--red),var(--accent),var(--gold))}
#speedSlider{background:linear-gradient(90deg,var(--muted),var(--accent))}
input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;width:18px;height:18px;border-radius:50%;background:var(--text);border:3px solid var(--accent);cursor:grab;box-shadow:0 0 8px rgba(0,220,232,.4)}
.btn{
  font-family:'Rajdhani',sans-serif;font-weight:600;font-size:.8rem;letter-spacing:.06em;
  padding:7px 16px;border:1px solid var(--border);border-radius:5px;background:var(--bg2);
  color:var(--text);cursor:pointer;transition:all .15s;display:flex;align-items:center;gap:5px;
}
.btn:hover{border-color:var(--accent);color:var(--accent);background:rgba(0,220,232,.05)}
.btn.on{border-color:var(--gold);color:var(--gold);background:rgba(240,168,0,.07)}
.spd{display:flex;align-items:center;gap:6px;font-size:.68rem;color:var(--muted)}
.ifr{max-width:1100px;width:100%;padding:10px 18px 28px;font-size:.72rem;color:var(--muted);line-height:1.7}
.ifr strong{color:var(--gold)}
</style>
</head>
<body>
<header>
  <h1>连环体伞骨 · 最终理想解原理动画</h1>
  <p>Chain-Link Rib &mdash; Ideal Final Result</p>
</header>
<div class="wrap">
  <div class="svg-box"><svg id="mSvg" viewBox="0 0 1060 420"></svg></div>
  <div class="row">
    <div class="panel">
      <div class="pt"><i class="fa fa-search-plus"></i> 锯齿啮合细节</div>
      <svg id="dSvg" viewBox="0 0 500 210"></svg>
    </div>
    <div class="panel">
      <div class="pt"><i class="fa fa-tachometer-alt"></i> 机构状态</div>
      <div id="info">
        <div class="ir"><span class="il">当前状态</span><span class="iv" id="s1">刚性锁定</span></div>
        <div class="ir"><span class="il">锯齿啮合度</span><span class="iv" id="s2">100%</span></div>
        <div class="ir"><span class="il">轴向力方向</span><span class="iv" id="s3">压缩</span></div>
        <div class="ir"><span class="il">镍钛超弹丝</span><span class="iv" id="s4">张紧承压</span></div>
        <div class="ir"><span class="il">链节自由度</span><span class="iv" id="s5">0°</span></div>
        <div class="ir"><span class="il">环体参数</span><span class="iv" style="color:var(--muted)">5mm × ⌀3mm Ti</span></div>
        <div class="ir"><span class="il">丝径 / 齿高</span><span class="iv" style="color:var(--muted)">⌀1.0mm / 0.3mm</span></div>
        <div class="ir"><span class="il">互锁角</span><span class="iv" style="color:var(--muted)">120°</span></div>
      </div>
    </div>
  </div>
  <div class="ctrl">
    <div class="sg"><label>开合度 Openness</label><input type="range" id="openSlider" min="0" max="1000" value="1000"></div>
    <button class="btn" id="bAuto"><i class="fa fa-play"></i> 自动演示</button>
    <button class="btn" id="bReset"><i class="fa fa-undo"></i> 重置</button>
    <div class="spd"><span>速度</span><input type="range" id="speedSlider" min="20" max="250" value="90" style="width:80px"></div>
  </div>
</div>
<div class="ifr"><strong>IFR 核心原理:</strong>利用开合运动中推块自身的轴向推力驱动锯齿啮合,以零额外动力源、极小增重实现「受压刚化、卸载柔化」的可逆刚柔转换——矛盾在系统内部自消除,理想解即资源自足。</div>

<script>
(function(){
"use strict";
const NS='http://www.w3.org/2000/svg';

/* ===== 参数 ===== */
const N=10, SW=52, SH=22, TH=6, GMAX=5.5;
const FX=120, FY=200, RY=200;

/* ===== 状态 ===== */
let openV=1, autoOn=false, aTime=0, spd=90, prevTS=null;

/* ===== 工具 ===== */
function $(id){return document.getElementById(id)}
function ce(tag,at,p){const e=document.createElementNS(NS,tag);for(const k in at)e.setAttribute(k,at[k]);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 calcChain(op){
  const js=[{x:FX,y:FY}], ss=[];
  let x=FX, y=FY;
  for(let i=0;i<N;i++){
    const p=i/(N-1);
    const ang=(1-op)*80*p;          // 角度沿链递增
    const gap=(1-op)*GMAX;
    const tot=SW+gap;
    const rad=ang*Math.PI/180;
    ss.push({x:x+tot/2*Math.cos(rad), y:y+tot/2*Math.sin(rad), a:ang, e:op});
    x+=tot*Math.cos(rad);
    y+=tot*Math.sin(rad);
    js.push({x,y});
  }
  return {js,ss,ex:x,ey:y};
}

/* ===== 主视图引用 ===== */
let mSg=[], mJg=[], mWire, mPB, mPR, mPBL, mFA=[], mSL, mGlowDots=[];

function buildMain(){
  const svg=$('mSvg');
  /* defs */
  const df=ce('defs',{},svg);
  /* 发光滤镜 */
  const gf=ce('filter',{id:'gw',x:'-50%',y:'-50%',width:'200%',height:'200%'},df);
  ce('feGaussianBlur',{in:'SourceGraphic',stdDeviation:'5',result:'b'},gf);
  const gm=ce('feMerge',{},gf); ce('feMergeNode',{in:'b'},gm); ce('feMergeNode',{in:'SourceGraphic'},gm);
  /* 金色发光 */
  const gf2=ce('filter',{id:'gwG',x:'-50%',y:'-50%',width:'200%',height:'200%'},df);
  ce('feGaussianBlur',{in:'SourceGraphic',stdDeviation:'3',result:'b'},gf2);
  const gm2=ce('feMerge',{},gf2); ce('feMergeNode',{in:'b'},gm2); ce('feMergeNode',{in:'SourceGraphic'},gm2);
  /* 红色发光 */
  const gf3=ce('filter',{id:'gwR',x:'-50%',y:'-50%',width:'200%',height:'200%'},df);
  ce('feGaussianBlur',{in:'SourceGraphic',stdDeviation:'3',result:'b'},gf3);
  const gm3=ce('feMerge',{},gf3); ce('feMergeNode',{in:'b'},gm3); ce('feMergeNode',{in:'SourceGraphic'},gm3);
  /* 箭头标记 */
  const mk1=ce('marker',{id:'aR',markerWidth:8,markerHeight:6,refX:8,refY:3,orient:'auto'},df);
  ce('polygon',{points:'0 0,8 3,0 6',fill:'#ff4444'},mk1);
  const mk2=ce('marker',{id:'aC',markerWidth:8,markerHeight:6,refX:8,refY:3,orient:'auto'},df);
  ce('polygon',{points:'0 0,8 3,0 6',fill:'#00dce8'},mk2);

  /* 背景网格 */
  const gg=ce('g',{opacity:'.06'},svg);
  for(let x=0;x<=1060;x+=50) ce('line',{x1:x,y1:0,x2:x,y2:420,stroke:'#40c0e0'},gg);
  for(let y=0;y<=420;y+=50) ce('line',{x1:0,y1:y,x2:1060,y2:y,stroke:'#40c0e0'},gg);

  /* 导轨 */
  ce('line',{x1:70,y1:RY,x2:1030,y2:RY,stroke:'#162038','stroke-width':'2','stroke-dasharray':'8 5'},svg);

  /* 固定端 */
  ce('rect',{x:FX-16,y:RY-32,width:16,height:64,rx:3,fill:'#15202e',stroke:'#28405a','stroke-width':'1.5'},svg);
  ce('polygon',{points:`${FX-8},${RY-40} ${FX-20},${RY-32} ${FX+4},${RY-32}`,fill:'#00dce8',opacity:'.5'},svg);
  const ft=ce('text',{x:FX-8,y:RY+55,'text-anchor':'middle',fill:'#5a6e88','font-size':'9','font-family':'Share Tech Mono'},svg);
  ft.textContent='固定端(伞尖)';

  /* 推杆 */
  mPR=ce('line',{x1:0,y1:RY,x2:0,y2:RY,stroke:'#2a3e58','stroke-width':'3'},svg);
  /* 推块 */
  mPB=ce('g',{},svg);
  ce('rect',{x:-16,y:RY-30,width:32,height:60,rx:4,fill:'#15202e',stroke:'#00dce8','stroke-width':'1.5'},mPB);
  ce('line',{x1:-8,y1:RY-10,x2:8,y2:RY-10,stroke:'#00dce8','stroke-width':'1.2',opacity:'.4'},mPB);
  ce('line',{x1:-8,y1:RY,x2:8,y2:RY,stroke:'#00dce8','stroke-width':'1.2',opacity:'.4'},mPB);
  ce('line',{x1:-8,y1:RY+10,x2:8,y2:RY+10,stroke:'#00dce8','stroke-width':'1.2',opacity:'.4'},mPB);
  mPBL=ce('text',{x:0,y:RY+52,'text-anchor':'middle',fill:'#00dce8','font-size':'9','font-family':'Share Tech Mono'},mPB);
  mPBL.textContent='推块';

  /* 超弹丝 */
  mWire=ce('path',{fill:'none',stroke:'#00dce8','stroke-width':'2','stroke-linecap':'round',opacity:'.7'},svg);

  /* 链节 */
  for(let i=0;i<N;i++){
    const g=ce('g',{},svg);
    ce('rect',{x:-SW/2,y:-SH/2,width:SW,height:SH,rx:3,fill:'#16222e',stroke:'#2a3e55','stroke-width':'1.2'},g);
    /* 锯齿指示 - 左右各3个小三角 */
    for(let t=0;t<3;t++){
      const ty=-SH/2+SH*(t+.5)/3;
      ce('polygon',{points:`${-SW/2},${ty-3} ${-SW/2-5},${ty} ${-SW/2},${ty+3}`,fill:'#2a3e55',opacity:'.7'},g);
      ce('polygon',{points:`${SW/2},${ty-3} ${SW/2+5},${ty} ${SW/2},${ty+3}`,fill:'#2a3e55',opacity:'.7'},g);
    }
    ce('circle',{cx:0,cy:0,r:2.2,fill:'none',stroke:'#2a3e55','stroke-width':'.8'},g);
    const nt=ce('text',{x:0,y:1.5,'text-anchor':'middle','dominant-baseline':'middle',fill:'#3a4e68','font-size':'7.5','font-family':'Share Tech Mono'},g);
    nt.textContent=i+1;
    mSg.push(g);
  }

  /* 啮合点发光 */
  for(let i=0;i<N-1;i++){
    const c=ce('circle',{r:'6',fill:'#f0a800',opacity:'0',filter:'url(#gwG)'},svg);
    mJg.push(c);
  }

  /* 压力/拉力箭头 */
  for(let i=0;i<4;i++){
    const ln=ce('line',{'stroke-width':'2',opacity:'0'},svg);
    mFA.push(ln);
  }

  /* 状态标签 */
  mSL=ce('text',{x:530,y:36,'text-anchor':'middle','font-family':'Rajdhani, sans-serif','font-weight':'700','font-size':'20',fill:'#f0a800',letterSpacing:'2'},svg);

  /* 伞形上下文图标 */
  const ug=ce('g',{transform:'translate(950,70)',opacity:'.3'},svg);
  ce('line',{x1:0,y1:-20,x2:0,y2:40,stroke:'#4a6a8a','stroke-width':'2'},ug);
  ce('path',{d:'M-45 8 Q-45 -18 0 -26 Q45 -18 45 8',fill:'none',stroke:'#4a6a8a','stroke-width':'1.5'},ug);
  ce('line',{x1:0,y1:-26,x2:-42,y2:6,stroke:'#4a6a8a','stroke-width':'1','stroke-dasharray':'3 2'},ug);
  ce('line',{x1:0,y1:-26,x2:42,y2:6,stroke:'#4a6a8a','stroke-width':'1','stroke-dasharray':'3 2'},ug);
  ce('line',{x1:0,y1:4,x2:-38,y2:5,stroke:'#f0a800','stroke-width':'2.5'},ug);
  const ut=ce('text',{x:0,y:56,'text-anchor':'middle',fill:'#4a6a8a','font-size':'8.5','font-family':'Share Tech Mono'},ug);
  ut.textContent='应用场景';

  /* IFR 资源注释条 */
  const nb=ce('g',{transform:'translate(530,390)'},svg);
  ce('rect',{x:-240,y:-14,width:480,height:28,rx:5,fill:'rgba(0,220,232,.05)',stroke:'rgba(0,220,232,.12)','stroke-width':'1'},nb);
  const nt2=ce('text',{x:0,y:2,'text-anchor':'middle',fill:'#00dce8','font-size':'10.5','font-family':'Share Tech Mono'},nb);
  nt2.textContent='IFR: 推力即资源 → 受压刚化 · 卸载柔化 · 零额外驱动';
}

/* ===== 细节视图引用 ===== */
let dSg=[], dTP=[], dER=[], dEL, dFA=[];

function buildDetail(){
  const svg=$('dSvg');
  const df=ce('defs',{},svg);
  const gf=ce('filter',{id:'dGw',x:'-50%',y:'-50%',width:'200%',height:'200%'},df);
  ce('feGaussianBlur',{in:'SourceGraphic',stdDeviation:'2.5',result:'b'},gf);
  const gm=ce('feMerge',{},gf); ce('feMergeNode',{in:'b'},gm); ce('feMergeNode',{in:'SourceGraphic'},gm);
  const mk=ce('marker',{id:'dAr',markerWidth:8,markerHeight:6,refX:8,refY:3,orient:'auto'},df);
  ce('polygon',{points:'0 0,8 3,0 6',fill:'#ff4444'},mk);

  /* 标题 */
  ce('text',{x:20,y:16,fill:'#5a6e88','font-size':'8.5','font-family':'Share Tech Mono'},svg).textContent='锯齿面(120°)';
  ce('text',{x:200,y:16,fill:'#5a6e88','font-size':'8.5','font-family':'Share Tech Mono'},svg).textContent='镍钛诺超弹丝 ⌀1.0mm';
  ce('text',{x:390,y:16,fill:'#5a6e88','font-size':'8.5','font-family':'Share Tech Mono'},svg).textContent='钛合金环体 5mm';

  /* 3个放大的节段 */
  const dW=88, dH=48, dTH=13;
  const bx0=65, by0=105, sp=10;
  for(let i=0;i<3;i++){
    const g=ce('g',{},svg);
    ce('rect',{x:0,y:0,width:dW,height:dH,rx:4,fill:'#16222e',stroke:'#2a3e55','stroke-width':'1.2'},g);
    /* 中心丝 */
    ce('circle',{cx:dW/2,cy:dH/2,r:3.5,fill:'none',stroke:'#2a3e55','stroke-width':'1'},g);
    ce('line',{x1:-16,y1:dH/2,x2:dW+16,y2:dH/2,stroke:'#00dce8','stroke-width':'1.5',opacity:'.45','stroke-dasharray':'4 3'},g);
    /* 锯齿路径 */
    const rp=ce('path',{fill:'none',stroke:'#2a3e55','stroke-width':'1.5'},g);
    const lp=ce('path',{fill:'none',stroke:'#2a3e55','stroke-width':'1.5'},g);
    /* 生成锯齿 */
    const nt=5, tw=dH/nt;
    let rd='M'+dW+' 0';
    for(let t=0;t<nt;t++){
      rd+=` L${dW+dTH} ${t*tw+tw/2} L${dW} ${(t+1)*tw}`;
    }
    rp.setAttribute('d',rd);
    let ld='M0 '+dH;
    for(let t=0;t<nt;t++){
      const y3=dH-t*tw, y2=y3-tw/2, y1=y3-tw;
      ld+=` L${-dTH} ${y2} L0 ${y1}`;
    }
    lp.setAttribute('d',ld);

    g.setAttribute('transform',`translate(${bx0+i*(dW+sp)},${by0})`);
    svg.appendChild(g);
    dSg.push({g,rp,lp,bx:bx0,by:by0,dW,dH,sp});
    dTP.push({rp,lp});
  }
  /* 啮合指示框 */
  for(let k=0;k<2;k++){
    const r=ce('rect',{x:0,y:by0-6,width:sp+6,height:dH+12,rx:3,fill:'none',stroke:'#f0a800','stroke-width':'1.5','stroke-dasharray':'4 2',opacity:'0'},svg);
    dER.push(r);
  }
  /* 力箭头 */
  dFA.push(ce('line',{x1:bx0-20,y1:by0+dH/2,x2:bx0-4,y2:by0+dH/2,stroke:'#ff4444','stroke-width':'2','marker-end':'url(#dAr)',opacity:'0'},svg));
  dFA.push(ce('line',{x1:0,y1:by0+dH/2,x2:0,y2:by0+dH/2,stroke:'#ff4444','stroke-width':'2','marker-end':'url(#dAr)',opacity:'0'},svg));
  /* 标签 */
  dEL=ce('text',{x:250,y:185,'text-anchor':'middle',fill:'#f0a800','font-size':'11','font-family':'Rajdhani, sans-serif','font-weight':'600'},svg);
}

/* ===== 更新主视图 ===== */
function updMain(){
  const ch=calcChain(openV);
  const {js,ss,ex,ey}=ch;

  /* 链节 */
  for(let i=0;i<N;i++){
    const s=ss[i], g=mSg[i];
    g.setAttribute('transform',`translate(${s.x.toFixed(1)},${s.y.toFixed(1)}) rotate(${s.a.toFixed(1)})`);
    const eng=s.e;
    const tc=eng>.5? `rgba(240,168,0,${lerp(.3,.95,(eng-.5)*2).toFixed(2)})` : `rgba(42,62,85,${lerp(.5,.25,eng*2).toFixed(2)})`;
    g.querySelectorAll('polygon').forEach(p=>p.setAttribute('fill',tc));
    const bd=g.querySelector('rect');
    bd.setAttribute('stroke',eng>.6?'#4a6040':'#2a3e55');
    bd.setAttribute('fill',eng>.6?'#182420':'#16222e');
  }

  /* 啮合点发光 */
  for(let i=0;i<N-1;i++){
    const j=js[i+1], c=mJg[i];
    c.setAttribute('cx',j.x.toFixed(1));
    c.setAttribute('cy',j.y.toFixed(1));
    c.setAttribute('opacity',openV>.55? lerp(0,.55,(openV-.55)/.45).toFixed(2) : '0');
  }

  /* 超弹丝 */
  let wd='M'+FX+' '+FY;
  for(let i=0;i<N;i++) wd+=` L${ss[i].x.toFixed(1)} ${ss[i].y.toFixed(1)}`;
  wd+=` L${ex.toFixed(1)} ${ey.toFixed(1)}`;
  mWire.setAttribute('d',wd);
  mWire.setAttribute('stroke',openV>.4?'#00dce8':'#1a3a4e');
  mWire.setAttribute('stroke-width',openV>.4?'2':'1.5');

  /* 推块 */
  mPB.setAttribute('transform',`translate(${ex.toFixed(1)},0)`);
  mPBL.setAttribute('x','0');
  mPR.setAttribute('x1',(ex+16).toFixed(1));
  mPR.setAttribute('x2',(ex+85).toFixed(1));

  /* 力箭头 */
  if(openV>.35){
    const ao=lerp(0,.65,(openV-.35)/.65).toFixed(2);
    for(let a=0;a<3;a++){
      const idx=Math.floor((a+1)*N/4);
      const s=ss[Math.min(idx,N-1)];
      const rad=s.a*Math.PI/180;
      const len=22;
      const x1=s.x-len*Math.cos(rad), y1=s.y-len*Math.sin(rad);
      const x2=s.x+len*Math.cos(rad), y2=s.y+len*Math.sin(rad);
      mFA[a].setAttribute('x1',x1.toFixed(1)); mFA[a].setAttribute('y1',y1.toFixed(1));
      mFA[a].setAttribute('x2',x2.toFixed(1)); mFA[a].setAttribute('y2',y2.toFixed(1));
      mFA[a].setAttribute('opacity',ao);
      mFA[a].setAttribute('stroke','#ff4444');
      mFA[a].setAttribute('marker-end','url(#aR)');
    }
  } else {
    mFA[0].setAttribute('opacity','0'); mFA[1].setAttribute('opacity','0'); mFA[2].setAttribute('opacity','0');
  }
  /* 拉力箭头 */
  if(openV<.45){
    const to=lerp(0,.6,(.45-openV)/.45).toFixed(2);
    mFA[3].setAttribute('x1',(ex+18).toFixed(1)); mFA[3].setAttribute('y1',RY);
    mFA[3].setAttribute('x2',(ex+55).toFixed(1)); mFA[3].setAttribute('y2',RY);
    mFA[3].setAttribute('opacity',to);
    mFA[3].setAttribute('stroke','#00dce8');
    mFA[3].setAttribute('marker-end','url(#aC)');
  } else {
    mFA[3].setAttribute('opacity','0');
  }

  /* 状态标签 */
  if(openV>.75){
    mSL.textContent='RIGID LOCK · 刚性锁定'; mSL.setAttribute('fill','#f0a800');
  } else if(openV<.25){
    mSL.textContent='FLEXIBLE RELEASE · 柔性释放'; mSL.setAttribute('fill','#ff4444');
  } else {
    mSL.textContent='TRANSITIONING · 过渡中'; mSL.setAttribute('fill','#00dce8');
  }
}

/* ===== 更新细节视图 ===== */
function updDetail(){
  const eng=openV;
  const gap=lerp(14,2,eng);
  const bx0=65, by0=105;
  for(let i=0;i<3;i++){
    const d=dSg[i];
    const bx=bx0+i*(d.dW+gap);
    d.g.setAttribute('transform',`translate(${bx.toFixed(1)},${d.by})`);
    const tc=eng>.5? `rgba(240,168,0,${lerp(.4,1,(eng-.5)*2).toFixed(2)})` : 'rgba(42,62,85,.6)';
    dTP[i].rp.setAttribute('stroke',tc);
    dTP[i].lp.setAttribute('stroke',tc);
    dTP[i].rp.setAttribute('stroke-width',eng>.5?'2':'1.2');
    dTP[i].lp.setAttribute('stroke-width',eng>.5?'2':'1.2');
    if(eng>.6){dTP[i].rp.setAttribute('filter','url(#dGw)');dTP[i].lp.setAttribute('filter','url(#dGw)');}
    else{dTP[i].rp.removeAttribute('filter');dTP[i].lp.removeAttribute('filter');}
  }
  dER.forEach((r,k)=>{
    r.setAttribute('x',(bx0+(k+1)*(dSg[0].dW+gap)-2).toFixed(1));
    r.setAttribute('opacity',eng>.5? lerp(0,.8,(eng-.5)*2).toFixed(2) : '0');
  });
  dFA[0].setAttribute('opacity',eng>.4? lerp(0,.7,(eng-.4)/.6).toFixed(2) : '0');
  const fx2=bx0+2*(dSg[0].dW+gap)+dSg[0].dW+4;
  dFA[1].setAttribute('x1',fx2.toFixed(1));
  dFA[1].setAttribute('x2',(fx2+16).toFixed(1));
  dFA[1].setAttribute('opacity',eng>.4? lerp(0,.7,(eng-.4)/.6).toFixed(2) : '0');

  if(eng>.7) dEL.textContent='锯齿紧密啮合 → 刚性传递弯矩';
  else if(eng<.3) dEL.textContent='锯齿脱开 → 链节自由旋转';
  else dEL.textContent='啮合度变化中...';
  dEL.setAttribute('fill',eng>.7?'#f0a800':eng<.3?'#ff4444':'#00dce8');
}

/* ===== 更新信息面板 ===== */
function updInfo(){
  const e=openV;
  const s1=$('s1'),s2=$('s2'),s3=$('s3'),s4=$('s4'),s5=$('s5');
  if(e>.75){s1.textContent='刚性锁定';s1.className='iv gd';}
  else if(e<.25){s1.textContent='柔性释放';s1.className='iv rd';}
  else{s1.textContent='过渡中';s1.className='iv cy';}
  s2.textContent=Math.round(e*100)+'%'; s2.className='iv '+(e>.5?'gd':'rd');
  s3.textContent=e>.5?'压缩 (推块上顶)':'拉伸 (推块下拉)'; s3.className='iv '+(e>.5?'gd':'rd');
  s4.textContent=e>.5?'张紧承压':'松弛随动'; s4.className='iv '+(e>.5?'gd':'rd');
  const dof=e<.08?360:Math.round(lerp(360,0,clamp(e*1.6,0,1)));
  s5.textContent=dof>=350?'全自由度':dof+'°'; s5.className='iv '+(dof>10?'rd':'gd');
}

/* ===== 全量更新 ===== */
function updAll(){updMain();updDetail();updInfo();}

/* ===== 动画主循环 ===== */
function tick(ts){
  if(prevTS===null) prevTS=ts;
  const dt=(ts-prevTS)/1000;
  prevTS=ts;
  if(autoOn){
    aTime+=dt*spd/100;
    openV=Math.cos(aTime*Math.PI*2)*.5+.5;
    $('openSlider').value=Math.round(openV*1000);
    updAll();
  }
  requestAnimationFrame(tick);
}

/* ===== 事件绑定 ===== */
function bind(){
  $('openSlider').addEventListener('input',function(){
    openV=parseInt(this.value)/1000;
    updAll();
  });
  $('bAuto').addEventListener('click',function(){
    autoOn=!autoOn;
    this.classList.toggle('on',autoOn);
    this.innerHTML=autoOn?'<i class="fa fa-pause"></i> 暂停演示':'<i class="fa fa-play"></i> 自动演示';
    if(autoOn){
      /* 从当前openV反算aTime,保证平滑衔接 */
      var cv=clamp(openV*2-1,-1,1);
      aTime=Math.acos(cv)/(Math.PI*2);
    }
  });
  $('bReset').addEventListener('click',function(){
    openV=1;autoOn=false;prevTS=null;
    $('openSlider').value=1000;
    $('bAuto').classList.remove('on');
    $('bAuto').innerHTML='<i class="fa fa-play"></i> 自动演示';
    updAll();
  });
  $('speedSlider').addEventListener('input',function(){spd=parseInt(this.value);});
}

/* ===== 启动 ===== */
buildMain();
buildDetail();
updAll();
bind();
requestAnimationFrame(tick);

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

修复要点说明:

  1. 动画循环修复:将 prevTS 初始值设为 null,第一帧时才初始化,避免首帧 dt 巨大导致跳变;requestAnimationFrame(tick) 放在函数末尾确保持续驱动。

  2. 滑块精度提升:将 min/max 改为 0/1000(千分位精度),使拖动时链节过渡更丝滑,不会出现卡顿感。

  3. 自动播放衔接:点击自动演示时,从当前 openV 反推 aTimeMath.acos(openV*2-1)),保证动画从当前状态平滑开始,不突兀跳变。

  4. 视觉反馈增强

    • 刚性态:链节边框变暗绿、锯齿三角变金色、啮合点发金光
    • 柔性态:链节变暗蓝灰、锯齿暗淡、红色拉力箭头出现
    • 过渡态:青色标签提示"过渡中"
  5. 交互验证:拖动滑块即可立即看到链节从水平刚性杆逐节弯折下垂为柔性链;点击自动演示按钮后,开合度在 0↔1 之间平滑循环往复。

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