分享图
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 rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Rajdhani:wght@300;500;700&family=Share+Tech+Mono&display=swap" rel="stylesheet">
<style>
  :root {
    --bg: #060a12;
    --grid: #0d1420;
    --steel: #6b7d96;
    --steel-light: #94a7bf;
    --cable: #c78c2e;
    --cable-dark: #8a5f1a;
    --water: #00e5ff;
    --water-deep: #0077b6;
    --measure: #ff8f00;
    --lock-red: #ff1744;
    --unlock-green: #00e676;
    --ifr-line: #76ff03;
    --panel-bg: #0b1120;
    --text: #d0dae8;
    --text-dim: #5a6e85;
    --accent: #00e5ff;
  }
  * { margin:0; padding:0; box-sizing:border-box; }
  body {
    background: var(--bg);
    color: var(--text);
    font-family: 'Rajdhani', sans-serif;
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    align-items: center;
    overflow-x: hidden;
  }
  .wrapper {
    width: 100%;
    max-width: 1500px;
    padding: 20px;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 16px;
  }
  .title-bar {
    display: flex;
    align-items: baseline;
    gap: 16px;
    width: 100%;
    padding: 0 8px;
  }
  .title-bar h1 {
    font-size: 22px;
    font-weight: 700;
    letter-spacing: 1px;
    color: var(--accent);
    text-transform: uppercase;
  }
  .title-bar .subtitle {
    font-size: 13px;
    color: var(--text-dim);
    font-family: 'Share Tech Mono', monospace;
  }
  .svg-container {
    width: 100%;
    border: 1px solid #1a2744;
    border-radius: 8px;
    overflow: hidden;
    background: var(--bg);
    box-shadow: 0 0 60px rgba(0,229,255,0.04), inset 0 0 80px rgba(0,0,0,0.4);
  }
  .svg-container svg {
    display: block;
    width: 100%;
    height: auto;
  }
  .controls {
    width: 100%;
    display: flex;
    flex-wrap: wrap;
    gap: 16px;
    align-items: stretch;
  }
  .ctrl-group {
    background: var(--panel-bg);
    border: 1px solid #1a2744;
    border-radius: 8px;
    padding: 14px 18px;
    flex: 1;
    min-width: 220px;
  }
  .ctrl-group label {
    display: block;
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 2px;
    color: var(--text-dim);
    margin-bottom: 8px;
    font-family: 'Share Tech Mono', monospace;
  }
  .ctrl-group .value-display {
    font-size: 28px;
    font-weight: 700;
    color: var(--accent);
    line-height: 1;
    margin-bottom: 6px;
  }
  .ctrl-group .value-display .unit {
    font-size: 14px;
    font-weight: 300;
    color: var(--text-dim);
    margin-left: 4px;
  }
  input[type=range] {
    -webkit-appearance: none;
    width: 100%;
    height: 6px;
    border-radius: 3px;
    background: #1a2744;
    outline: none;
  }
  input[type=range]::-webkit-slider-thumb {
    -webkit-appearance: none;
    width: 18px; height: 18px;
    border-radius: 50%;
    background: var(--accent);
    cursor: pointer;
    box-shadow: 0 0 8px rgba(0,229,255,0.5);
  }
  .btn-row {
    display: flex;
    gap: 8px;
    margin-top: 10px;
  }
  .btn {
    font-family: 'Share Tech Mono', monospace;
    font-size: 12px;
    padding: 6px 14px;
    border: 1px solid #2a3a55;
    border-radius: 4px;
    background: #111b2e;
    color: var(--text);
    cursor: pointer;
    transition: all .2s;
    text-transform: uppercase;
    letter-spacing: 1px;
  }
  .btn:hover { background: #1a2b48; border-color: var(--accent); color: #fff; }
  .btn.active { background: #0a3a5a; border-color: var(--accent); color: var(--accent); }
  .phase-indicator {
    display: flex;
    gap: 4px;
    margin-top: 8px;
  }
  .phase-dot {
    width: 10px; height: 10px;
    border-radius: 50%;
    background: #1a2744;
    transition: background .3s, box-shadow .3s;
  }
  .phase-dot.active {
    background: var(--accent);
    box-shadow: 0 0 8px var(--accent);
  }
  .phase-dot.red { background: var(--lock-red); box-shadow: 0 0 8px var(--lock-red); }
  .phase-dot.amber { background: var(--measure); box-shadow: 0 0 8px var(--measure); }
  .phase-dot.cyan { background: var(--water); box-shadow: 0 0 8px var(--water); }
  .phase-dot.green { background: var(--unlock-green); box-shadow: 0 0 8px var(--unlock-green); }
  @media (prefers-reduced-motion: reduce) {
    * { animation: none !important; transition: none !important; }
  }
</style>
</head>
<body>
<div class="wrapper">
  <div class="title-bar">
    <h1>IFR 原理动画</h1>
    <span class="subtitle">矩阵自定心滚轮 · 超高压水射流切割 — 5140mm跨度电缆精密切断</span>
  </div>

  <div class="svg-container">
    <svg id="mainSvg" viewBox="0 0 1400 820" xmlns="http://www.w3.org/2000/svg">
      <defs>
        <!-- 工程网格 -->
        <pattern id="gridSmall" width="20" height="20" patternUnits="userSpaceOnUse">
          <path d="M 20 0 L 0 0 0 20" fill="none" stroke="#0f1825" stroke-width="0.5"/>
        </pattern>
        <pattern id="gridLarge" width="100" height="100" patternUnits="userSpaceOnUse">
          <rect width="100" height="100" fill="url(#gridSmall)"/>
          <path d="M 100 0 L 0 0 0 100" fill="none" stroke="#141f30" stroke-width="1"/>
        </pattern>
        <!-- 金属渐变 -->
        <linearGradient id="steelGrad" x1="0" y1="0" x2="0" y2="1">
          <stop offset="0%" stop-color="#8fa4bd"/>
          <stop offset="40%" stop-color="#5a7089"/>
          <stop offset="100%" stop-color="#3a4f66"/>
        </linearGradient>
        <linearGradient id="rollerGrad" x1="0" y1="0" x2="1" y2="1">
          <stop offset="0%" stop-color="#a0b4cc"/>
          <stop offset="50%" stop-color="#6b82a0"/>
          <stop offset="100%" stop-color="#4a5f78"/>
        </linearGradient>
        <linearGradient id="cableGrad" x1="0" y1="0" x2="0" y2="1">
          <stop offset="0%" stop-color="#daa540"/>
          <stop offset="30%" stop-color="#c78c2e"/>
          <stop offset="70%" stop-color="#a06e1c"/>
          <stop offset="100%" stop-color="#7a5214"/>
        </linearGradient>
        <linearGradient id="waterGrad" x1="0" y1="0" x2="0" y2="1">
          <stop offset="0%" stop-color="#00e5ff" stop-opacity="0.9"/>
          <stop offset="50%" stop-color="#00b8d4" stop-opacity="0.7"/>
          <stop offset="100%" stop-color="#0077b6" stop-opacity="0.3"/>
        </linearGradient>
        <linearGradient id="nozzleGrad" x1="0" y1="0" x2="0" y2="1">
          <stop offset="0%" stop-color="#556680"/>
          <stop offset="100%" stop-color="#2a3a50"/>
        </linearGradient>
        <!-- 水射流发光 -->
        <filter id="waterGlow" x="-100%" y="-100%" width="300%" height="300%">
          <feGaussianBlur in="SourceGraphic" stdDeviation="4" result="blur"/>
          <feComposite in="SourceGraphic" in2="blur" operator="over"/>
        </filter>
        <filter id="strongGlow" x="-100%" y="-100%" width="300%" height="300%">
          <feGaussianBlur in="SourceGraphic" stdDeviation="8" result="blur"/>
          <feComposite in="SourceGraphic" in2="blur" operator="over"/>
        </filter>
        <filter id="softGlow" x="-50%" y="-50%" width="200%" height="200%">
          <feGaussianBlur in="SourceGraphic" stdDeviation="3" result="blur"/>
          <feMerge>
            <feMergeNode in="blur"/>
            <feMergeNode in="SourceGraphic"/>
          </feMerge>
        </filter>
        <filter id="ifrGlow" x="-50%" y="-50%" width="200%" height="200%">
          <feGaussianBlur in="SourceGraphic" stdDeviation="2" result="blur"/>
          <feMerge>
            <feMergeNode in="blur"/>
            <feMergeNode in="SourceGraphic"/>
          </feMerge>
        </filter>
        <!-- 锥形滚轮标记 (用于截面图) -->
        <clipPath id="crossClip">
          <rect x="1085" y="55" width="300" height="295" rx="6"/>
        </clipPath>
      </defs>

      <!-- 背景 -->
      <rect width="1400" height="820" fill="url(#gridLarge)"/>

      <!-- ===== 主体区域 ===== -->

      <!-- 理想中心面 — IFR核心视觉 -->
      <line id="ifrLine" x1="60" y1="430" x2="1070" y2="430"
            stroke="#76ff03" stroke-width="1.2" stroke-dasharray="12,6" opacity="0.7" filter="url(#ifrGlow)"/>
      <text x="65" y="422" fill="#76ff03" font-size="11" font-family="Share Tech Mono, monospace" opacity="0.9">
        IFR 理想中心面 — 恒定不变
      </text>
      <!-- 中心面公差标注 -->
      <line x1="60" y1="430" x2="60" y2="430" stroke="#76ff03" stroke-width="0.5" opacity="0.4"/>
      <text id="toleranceLabel" x="65" y="446" fill="#76ff03" font-size="9" font-family="Share Tech Mono, monospace" opacity="0.6">
        公差 ±0.2mm
      </text>

      <!-- 机架底座 -->
      <rect x="80" y="540" width="980" height="12" rx="2" fill="#1a2744" stroke="#2a3a55" stroke-width="0.5"/>
      <rect x="80" y="290" width="980" height="8" rx="2" fill="#1a2744" stroke="#2a3a55" stroke-width="0.5"/>

      <!-- 电缆 (动态高度) -->
      <g id="cableGroup">
        <rect id="cableBody" x="60" y="410" width="1000" height="40" rx="4" fill="url(#cableGrad)" opacity="0.95"/>
        <!-- 电缆芯线暗示 -->
        <line id="cableCoreLine" x1="60" y1="430" x2="1060" y2="430" stroke="#e8c35a" stroke-width="0.8" opacity="0.4"/>
      </g>

      <!-- 切割分离段 (初始隐藏) -->
      <rect id="cableCutRight" x="1000" y="410" width="60" height="40" rx="4" fill="url(#cableGrad)" opacity="0"/>

      <!-- ===== 滚轮站 1 ===== -->
      <g id="rollerStation1" transform="translate(200,0)">
        <!-- 弹簧/气缸连接 -->
        <line x1="-30" y1="310" x2="-30" y2="360" stroke="#2a3a55" stroke-width="3"/>
        <line x1="30" y1="310" x2="30" y2="360" stroke="#2a3a55" stroke-width="3"/>
        <line x1="-30" y1="500" x2="-30" y2="540" stroke="#2a3a55" stroke-width="3"/>
        <line x1="30" y1="500" x2="30" y2="540" stroke="#2a3a55" stroke-width="3"/>
        <!-- 弹簧符号 -->
        <path d="M-30,320 l5,5 l-10,6 l10,6 l-10,6 l10,6 l-5,5" fill="none" stroke="#4a6080" stroke-width="1.5"/>
        <path d="M30,320 l5,5 l-10,6 l10,6 l-10,6 l10,6 l-5,5" fill="none" stroke="#4a6080" stroke-width="1.5"/>
        <path d="M-30,535 l5,-5 l-10,-6 l10,-6 l-10,-6 l10,-6 l-5,-5" fill="none" stroke="#4a6080" stroke-width="1.5"/>
        <path d="M30,535 l5,-5 l-10,-6 l10,-6 l-10,-6 l10,-6 l-5,-5" fill="none" stroke="#4a6080" stroke-width="1.5"/>
        <!-- 上滚轮 -->
        <g id="upperRollers1">
          <circle cx="-14" cy="380" r="20" fill="url(#rollerGrad)" stroke="#8fa4bd" stroke-width="1.5"/>
          <circle cx="14" cy="380" r="20" fill="url(#rollerGrad)" stroke="#8fa4bd" stroke-width="1.5"/>
          <line x1="-14" y1="365" x2="-14" y2="395" stroke="#8fa4bd" stroke-width="0.5" opacity="0.5"/>
          <line x1="14" y1="365" x2="14" y2="395" stroke="#8fa4bd" stroke-width="0.5" opacity="0.5"/>
        </g>
        <!-- 下滚轮 -->
        <g id="lowerRollers1">
          <circle cx="-14" cy="480" r="20" fill="url(#rollerGrad)" stroke="#8fa4bd" stroke-width="1.5"/>
          <circle cx="14" cy="480" r="20" fill="url(#rollerGrad)" stroke="#8fa4bd" stroke-width="1.5"/>
          <line x1="-14" y1="465" x2="-14" y2="495" stroke="#8fa4bd" stroke-width="0.5" opacity="0.5"/>
          <line x1="14" y1="465" x2="14" y2="495" stroke="#8fa4bd" stroke-width="0.5" opacity="0.5"/>
        </g>
        <!-- 锁紧指示环 (初始隐藏) -->
        <circle id="lockRing1a" cx="0" cy="380" r="26" fill="none" stroke="var(--lock-red)" stroke-width="2" opacity="0"/>
        <circle id="lockRing1b" cx="0" cy="480" r="26" fill="none" stroke="var(--lock-red)" stroke-width="2" opacity="0"/>
        <text x="0" y="570" text-anchor="middle" fill="#5a6e85" font-size="10" font-family="Share Tech Mono, monospace">定心站 I</text>
      </g>

      <!-- ===== 滚轮站 2 ===== -->
      <g id="rollerStation2" transform="translate(380,0)">
        <line x1="-30" y1="310" x2="-30" y2="360" stroke="#2a3a55" stroke-width="3"/>
        <line x1="30" y1="310" x2="30" y2="360" stroke="#2a3a55" stroke-width="3"/>
        <line x1="-30" y1="500" x2="-30" y2="540" stroke="#2a3a55" stroke-width="3"/>
        <line x1="30" y1="500" x2="30" y2="540" stroke="#2a3a55" stroke-width="3"/>
        <path d="M-30,320 l5,5 l-10,6 l10,6 l-10,6 l10,6 l-5,5" fill="none" stroke="#4a6080" stroke-width="1.5"/>
        <path d="M30,320 l5,5 l-10,6 l10,6 l-10,6 l10,6 l-5,5" fill="none" stroke="#4a6080" stroke-width="1.5"/>
        <path d="M-30,535 l5,-5 l-10,-6 l10,-6 l-10,-6 l10,-6 l-5,-5" fill="none" stroke="#4a6080" stroke-width="1.5"/>
        <path d="M30,535 l5,-5 l-10,-6 l10,-6 l-10,-6 l10,-6 l-5,-5" fill="none" stroke="#4a6080" stroke-width="1.5"/>
        <g id="upperRollers2">
          <circle cx="-14" cy="380" r="20" fill="url(#rollerGrad)" stroke="#8fa4bd" stroke-width="1.5"/>
          <circle cx="14" cy="380" r="20" fill="url(#rollerGrad)" stroke="#8fa4bd" stroke-width="1.5"/>
          <line x1="-14" y1="365" x2="-14" y2="395" stroke="#8fa4bd" stroke-width="0.5" opacity="0.5"/>
          <line x1="14" y1="365" x2="14" y2="395" stroke="#8fa4bd" stroke-width="0.5" opacity="0.5"/>
        </g>
        <g id="lowerRollers2">
          <circle cx="-14" cy="480" r="20" fill="url(#rollerGrad)" stroke="#8fa4bd" stroke-width="1.5"/>
          <circle cx="14" cy="480" r="20" fill="url(#rollerGrad)" stroke="#8fa4bd" stroke-width="1.5"/>
          <line x1="-14" y1="465" x2="-14" y2="495" stroke="#8fa4bd" stroke-width="0.5" opacity="0.5"/>
          <line x1="14" y1="465" x2="14" y2="495" stroke="#8fa4bd" stroke-width="0.5" opacity="0.5"/>
        </g>
        <circle id="lockRing2a" cx="0" cy="380" r="26" fill="none" stroke="var(--lock-red)" stroke-width="2" opacity="0"/>
        <circle id="lockRing2b" cx="0" cy="480" r="26" fill="none" stroke="var(--lock-red)" stroke-width="2" opacity="0"/>
        <text x="0" y="570" text-anchor="middle" fill="#5a6e85" font-size="10" font-family="Share Tech Mono, monospace">定心站 II</text>
      </g>

      <!-- ===== 滚轮站 3 ===== -->
      <g id="rollerStation3" transform="translate(560,0)">
        <line x1="-30" y1="310" x2="-30" y2="360" stroke="#2a3a55" stroke-width="3"/>
        <line x1="30" y1="310" x2="30" y2="360" stroke="#2a3a55" stroke-width="3"/>
        <line x1="-30" y1="500" x2="-30" y2="540" stroke="#2a3a55" stroke-width="3"/>
        <line x1="30" y1="500" x2="30" y2="540" stroke="#2a3a55" stroke-width="3"/>
        <path d="M-30,320 l5,5 l-10,6 l10,6 l-10,6 l10,6 l-5,5" fill="none" stroke="#4a6080" stroke-width="1.5"/>
        <path d="M30,320 l5,5 l-10,6 l10,6 l-10,6 l10,6 l-5,5" fill="none" stroke="#4a6080" stroke-width="1.5"/>
        <path d="M-30,535 l5,-5 l-10,-6 l10,-6 l-10,-6 l10,-6 l-5,-5" fill="none" stroke="#4a6080" stroke-width="1.5"/>
        <path d="M30,535 l5,-5 l-10,-6 l10,-6 l-10,-6 l10,-6 l-5,-5" fill="none" stroke="#4a6080" stroke-width="1.5"/>
        <g id="upperRollers3">
          <circle cx="-14" cy="380" r="20" fill="url(#rollerGrad)" stroke="#8fa4bd" stroke-width="1.5"/>
          <circle cx="14" cy="380" r="20" fill="url(#rollerGrad)" stroke="#8fa4bd" stroke-width="1.5"/>
          <line x1="-14" y1="365" x2="-14" y2="395" stroke="#8fa4bd" stroke-width="0.5" opacity="0.5"/>
          <line x1="14" y1="365" x2="14" y2="395" stroke="#8fa4bd" stroke-width="0.5" opacity="0.5"/>
        </g>
        <g id="lowerRollers3">
          <circle cx="-14" cy="480" r="20" fill="url(#rollerGrad)" stroke="#8fa4bd" stroke-width="1.5"/>
          <circle cx="14" cy="480" r="20" fill="url(#rollerGrad)" stroke="#8fa4bd" stroke-width="1.5"/>
          <line x1="-14" y1="465" x2="-14" y2="495" stroke="#8fa4bd" stroke-width="0.5" opacity="0.5"/>
          <line x1="14" y1="465" x2="14" y2="495" stroke="#8fa4bd" stroke-width="0.5" opacity="0.5"/>
        </g>
        <circle id="lockRing3a" cx="0" cy="380" r="26" fill="none" stroke="var(--lock-red)" stroke-width="2" opacity="0"/>
        <circle id="lockRing3b" cx="0" cy="480" r="26" fill="none" stroke="var(--lock-red)" stroke-width="2" opacity="0"/>
        <text x="0" y="570" text-anchor="middle" fill="#5a6e85" font-size="10" font-family="Share Tech Mono, monospace">定心站 III</text>
      </g>

      <!-- ===== 光栅尺 ===== -->
      <g id="gratingGroup" transform="translate(720,0)">
        <!-- 光栅尺标尺 -->
        <rect x="-4" y="295" width="8" height="245" rx="2" fill="#1a2744" stroke="#2a3a55" stroke-width="0.5"/>
        <!-- 刻度 -->
        <g stroke="#3a5070" stroke-width="0.5" opacity="0.7">
          <line x1="-12" y1="310" x2="-4" y2="310"/>
          <line x1="-12" y1="330" x2="-4" y2="330"/>
          <line x1="-12" y1="350" x2="-4" y2="350"/>
          <line x1="-12" y1="370" x2="-4" y2="370"/>
          <line x1="-16" y1="390" x2="-4" y2="390"/>
          <line x1="-12" y1="410" x2="-4" y2="410"/>
          <line x1="-12" y1="430" x2="-4" y2="430"/>
          <line x1="-12" y1="450" x2="-4" y2="450"/>
          <line x1="-12" y1="470" x2="-4" y2="470"/>
          <line x1="-16" y1="490" x2="-4" y2="490"/>
          <line x1="-12" y1="510" x2="-4" y2="510"/>
          <line x1="-12" y1="530" x2="-4" y2="530"/>
        </g>
        <!-- 光栅读数头 -->
        <rect id="gratingHead" x="-20" y="420" width="16" height="20" rx="2" fill="#2a3a55" stroke="#ff8f00" stroke-width="1"/>
        <circle id="gratingDot" cx="-12" cy="430" r="3" fill="#ff8f00" opacity="0.5"/>
        <!-- 1m标记线 -->
        <line id="gratingTriggerLine" x1="4" y1="295" x2="4" y2="540" stroke="#ff8f00" stroke-width="1" stroke-dasharray="4,4" opacity="0"/>
        <text x="14" y="302" fill="#5a6e85" font-size="9" font-family="Share Tech Mono, monospace">光栅尺</text>
        <text x="14" y="314" fill="#3a5070" font-size="8" font-family="Share Tech Mono, monospace">分辨率 0.01mm</text>
        <!-- 测量值显示 -->
        <rect x="10" y="380" width="70" height="24" rx="3" fill="#0d1420" stroke="#2a3a55" stroke-width="0.5"/>
        <text id="gratingValue" x="45" y="397" text-anchor="middle" fill="#ff8f00" font-size="13" font-family="Share Tech Mono, monospace">0.000 m</text>
      </g>

      <!-- ===== 水射流切割站 ===== -->
      <g id="cutterGroup" transform="translate(940,0)">
        <!-- 密封排污舱 -->
        <rect x="-55" y="295" width="110" height="260" rx="6" fill="none" stroke="#1e3050" stroke-width="1.5" stroke-dasharray="6,3"/>
        <text x="0" y="580" text-anchor="middle" fill="#3a5070" font-size="8" font-family="Share Tech Mono, monospace">密封排污舱</text>

        <!-- 喷嘴支架 -->
        <rect x="-10" y="290" width="20" height="80" rx="2" fill="url(#nozzleGrad)" stroke="#4a5f78" stroke-width="1"/>
        <!-- 喷嘴 -->
        <polygon id="nozzleTip" points="-6,370 6,370 2,385 -2,385" fill="#2a3a50" stroke="#5a7089" stroke-width="1"/>
        <circle cx="0" cy="375" r="2.5" fill="#001a33"/>

        <!-- 水射流 (初始隐藏) -->
        <g id="waterJetGroup" opacity="0">
          <line id="waterStream" x1="0" y1="385" x2="0" y2="475" stroke="url(#waterGrad)" stroke-width="2.5" filter="url(#waterGlow)"/>
          <!-- 水射流核心 -->
          <line x1="0" y1="385" x2="0" y2="475" stroke="#80f0ff" stroke-width="0.8" opacity="0.9"/>
          <!-- 飞溅粒子容器 -->
          <g id="splashParticles"></g>
        </g>

        <!-- 扫描移动轨道 -->
        <line x1="-40" y1="375" x2="40" y2="375" stroke="#1e3050" stroke-width="1" stroke-dasharray="3,3"/>
        <text x="0" y="285" text-anchor="middle" fill="#5a6e85" font-size="9" font-family="Share Tech Mono, monospace">超高压水射流</text>
        <text x="0" y="272" text-anchor="middle" fill="#3a5070" font-size="8" font-family="Share Tech Mono, monospace">3000-4000 bar</text>
      </g>

      <!-- ===== 截面图 (右上角) ===== -->
      <g id="crossSectionGroup">
        <rect x="1085" y="55" width="300" height="295" rx="6" fill="#080e18" stroke="#1e3050" stroke-width="1"/>
        <text x="1235" y="76" text-anchor="middle" fill="#5a6e85" font-size="10" font-family="Share Tech Mono, monospace" letter-spacing="2">截面视图 — 锥面自定心</text>

        <!-- 截面中心参考线 -->
        <line x1="1100" y1="210" x2="1370" y2="210" stroke="#76ff03" stroke-width="0.8" stroke-dasharray="6,4" opacity="0.4"/>
        <text x="1368" y="206" text-anchor="end" fill="#76ff03" font-size="8" font-family="Share Tech Mono, monospace" opacity="0.6">中心</text>

        <!-- 截面电缆 -->
        <circle id="crossCable" cx="1235" cy="210" r="25" fill="url(#cableGrad)" stroke="#c78c2e" stroke-width="0.5" opacity="0.9"/>
        <circle id="crossCableCore" cx="1235" cy="210" r="5" fill="#e8c35a" opacity="0.4"/>

        <!-- 4个锥形滚轮 (30度锥角) -->
        <g id="crossRollers">
          <!-- 左上 -->
          <polygon id="crLU" points="1200,175 1210,175 1215,205 1205,205" fill="url(#rollerGrad)" stroke="#8fa4bd" stroke-width="1" opacity="0.95"/>
          <!-- 左下 -->
          <polygon id="crLD" points="1200,245 1210,245 1205,215 1215,215" fill="url(#rollerGrad)" stroke="#8fa4bd" stroke-width="1" opacity="0.95"/>
          <!-- 右上 -->
          <polygon id="crRU" points="1260,175 1270,175 1265,205 1255,205" fill="url(#rollerGrad)" stroke="#8fa4bd" stroke-width="1" opacity="0.95"/>
          <!-- 右下 -->
          <polygon id="crRD" points="1260,245 1270,245 1255,215 1265,215" fill="url(#rollerGrad)" stroke="#8fa4bd" stroke-width="1" opacity="0.95"/>
        </g>

        <!-- 锥角标注 -->
        <path d="M1200,180 A20,20 0 0,1 1210,175" fill="none" stroke="#ff8f00" stroke-width="0.8" opacity="0.7"/>
        <text x="1188" y="180" fill="#ff8f00" font-size="8" font-family="Share Tech Mono, monospace" opacity="0.7">30°</text>
        <path d="M1270,180 A20,20 0 0,0 1260,175" fill="none" stroke="#ff8f00" stroke-width="0.8" opacity="0.7"/>
        <text x="1274" y="180" fill="#ff8f00" font-size="8" font-family="Share Tech Mono, monospace" opacity="0.7">30°</text>

        <!-- 弹簧/气缸示意 -->
        <line x1="1175" y1="190" x2="1200" y2="190" stroke="#4a6080" stroke-width="1.5"/>
        <path d="M1175,190 l3,-4 l6,8 l6,-8 l6,8 l3,-4" fill="none" stroke="#4a6080" stroke-width="1"/>
        <line x1="1175" y1="230" x2="1200" y2="230" stroke="#4a6080" stroke-width="1.5"/>
        <path d="M1175,230 l3,-4 l6,8 l6,-8 l6,8 l3,-4" fill="none" stroke="#4a6080" stroke-width="1"/>
        <line x1="1270" y1="190" x2="1295" y2="190" stroke="#4a6080" stroke-width="1.5"/>
        <path d="M1295,190 l-3,-4 l-6,8 l-6,-8 l-6,8 l-3,-4" fill="none" stroke="#4a6080" stroke-width="1"/>
        <line x1="1270" y1="230" x2="1295" y2="230" stroke="#4a6080" stroke-width="1.5"/>
        <path d="M1295,230 l-3,-4 l-6,8 l-6,-8 l-6,8 l-3,-4" fill="none" stroke="#4a6080" stroke-width="1"/>

        <!-- 双向抱拢箭头 -->
        <g id="clampArrows" opacity="0.6">
          <polygon points="1195,210 1200,206 1200,214" fill="#ff8f00"/>
          <polygon points="1275,210 1270,206 1270,214" fill="#ff8f00"/>
        </g>

        <!-- 直径标注 -->
        <line id="crossDimLine" x1="1235" y1="185" x2="1235" y2="235" stroke="#5a6e85" stroke-width="0.5" stroke-dasharray="2,2"/>
        <text id="crossDimLabel" x="1252" y="214" fill="#5a6e85" font-size="9" font-family="Share Tech Mono, monospace">φ40</text>
      </g>

      <!-- ===== 参数面板 ===== -->
      <g id="paramPanel">
        <rect x="1085" y="370" width="300" height="190" rx="6" fill="#080e18" stroke="#1e3050" stroke-width="1"/>
        <text x="1235" y="392" text-anchor="middle" fill="#5a6e85" font-size="10" font-family="Share Tech Mono, monospace" letter-spacing="2">实时参数</text>
        <line x1="1100" y1="400" x2="1370" y2="400" stroke="#1e3050" stroke-width="0.5"/>

        <text x="1105" y="422" fill="#5a6e85" font-size="10" font-family="Share Tech Mono, monospace">水射流压力</text>
        <text id="paramPressure" x="1365" y="422" text-anchor="end" fill="#00e5ff" font-size="11" font-family="Share Tech Mono, monospace">3500 bar</text>

        <text x="1105" y="446" fill="#5a6e85" font-size="10" font-family="Share Tech Mono, monospace">电缆直径</text>
        <text id="paramDiameter" x="1365" y="446" text-anchor="end" fill="#c78c2e" font-size="11" font-family="Share Tech Mono, monospace">40 mm</text>

        <text x="1105" y="470" fill="#5a6e85" font-size="10" font-family="Share Tech Mono, monospace">中心高偏差</text>
        <text id="paramCenterDev" x="1365" y="470" text-anchor="end" fill="#76ff03" font-size="11" font-family="Share Tech Mono, monospace">±0.00 mm</text>

        <text x="1105" y="494" fill="#5a6e85" font-size="10" font-family="Share Tech Mono, monospace">滚轮锥角</text>
        <text x="1365" y="494" text-anchor="end" fill="#8fa4bd" font-size="11" font-family="Share Tech Mono, monospace">30°</text>

        <text x="1105" y="518" fill="#5a6e85" font-size="10" font-family="Share Tech Mono, monospace">当前阶段</text>
        <text id="paramPhase" x="1365" y="518" text-anchor="end" fill="#00e676" font-size="11" font-family="Share Tech Mono, monospace">送料定心</text>

        <text x="1105" y="546" fill="#5a6e85" font-size="10" font-family="Share Tech Mono, monospace">侧向挤压力</text>
        <text id="paramForce" x="1365" y="546" text-anchor="end" fill="#76ff03" font-size="11" font-family="Share Tech Mono, monospace">0 N (IFR)</text>
      </g>

      <!-- ===== IFR理念标注 ===== -->
      <g id="ifrAnnotation">
        <rect x="1085" y="580" width="300" height="90" rx="6" fill="#080e18" stroke="#76ff03" stroke-width="0.5" opacity="0.8"/>
        <text x="1100" y="602" fill="#76ff03" font-size="11" font-weight="700" font-family="Rajdhani, sans-serif">IFR 最终理想解</text>
        <text x="1100" y="620" fill="#5a8060" font-size="9" font-family="Share Tech Mono, monospace">矛盾: 夹紧定位 vs 不压扁变形</text>
        <text x="1100" y="636" fill="#5a8060" font-size="9" font-family="Share Tech Mono, monospace">解: 锥面几何自定心 → 自适应消除矛盾</text>
        <text x="1100" y="652" fill="#5a8060" font-size="9" font-family="Share Tech Mono, monospace">     水射流代替切刀 → 零侧向力切断</text>
      </g>

      <!-- 状态指示条 -->
      <rect x="80" y="620" width="980" height="32" rx="4" fill="#0b1120" stroke="#1e3050" stroke-width="0.5"/>
      <g id="statusBar">
        <text x="90" y="641" fill="#5a6e85" font-size="10" font-family="Share Tech Mono, monospace">流程:</text>
        <!-- 各阶段标签 -->
        <text id="stFeed" x="150" y="641" fill="#00e676" font-size="10" font-family="Share Tech Mono, monospace" opacity="0.3">送料定心</text>
        <text x="235" y="641" fill="#2a3a55" font-size="10" font-family="Share Tech Mono, monospace">→</text>
        <text id="stMeasure" x="255" y="641" fill="#ff8f00" font-size="10" font-family="Share Tech Mono, monospace" opacity="0.3">光栅测量</text>
        <text x="340" y="641" fill="#2a3a55" font-size="10" font-family="Share Tech Mono, monospace">→</text>
        <text id="stLock" x="360" y="641" fill="#ff1744" font-size="10" font-family="Share Tech Mono, monospace" opacity="0.3">滚轮锁死</text>
        <text x="445" y="641" fill="#2a3a55" font-size="10" font-family="Share Tech Mono, monospace">→</text>
        <text id="stCut" x="465" y="641" fill="#00e5ff" font-size="10" font-family="Share Tech Mono, monospace" opacity="0.3">水射流切断</text>
        <text x="560" y="641" fill="#2a3a55" font-size="10" font-family="Share Tech Mono, monospace">→</text>
        <text id="stUnlock" x="580" y="641" fill="#00e676" font-size="10" font-family="Share Tech Mono, monospace" opacity="0.3">解锁送料</text>

        <!-- 进度条 -->
        <rect x="680" y="630" width="360" height="8" rx="4" fill="#1a2744"/>
        <rect id="progressBar" x="680" y="630" width="0" height="8" rx="4" fill="#00e5ff" opacity="0.7"/>
      </g>

      <!-- 送料方向箭头 -->
      <g id="feedArrow" opacity="0.5">
        <polygon points="80,430 95,422 95,438" fill="#00e676"/>
        <text x="68" y="460" fill="#5a6e85" font-size="8" font-family="Share Tech Mono, monospace">进料</text>
      </g>

      <!-- 切断效果指示 -->
      <g id="cutIndicator" opacity="0">
        <line x1="940" y1="400" x2="940" y2="460" stroke="#00e5ff" stroke-width="1.5" filter="url(#softGlow)"/>
        <text x="955" y="400" fill="#00e5ff" font-size="9" font-family="Share Tech Mono, monospace">切断线</text>
      </g>

      <!-- 无侧向力标注 -->
      <g id="noForceLabel" opacity="0">
        <text x="980" y="500" fill="#76ff03" font-size="9" font-family="Share Tech Mono, monospace">侧向力 = 0</text>
        <text x="980" y="512" fill="#4a6a50" font-size="8" font-family="Share Tech Mono, monospace">无挤压变形</text>
      </g>

      <!-- 滚轮旋转动画指示 (动态由JS控制) -->
      <g id="rotationIndicators"></g>

    </svg>
  </div>

  <!-- 控制面板 -->
  <div class="controls">
    <div class="ctrl-group" style="flex:1.5">
      <label>电缆直径</label>
      <div class="value-display"><span id="diameterDisplay">40</span><span class="unit">mm</span></div>
      <input type="range" id="diameterSlider" min="5" max="140" value="40" step="1">
      <div style="display:flex;justify-content:space-between;font-size:9px;color:#3a5070;font-family:'Share Tech Mono',monospace;margin-top:2px">
        <span>5mm 细缆</span><span>140mm 粗缆</span>
      </div>
    </div>
    <div class="ctrl-group" style="flex:1">
      <label>水射流压力</label>
      <div class="value-display"><span id="pressureDisplay">3500</span><span class="unit">bar</span></div>
      <input type="range" id="pressureSlider" min="3000" max="4000" value="3500" step="50">
      <div style="display:flex;justify-content:space-between;font-size:9px;color:#3a5070;font-family:'Share Tech Mono',monospace;margin-top:2px">
        <span>3000</span><span>4000</span>
      </div>
    </div>
    <div class="ctrl-group" style="flex:0.8">
      <label>动画控制</label>
      <div class="btn-row">
        <button class="btn active" id="btnPlay" onclick="togglePlay()">暂停</button>
        <button class="btn" id="btnReset" onclick="resetAnim()">重置</button>
        <button class="btn" id="btnStep" onclick="stepPhase()">步进</button>
      </div>
      <div class="phase-indicator" style="margin-top:12px">
        <div class="phase-dot" id="pd0"></div>
        <div class="phase-dot" id="pd1"></div>
        <div class="phase-dot" id="pd2"></div>
        <div class="phase-dot" id="pd3"></div>
        <div class="phase-dot" id="pd4"></div>
      </div>
      <div style="font-size:8px;color:#3a5070;font-family:'Share Tech Mono',monospace;margin-top:4px">
        送料 → 测量 → 锁死 → 切断 → 解锁
      </div>
    </div>
    <div class="ctrl-group" style="flex:0.7">
      <label>动画速度</label>
      <div class="value-display" style="font-size:20px"><span id="speedDisplay">1.0</span><span class="unit">x</span></div>
      <input type="range" id="speedSlider" min="0.3" max="3" value="1" step="0.1">
    </div>
  </div>
</div>

<script>
// ============== 配置 ==============
const CABLE_Y = 430;         // 中心线Y坐标
const ROLLER_R = 20;         // 滚轮半径
const PHASES = ['FEED','MEASURE','LOCK','CUT','UNLOCK'];
const PHASE_DURATIONS = [4000, 1200, 600, 2800, 800]; // 毫秒

// ============== 状态 ==============
let cableDiameter = 40;
let waterPressure = 3500;
let animSpeed = 1.0;
let playing = true;
let currentPhaseIdx = 0;
let phaseTime = 0;
let lastTimestamp = 0;
let totalCycleTime = PHASE_DURATIONS.reduce((a,b)=>a+b,0);
let feedProgress = 0;    // 0-1 送料进度
let measureFlash = 0;    // 测量闪烁强度
let lockIntensity = 0;   // 锁紧强度 0-1
let cutProgress = 0;     // 切割进度 0-1
let unlockProgress = 0;  // 解锁进度
let nozzleX = 0;         // 喷嘴X偏移
let particles = [];       // 飞溅粒子
let rollerAngle = 0;     // 滚轮旋转角

// ============== DOM引用 ==============
const svg = document.getElementById('mainSvg');
const cableBody = document.getElementById('cableBody');
const cableCoreLine = document.getElementById('cableCoreLine');
const cableCutRight = document.getElementById('cableCutRight');
const waterJetGroup = document.getElementById('waterJetGroup');
const waterStream = document.getElementById('waterStream');
const splashGroup = document.getElementById('splashParticles');
const gratingValue = document.getElementById('gratingValue');
const gratingDot = document.getElementById('gratingDot');
const gratingHead = document.getElementById('gratingHead');
const gratingTriggerLine = document.getElementById('gratingTriggerLine');
const cutIndicator = document.getElementById('cutIndicator');
const noForceLabel = document.getElementById('noForceLabel');
const crossCable = document.getElementById('crossCable');
const crossCableCore = document.getElementById('crossCableCore');
const crossDimLabel = document.getElementById('crossDimLabel');
const progressBar = document.getElementById('progressBar');
const ifrLine = document.getElementById('ifrLine');

// 滚轮组引用
const rollerStations = [1,2,3].map(i => ({
  upper: document.getElementById(`upperRollers${i}`),
  lower: document.getElementById(`lowerRollers${i}`),
  lockA: document.getElementById(`lockRing${i}a`),
  lockB: document.getElementById(`lockRing${i}b`),
}));

// 截面滚轮
const crLU = document.getElementById('crLU');
const crLD = document.getElementById('crLD');
const crRU = document.getElementById('crRU');
const crRD = document.getElementById('crRD');

// 状态文字
const statusTexts = [
  document.getElementById('stFeed'),
  document.getElementById('stMeasure'),
  document.getElementById('stLock'),
  document.getElementById('stCut'),
  document.getElementById('stUnlock'),
];
const phaseDots = [0,1,2,3,4].map(i => document.getElementById(`pd${i}`));

// 参数面板
const paramPressure = document.getElementById('paramPressure');
const paramDiameter = document.getElementById('paramDiameter');
const paramCenterDev = document.getElementById('paramCenterDev');
const paramPhase = document.getElementById('paramPhase');
const paramForce = document.getElementById('paramForce');

// ============== 工具函数 ==============
function lerp(a,b,t) { return a + (b-a) * Math.min(1, Math.max(0, t)); }
function mapRange(v, inMin, inMax, outMin, outMax) {
  return outMin + (v - inMin) / (inMax - inMin) * (outMax - outMin);
}
// 电缆直径 → SVG高度
function diaToSvgHeight(d) { return mapRange(d, 5, 140, 8, 90); }
// 电缆直径 → 截面半径
function diaToCrossRadius(d) { return mapRange(d, 5, 140, 6, 55); }

// ============== 更新函数 ==============
function updateCableVisuals() {
  const h = diaToSvgHeight(cableDiameter);
  const y = CABLE_Y - h/2;

  // 主体电缆
  cableBody.setAttribute('y', y);
  cableBody.setAttribute('height', h);
  cableCoreLine.setAttribute('y1', CABLE_Y);
  cableCoreLine.setAttribute('y2', CABLE_Y);

  // 切断后右侧段
  cableCutRight.setAttribute('y', y);
  cableCutRight.setAttribute('height', h);

  // 更新滚轮位置
  rollerStations.forEach(rs => {
    const upperY = CABLE_Y - h/2 - ROLLER_R + 2;
    const lowerY = CABLE_Y + h/2 + ROLLER_R - 2;
    rs.upper.setAttribute('transform', `translate(0, ${upperY - 380})`);
    rs.lower.setAttribute('transform', `translate(0, ${lowerY - 480})`);
    rs.lockA.setAttribute('cy', upperY);
    rs.lockB.setAttribute('cy', lowerY);
  });

  // 截面图
  const cr = diaToCrossRadius(cableDiameter);
  crossCable.setAttribute('r', cr);
  crossCableCore.setAttribute('r', Math.max(2, cr * 0.2));
  crossDimLabel.textContent = `φ${cableDiameter}`;

  // 截面滚轮位置 - 锥面30度自定心
  const coneOffset = cr + 8; // 滚轮尖角到中心的距离
  const coneWidth = 12;
  const coneDepth = cr * 0.8;
  // 左上滚轮
  crLU.setAttribute('points',
    `${1235-coneOffset-coneWidth},${210-coneDepth} ${1235-coneOffset+coneWidth},${210-coneDepth} ${1235-coneOffset+2},${210+4} ${1235-coneOffset-2},${210+4}`);
  // 左下滚轮
  crLD.setAttribute('points',
    `${1235-coneOffset-coneWidth},${210+coneDepth} ${1235-coneOffset+coneWidth},${210+coneDepth} ${1235-coneOffset+2},${210-4} ${1235-coneOffset-2},${210-4}`);
  // 右上滚轮
  crRU.setAttribute('points',
    `${1235+coneOffset+coneWidth},${210-coneDepth} ${1235+coneOffset-coneWidth},${210-coneDepth} ${1235+coneOffset-2},${210+4} ${1235+coneOffset+2},${210+4}`);
  // 右下滚轮
  crRD.setAttribute('points',
    `${1235+coneOffset+coneWidth},${210+coneDepth} ${1235+coneOffset-coneWidth},${210+coneDepth} ${1235+coneOffset-2},${210-4} ${1235+coneOffset+2},${210-4}`);

  // 参数面板
  paramDiameter.textContent = `${cableDiameter} mm`;
}

function updatePhaseVisuals(dt) {
  const phase = PHASES[currentPhaseIdx];
  const duration = PHASE_DURATIONS[currentPhaseIdx];
  const progress = Math.min(1, phaseTime / duration);

  // 重置所有状态文字
  statusTexts.forEach((st, i) => {
    st.setAttribute('opacity', i === currentPhaseIdx ? '1' : '0.3');
  });
  phaseDots.forEach((pd, i) => {
    pd.className = 'phase-dot';
    if (i === currentPhaseIdx) {
      const colors = ['active','amber','red','cyan','green'];
      pd.classList.add(colors[i]);
    }
  });

  // 进度条
  let totalProg = 0;
  for (let i = 0; i < currentPhaseIdx; i++) totalProg += PHASE_DURATIONS[i];
  totalProg += phaseTime;
  const barW = (totalProg / totalCycleTime) * 360;
  progressBar.setAttribute('width', barW);
  const barColors = ['#00e676','#ff8f00','#ff1744','#00e5ff','#00e676'];
  progressBar.setAttribute('fill', barColors[currentPhaseIdx]);

  // 阶段特定视觉
  switch(phase) {
    case 'FEED':
      feedProgress = progress;
      lockIntensity = lerp(lockIntensity, 0, 0.1);
      waterJetGroup.setAttribute('opacity', '0');
      cutIndicator.setAttribute('opacity', '0');
      noForceLabel.setAttribute('opacity', '0');
      gratingTriggerLine.setAttribute('opacity', '0');
      gratingDot.setAttribute('opacity', '0.5');
      paramPhase.textContent = '送料定心';
      paramPhase.setAttribute('fill', '#00e676');
      paramForce.textContent = '0 N (IFR)';
      // 滚轮旋转
      rollerAngle += dt * 0.003 * animSpeed;
      break;

    case 'MEASURE':
      measureFlash = Math.sin(phaseTime * 0.01) * 0.5 + 0.5;
      gratingTriggerLine.setAttribute('opacity', measureFlash * 0.8);
      gratingDot.setAttribute('opacity', 0.5 + measureFlash * 0.5);
      gratingHead.setAttribute('stroke', `rgba(255,143,0,${0.5 + measureFlash * 0.5})`);
      paramPhase.textContent = '光栅触发';
      paramPhase.setAttribute('fill', '#ff8f00');
      // 光栅值递增到1m
      const mVal = (progress * 1.0).toFixed(3);
      gratingValue.textContent = `${mVal} m`;
      if (progress > 0.95) gratingValue.setAttribute('fill', '#ff8f00');
      break;

    case 'LOCK':
      lockIntensity = lerp(lockIntensity, 1, 0.15);
      rollerStations.forEach(rs => {
        rs.lockA.setAttribute('opacity', lockIntensity);
        rs.lockB.setAttribute('opacity', lockIntensity);
      });
      paramPhase.textContent = '滚轮锁死';
      paramPhase.setAttribute('fill', '#ff1744');
      paramForce.textContent = '锁紧力 → 0侧向挤压';
      break;

    case 'CUT':
      cutProgress = progress;
      waterJetGroup.setAttribute('opacity', '1');
      cutIndicator.setAttribute('opacity', lerp(0, 0.8, Math.min(1, progress*3)));
      noForceLabel.setAttribute('opacity', lerp(0, 0.9, Math.min(1, progress*2)));

      // 喷嘴扫描移动 (从左到右扫过电缆)
      const scanRange = diaToSvgHeight(cableDiameter) * 0.6;
      nozzleX = Math.sin(progress * Math.PI) * scanRange * 0.3;

      // 水射流长度 - 从喷嘴到电缆底部
      const h = diaToSvgHeight(cableDiameter);
      const streamStart = 385;
      const streamEnd = CABLE_Y + h/2 + 5;
      waterStream.setAttribute('y1', streamStart);
      waterStream.setAttribute('y2', streamEnd);
      waterStream.setAttribute('x1', nozzleX);
      waterStream.setAttribute('x2', nozzleX);

      // 移动喷嘴
      document.getElementById('nozzleTip').setAttribute('transform', `translate(${nozzleX},0)`);

      // 生成飞溅粒子
      if (Math.random() < 0.4) {
        spawnParticle(nozzleX, streamEnd);
      }

      paramPhase.textContent = '水射流切断';
      paramPhase.setAttribute('fill', '#00e5ff');
      paramPressure.textContent = `${waterPressure} bar`;

      // 切断效果 - 电缆逐渐分离
      if (progress > 0.4) {
        const sep = (progress - 0.4) * 15;
        cableCutRight.setAttribute('x', 940 + sep);
        cableCutRight.setAttribute('opacity', '1');
        cableBody.setAttribute('width', 940 - 60);
      }
      break;

    case 'UNLOCK':
      unlockProgress = progress;
      lockIntensity = lerp(lockIntensity, 0, 0.15);
      rollerStations.forEach(rs => {
        rs.lockA.setAttribute('opacity', lockIntensity);
        rs.lockB.setAttribute('opacity', lockIntensity);
      });
      waterJetGroup.setAttribute('opacity', lerp(1, 0, 0.1));
      paramPhase.textContent = '解锁继续';
      paramPhase.setAttribute('fill', '#00e676');
      break;
  }

  // IFR中心线脉动
  const ifrPulse = 0.5 + Math.sin(Date.now() * 0.003) * 0.2;
  ifrLine.setAttribute('opacity', ifrPulse);

  // 中心高偏差
  const dev = (Math.random() * 0.08 - 0.04).toFixed(2);
  paramCenterDev.textContent = `±${Math.abs(dev)} mm`;

  // 更新粒子
  updateParticles(dt);
}

// ============== 粒子系统 ==============
function spawnParticle(x, y) {
  const p = {
    x: 940 + x,
    y: y,
    vx: (Math.random() - 0.5) * 4,
    vy: Math.random() * 3 + 1,
    life: 1.0,
    decay: 0.01 + Math.random() * 0.02,
    size: 1.5 + Math.random() * 2,
  };
  particles.push(p);

  const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
  circle.setAttribute('r', p.size);
  circle.setAttribute('fill', '#00e5ff');
  circle.setAttribute('opacity', '0.8');
  circle.setAttribute('cx', p.x);
  circle.setAttribute('cy', p.y);
  splashGroup.appendChild(circle);
  p.el = circle;
}

function updateParticles(dt) {
  for (let i = particles.length - 1; i >= 0; i--) {
    const p = particles[i];
    p.x += p.vx;
    p.y += p.vy;
    p.vy += 0.1; // 重力
    p.life -= p.decay;
    if (p.life <= 0) {
      p.el.remove();
      particles.splice(i, 1);
    } else {
      p.el.setAttribute('cx', p.x);
      p.el.setAttribute('cy', p.y);
      p.el.setAttribute('opacity', p.life * 0.8);
    }
  }
}

function clearParticles() {
  particles.forEach(p => p.el && p.el.remove());
  particles = [];
}

// ============== 主动画循环 ==============
function animate(timestamp) {
  if (!lastTimestamp) lastTimestamp = timestamp;
  const dt = (timestamp - lastTimestamp) * animSpeed;
  lastTimestamp = timestamp;

  if (playing) {
    phaseTime += dt;
    const duration = PHASE_DURATIONS[currentPhaseIdx];

    if (phaseTime >= duration) {
      // 切换到下一阶段
      phaseTime = 0;
      currentPhaseIdx = (currentPhaseIdx + 1) % PHASES.length;

      // 阶段切换时的重置
      if (PHASES[currentPhaseIdx] === 'FEED') {
        // 新周期开始,重置
        feedProgress = 0;
        cutProgress = 0;
        unlockProgress = 0;
        nozzleX = 0;
        cableCutRight.setAttribute('opacity', '0');
        cableBody.setAttribute('width', 1000);
        document.getElementById('nozzleTip').setAttribute('transform', 'translate(0,0)');
        gratingValue.textContent = '0.000 m';
        gratingValue.setAttribute('fill', '#ff8f00');
        clearParticles();
      }
      if (PHASES[currentPhaseIdx] === 'MEASURE') {
        gratingHead.setAttribute('stroke', '#ff8f00');
      }
    }

    updatePhaseVisuals(dt);
  }

  requestAnimationFrame(animate);
}

// ============== 交互控制 ==============
document.getElementById('diameterSlider').addEventListener('input', function() {
  cableDiameter = parseInt(this.value);
  document.getElementById('diameterDisplay').textContent = cableDiameter;
  updateCableVisuals();
  // 动态调整压力 - 细缆降压
  if (cableDiameter < 15) {
    waterPressure = Math.min(waterPressure, 3200);
    document.getElementById('pressureSlider').value = waterPressure;
    document.getElementById('pressureDisplay').textContent = waterPressure;
  }
});

document.getElementById('pressureSlider').addEventListener('input', function() {
  waterPressure = parseInt(this.value);
  document.getElementById('pressureDisplay').textContent = waterPressure;
  paramPressure.textContent = `${waterPressure} bar`;
});

document.getElementById('speedSlider').addEventListener('input', function() {
  animSpeed = parseFloat(this.value);
  document.getElementById('speedDisplay').textContent = animSpeed.toFixed(1);
});

function togglePlay() {
  playing = !playing;
  const btn = document.getElementById('btnPlay');
  btn.textContent = playing ? '暂停' : '播放';
  btn.classList.toggle('active', playing);
}

function resetAnim() {
  currentPhaseIdx = 0;
  phaseTime = 0;
  feedProgress = 0;
  measureFlash = 0;
  lockIntensity = 0;
  cutProgress = 0;
  unlockProgress = 0;
  nozzleX = 0;
  lastTimestamp = 0;
  clearParticles();
  cableCutRight.setAttribute('opacity', '0');
  cableBody.setAttribute('width', 1000);
  waterJetGroup.setAttribute('opacity', '0');
  cutIndicator.setAttribute('opacity', '0');
  noForceLabel.setAttribute('opacity', '0');
  gratingTriggerLine.setAttribute('opacity', '0');
  document.getElementById('nozzleTip').setAttribute('transform', 'translate(0,0)');
  gratingValue.textContent = '0.000 m';
  rollerStations.forEach(rs => {
    rs.lockA.setAttribute('opacity', '0');
    rs.lockB.setAttribute('opacity', '0');
  });
  playing = true;
  document.getElementById('btnPlay').textContent = '暂停';
  document.getElementById('btnPlay').classList.add('active');
}

function stepPhase() {
  phaseTime = 0;
  currentPhaseIdx = (currentPhaseIdx + 1) % PHASES.length;
  if (PHASES[currentPhaseIdx] === 'FEED') {
    cableCutRight.setAttribute('opacity', '0');
    cableBody.setAttribute('width', 1000);
    clearParticles();
  }
}

// ============== 初始化 ==============
updateCableVisuals();
requestAnimationFrame(animate);
</script>
</body>
</html>

实现说明:

视觉设计:采用深色工业仪表盘风格,精确工程网格背景,以铜色表示电缆、钢灰色表示机械结构、青色标识水射流、石灰绿(#76ff03)标识 IFR 理想中心面——这条虚线始终贯穿全场并轻柔脉动,视觉上强化"中心面恒定不变"这一核心 IFR 思想。

核心动画逻辑:五阶段状态机循环——送料定心 → 光栅测量 → 滚轮锁死 → 水射流切断 → 解锁继续。每阶段有独立的视觉反馈:滚轮旋转、光栅尺读数递增并闪烁、锁紧环红色高亮、水射流喷射与飞溅粒子、电缆分离动画。

IFR 理念突出

  • 中心参考线永不移动,直径变化时滚轮自适应开合但中心面恒定;
  • 水射流始终瞄准该理想面,零侧向力切断(标注"侧向力 = 0");
  • 右侧面板明确列出矛盾与消解逻辑。

交互性:电缆直径滑块(5-140mm)实时改变电缆粗细和滚轮位置,直观体验"无论线径如何,中心高度一致";水射流压力滑块;播放/暂停/步进/重置控制;动画速度调节。截面图同步展示锥面滚轮自适应收放。

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