分享图
动画工坊
引擎就绪

这是一份专门针对伞骨切向螺旋同步收束机构设计的高保真 SVG 原理动画与交互演示系统。代码为单文件完整 HTML,采用精密工程蓝图风格,中央核心机构动画占据最大视觉空间,说明性文字精简并贴边排布,页面加载后全自动循环播放。

<!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>
  <style>
    /* 基础重置与工程蓝图背景 */
    * {
      box-sizing: border-box;
      margin: 0;
      padding: 0;
      user-select: none;
    }
    body {
      background-color: #080c14;
      color: #94a3b8;
      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", monospace;
      overflow: hidden;
      width: 100vw;
      height: 100vh;
      display: flex;
      flex-direction: column;
      position: relative;
    }

    /* 精密网格底纹 */
    .grid-bg {
      position: absolute;
      inset: 0;
      background-image: 
        radial-gradient(circle at 50% 50%, rgba(30, 58, 102, 0.25) 0%, transparent 80%),
        linear-gradient(rgba(255, 255, 255, 0.03) 1px, transparent 1px),
        linear-gradient(90deg, rgba(255, 255, 255, 0.03) 1px, transparent 1px);
      background-size: 100% 100%, 32px 32px, 32px 32px;
      pointer-events: none;
      z-index: 0;
    }

    /* 顶部边缘极简标题栏(不遮挡主画面) */
    header {
      position: absolute;
      top: 14px;
      left: 18px;
      z-index: 10;
      pointer-events: none;
    }
    .title-tag {
      font-size: 10px;
      letter-spacing: 2px;
      color: #38bdf8;
      text-transform: uppercase;
      display: flex;
      align-items: center;
      gap: 6px;
      margin-bottom: 2px;
    }
    .title-tag::before {
      content: "";
      width: 6px;
      height: 6px;
      border-radius: 50%;
      background: #38bdf8;
      box-shadow: 0 0 8px #38bdf8;
    }
    h1 {
      font-size: 15px;
      font-weight: 600;
      color: #f1f5f9;
      letter-spacing: 0.5px;
    }
    .subtitle {
      font-size: 11px;
      color: #64748b;
      margin-top: 2px;
    }

    /* 右上角精密参数仪表HUD(精简小字号) */
    .hud-panel {
      position: absolute;
      top: 14px;
      right: 18px;
      z-index: 10;
      background: rgba(13, 22, 38, 0.7);
      border: 1px solid rgba(56, 189, 248, 0.2);
      border-radius: 6px;
      padding: 8px 12px;
      backdrop-filter: blur(8px);
      display: grid;
      grid-template-columns: repeat(2, auto);
      gap: 6px 14px;
      font-size: 11px;
      pointer-events: none;
    }
    .hud-item {
      display: flex;
      flex-direction: column;
    }
    .hud-label {
      color: #64748b;
      font-size: 9px;
      text-transform: uppercase;
      letter-spacing: 0.5px;
    }
    .hud-val {
      color: #38bdf8;
      font-family: monospace;
      font-weight: 600;
      font-size: 12px;
    }

    /* 主视觉容器 - 核心动画是绝对主角 */
    .main-viewport {
      position: relative;
      flex: 1;
      width: 100%;
      height: 100%;
      display: flex;
      align-items: center;
      justify-content: center;
      z-index: 1;
    }
    svg#mechanismSvg {
      width: 100%;
      height: 100%;
      max-width: 1200px;
      max-height: 820px;
      overflow: visible;
    }

    /* 底部紧凑浮动控制条与状态提示 */
    .bottom-bar {
      position: absolute;
      bottom: 12px;
      left: 50%;
      transform: translateX(-50%);
      z-index: 10;
      display: flex;
      align-items: center;
      gap: 16px;
      background: rgba(15, 23, 42, 0.75);
      border: 1px solid rgba(255, 255, 255, 0.1);
      padding: 6px 16px;
      border-radius: 24px;
      backdrop-filter: blur(8px);
    }
    .status-badge {
      display: flex;
      align-items: center;
      gap: 6px;
      font-size: 11px;
      color: #cbd5e1;
    }
    .status-dot {
      width: 7px;
      height: 7px;
      border-radius: 50%;
      background: #10b981;
      box-shadow: 0 0 8px #10b981;
      transition: background 0.3s;
    }
    .ctrl-btn {
      background: transparent;
      border: 1px solid rgba(56, 189, 248, 0.4);
      color: #38bdf8;
      padding: 3px 10px;
      border-radius: 12px;
      font-size: 11px;
      cursor: pointer;
      transition: all 0.2s;
    }
    .ctrl-btn:hover {
      background: rgba(56, 189, 248, 0.15);
      border-color: #38bdf8;
    }
    .timeline-range {
      appearance: none;
      width: 120px;
      height: 3px;
      background: #334155;
      border-radius: 2px;
      outline: none;
    }
    .timeline-range::-webkit-slider-thumb {
      appearance: none;
      width: 10px;
      height: 10px;
      border-radius: 50%;
      background: #38bdf8;
      cursor: pointer;
      box-shadow: 0 0 6px #38bdf8;
    }

    /* 左下角关键机理微说明(文字精简、字号超小) */
    .footnote {
      position: absolute;
      bottom: 14px;
      left: 18px;
      font-size: 10px;
      color: #475569;
      pointer-events: none;
      line-height: 1.5;
    }
  </style>
</head>
<body>
  <div class="grid-bg"></div>

  <!-- 顶部轻量标头 -->
  <header>
    <div class="title-tag">Ideal Final Result</div>
    <h1>伞骨切向螺旋同步收束机构 (8骨)</h1>
    <div class="subtitle">单中心轮毂 55° 旋转驱动 · 8 槽导销切向内敛并拢</div>
  </header>

  <!-- 右上角 HUD 实时监测板 -->
  <div class="hud-panel">
    <div class="hud-item">
      <span class="hud-label">轮毂旋转角 φ</span>
      <span class="hud-val" id="hudAngle">0.0° / 55.0°</span>
    </div>
    <div class="hud-item">
      <span class="hud-label">导槽径向行程 Δr</span>
      <span class="hud-val" id="hudStroke">0.0 mm / 22.0 mm</span>
    </div>
    <div class="hud-item">
      <span class="hud-label">扭簧瞬态扭矩</span>
      <span class="hud-val" id="hudTorque">0.60 N·m</span>
    </div>
    <div class="hud-item">
      <span class="hud-label">粘性阻尼状态</span>
      <span class="hud-val" id="hudDamp">0.12 N·m·s/rad</span>
    </div>
  </div>

  <!-- 中央主视窗:SVG 核心机构 -->
  <div class="main-viewport">
    <svg id="mechanismSvg" viewBox="0 0 1000 650">
      <defs>
        <!-- 金属与发光渐变 -->
        <radialGradient id="hubGrad" cx="50%" cy="50%" r="50%">
          <stop offset="0%" stop-color="#1e293b"/>
          <stop offset="65%" stop-color="#0f172a"/>
          <stop offset="100%" stop-color="#0284c7" stop-opacity="0.8"/>
        </radialGradient>

        <linearGradient id="ribGrad" x1="0%" y1="0%" x2="100%" y2="0%">
          <stop offset="0%" stop-color="#38bdf8"/>
          <stop offset="50%" stop-color="#60a5fa"/>
          <stop offset="100%" stop-color="#1d4ed8"/>
        </linearGradient>

        <!-- 导销发光点滤镜 -->
        <filter id="pinGlow" x="-50%" y="-50%" width="200%" height="200%">
          <feGaussianBlur in="SourceGraphic" stdDeviation="2" result="blur"/>
          <feMerge>
            <feMergeNode in="blur"/>
            <feMergeNode in="SourceGraphic"/>
          </feMerge>
        </filter>

        <!-- 刻度线图案 -->
        <pattern id="minorGrid" width="10" height="10" patternUnits="userSpaceOnUse">
          <circle cx="5" cy="5" r="0.5" fill="#1e293b"/>
        </pattern>
      </defs>

      <!-- 坐标刻度基底 -->
      <rect x="20" y="20" width="960" height="610" rx="8" fill="url(#minorGrid)" stroke="rgba(255,255,255,0.05)" stroke-width="1"/>

      <!-- ====================== 左侧:俯视主视图(核心旋转与螺旋驱动区) ====================== -->
      <g id="topViewPlan" transform="translate(420, 325)">
        <!-- 基准外圆及角度标尺 -->
        <circle cx="0" cy="0" r="230" fill="none" stroke="rgba(56, 189, 248, 0.12)" stroke-width="1" stroke-dasharray="3 3"/>
        <circle cx="0" cy="0" r="140" fill="none" stroke="rgba(56, 189, 248, 0.18)" stroke-width="1" stroke-dasharray="4 4"/>
        <circle cx="0" cy="0" r="48" fill="none" stroke="rgba(56, 189, 248, 0.3)" stroke-width="1"/>

        <!-- 8条机架径向导向槽虚线轨迹 -->
        <g id="radialGuides" opacity="0.4">
          <!-- 动态用 JS 生成或预设 -->
        </g>

        <!-- 伞面切向折叠羽翼(半透明织物包络区) -->
        <g id="fabricFoldLayer"></g>

        <!-- 8根主伞骨(受控于导销,切向收拢) -->
        <g id="ribsLayer"></g>

        <!-- 核心动态旋转轮毂组件(根据旋转角整体转动) -->
        <g id="rotatingHub">
          <!-- 轮毂外圈盘 -->
          <circle cx="0" cy="0" r="102" fill="url(#hubGrad)" stroke="#38bdf8" stroke-width="1.8" filter="drop-shadow(0 0 12px rgba(56,189,248,0.25))"/>
          <circle cx="0" cy="0" r="94" fill="none" stroke="rgba(56,189,248,0.3)" stroke-width="1" stroke-dasharray="2 3"/>

          <!-- 8条精密螺旋导槽(实体镂空) -->
          <g id="spiralSlotsLayer"></g>

          <!-- 55° 旋转行程刻度指示区 -->
          <path id="angleArc" fill="none" stroke="#f59e0b" stroke-width="2.5" stroke-linecap="round" opacity="0.8"/>
          <circle cx="0" cy="0" r="32" fill="#0b1329" stroke="#38bdf8" stroke-width="1.2"/>
          
          <!-- 轮毂内部扭簧仿真线圈 -->
          <path id="torsionSpring" fill="none" stroke="#f43f5e" stroke-width="2" stroke-linecap="round"/>
        </g>

        <!-- 8个高精导销(嵌入螺旋槽并在机架导引下滑移) -->
        <g id="pinsLayer"></g>

        <!-- 中心固定中棒截面剖面 -->
        <g id="centerShaft">
          <circle cx="0" cy="0" r="16" fill="#090d16" stroke="#94a3b8" stroke-width="2"/>
          <circle cx="0" cy="0" r="6" fill="#38bdf8"/>
          <!-- 锁紧键槽标记 -->
          <rect x="-2" y="-16" width="4" height="6" fill="#64748b"/>
        </g>

        <!-- 局部注释箭头指示(精简贴附) -->
        <g id="viewAnnotations" pointer-events="none">
          <text x="-98" y="-115" fill="#38bdf8" font-size="10" font-family="monospace">中心旋转轮毂 (8螺旋槽)</text>
          <line x1="-50" y1="-110" x2="-25" y2="-90" stroke="#38bdf8" stroke-width="0.8" stroke-dasharray="2 2"/>
        </g>
      </g>

      <!-- ====================== 右侧:联动立面示意(正视收拢贴合柱态) ====================== -->
      <g id="sideElevationView" transform="translate(850, 325)">
        <rect x="-90" y="-220" width="180" height="440" rx="6" fill="rgba(15,23,42,0.4)" stroke="rgba(255,255,255,0.08)" stroke-width="1"/>
        <text x="0" y="-195" text-anchor="middle" fill="#94a3b8" font-size="11" font-weight="600">立面收束形态</text>
        <text x="0" y="-180" text-anchor="middle" fill="#475569" font-size="9" font-family="monospace">IFR: 免双手整理贴合</text>

        <!-- 中棒 -->
        <rect x="-5" y="-160" width="10" height="340" fill="#334155" stroke="#64748b" stroke-width="1" rx="2"/>
        <!-- 轮毂座 -->
        <rect x="-26" y="-70" width="52" height="18" fill="#1e293b" stroke="#38bdf8" stroke-width="1.2" rx="3"/>
        <circle cx="0" cy="-61" r="3" fill="#f59e0b"/>

        <!-- 立面左右联动伞骨束 -->
        <g id="sideRibs"></g>

        <!-- 收紧状态指示环 -->
        <ellipse id="clampingRing" cx="0" cy="40" rx="36" ry="6" fill="none" stroke="#10b981" stroke-width="1.5" stroke-dasharray="3 3"/>
        <text id="sideStateText" x="0" y="80" text-anchor="middle" fill="#10b981" font-size="10" font-family="monospace">紧密自贴合</text>

        <!-- 尺寸标注线:导程 22mm -->
        <g transform="translate(56, -70)">
          <line x1="0" y1="0" x2="0" y2="28" stroke="#f59e0b" stroke-width="1"/>
          <line x1="-3" y1="0" x2="3" y2="0" stroke="#f59e0b" stroke-width="1"/>
          <line x1="-3" y1="28" x2="3" y2="28" stroke="#f59e0b" stroke-width="1"/>
          <text x="6" y="18" fill="#f59e0b" font-size="9" font-family="monospace">导程 22mm</text>
        </g>
      </g>
    </svg>
  </div>

  <!-- 左下角微型机理说明 -->
  <div class="footnote">
    机理:释放锁 → 扭簧释能0.6N·m → 轮毂转55° → 螺旋槽驱动导销切向内移 → 阻尼器平稳限速 → 8骨螺旋贴紧中棒
  </div>

  <!-- 底部微交互控制条 -->
  <div class="bottom-bar">
    <div class="status-badge">
      <div class="status-dot" id="statusDot"></div>
      <span id="cyclePhaseText">自动播放中 · 同步螺旋收束</span>
    </div>
    <input type="range" class="timeline-range" id="progressSlider" min="0" max="100" value="0" step="0.1">
    <button class="ctrl-btn" id="playPauseBtn">暂停</button>
    <button class="ctrl-btn" id="resetBtn">重开演示</button>
  </div>

  <script>
    /* =========================================================================
       几何数学模型与物理协同仿真
       ========================================================================= */
    const NUM_RIBS = 8;
    const MAX_ROTATION_DEG = 55.0; // 轮毂总转动角 55°
    const R_PIN_OPEN = 86;         // 张开时导销距中心半径 (px)
    const R_PIN_CLOSED = 44;       // 收拢时导销贴紧中心半径 (px) -> 导程行程比 22mm
    const RIB_LENGTH = 195;        // 伞骨长度 (px)

    // DOM 元素缓存
    const rotatingHub = document.getElementById('rotatingHub');
    const spiralSlotsLayer = document.getElementById('spiralSlotsLayer');
    const pinsLayer = document.getElementById('pinsLayer');
    const ribsLayer = document.getElementById('ribsLayer');
    const fabricFoldLayer = document.getElementById('fabricFoldLayer');
    const radialGuides = document.getElementById('radialGuides');
    const angleArc = document.getElementById('angleArc');
    const torsionSpring = document.getElementById('torsionSpring');
    const sideRibs = document.getElementById('sideRibs');
    const clampingRing = document.getElementById('clampingRing');
    const sideStateText = document.getElementById('sideStateText');

    // HUD & 控制面板
    const hudAngle = document.getElementById('hudAngle');
    const hudStroke = document.getElementById('hudStroke');
    const hudTorque = document.getElementById('hudTorque');
    const progressSlider = document.getElementById('progressSlider');
    const playPauseBtn = document.getElementById('playPauseBtn');
    const resetBtn = document.getElementById('resetBtn');
    const statusDot = document.getElementById('statusDot');
    const cyclePhaseText = document.getElementById('cyclePhaseText');

    /* 1. 构建轮毂上 8 条阿基米德/对数混合精密螺旋导槽 */
    function buildSpiralPathString() {
      // 导槽定义在轮毂局部坐标系中,槽角度跨度对应 55°
      let d = "";
      const steps = 25;
      const spanRad = (MAX_ROTATION_DEG * Math.PI) / 180;
      
      // 绘制外侧边界向内,再倒退内侧边界形成双轨实体槽
      const slotWidth = 4.5;
      for (let i = 0; i <= steps; i++) {
        const u = i / steps;
        const r = R_PIN_CLOSED + (R_PIN_OPEN - R_PIN_CLOSED) * u;
        const theta = spanRad * (1 - u);
        const x = (r + slotWidth/2) * Math.cos(theta);
        const y = (r + slotWidth/2) * Math.sin(theta);
        d += (i === 0 ? `M ${x.toFixed(2)} ${y.toFixed(2)}` : ` L ${x.toFixed(2)} ${y.toFixed(2)}`);
      }
      for (let i = steps; i >= 0; i--) {
        const u = i / steps;
        const r = R_PIN_CLOSED + (R_PIN_OPEN - R_PIN_CLOSED) * u;
        const theta = spanRad * (1 - u);
        const x = (r - slotWidth/2) * Math.cos(theta);
        const y = (r - slotWidth/2) * Math.sin(theta);
        d += ` L ${x.toFixed(2)} ${y.toFixed(2)}`;
      }
      d += " Z";
      return d;
    }

    // 初始化轮毂导槽与机架参考导引
    const singleSlotPath = buildSpiralPathString();
    for (let k = 0; k < NUM_RIBS; k++) {
      const baseDeg = (360 / NUM_RIBS) * k;
      
      // 8条螺旋槽
      const slot = document.createElementNS("http://www.w3.org/2000/svg", "path");
      slot.setAttribute("d", singleSlotPath);
      slot.setAttribute("fill", "#090d16");
      slot.setAttribute("stroke", "#0284c7");
      slot.setAttribute("stroke-width", "1.2");
      slot.setAttribute("transform", `rotate(${baseDeg})`);
      spiralSlotsLayer.appendChild(slot);

      // 机架径向滑轨指示虚线
      const rad = (baseDeg * Math.PI) / 180;
      const guideLine = document.createElementNS("http://www.w3.org/2000/svg", "line");
      guideLine.setAttribute("x1", (28 * Math.cos(rad)).toFixed(1));
      guideLine.setAttribute("y1", (28 * Math.sin(rad)).toFixed(1));
      guideLine.setAttribute("x2", (110 * Math.cos(rad)).toFixed(1));
      guideLine.setAttribute("y2", (110 * Math.sin(rad)).toFixed(1));
      guideLine.setAttribute("stroke", "#38bdf8");
      guideLine.setAttribute("stroke-width", "1");
      guideLine.setAttribute("stroke-dasharray", "2 3");
      radialGuides.appendChild(guideLine);
    }

    // 初始化 8 组伞骨与导销 SVG 节点
    const pinElems = [];
    const ribElems = [];
    const fabricElems = [];
    for (let k = 0; k < NUM_RIBS; k++) {
      // 伞骨杆件
      const rib = document.createElementNS("http://www.w3.org/2000/svg", "g");
      rib.innerHTML = `
        <line class="rib-shaft" x1="0" y1="0" x2="0" y2="0" stroke="url(#ribGrad)" stroke-width="3" stroke-linecap="round"/>
        <line class="rib-ridge" x1="0" y1="0" x2="0" y2="0" stroke="#bae6fd" stroke-width="1" stroke-linecap="round"/>
        <circle class="rib-tip" cx="0" cy="0" r="3.2" fill="#38bdf8"/>
      `;
      ribsLayer.appendChild(rib);
      ribElems.push(rib);

      // 伞布切向折痕多边形
      const fabric = document.createElementNS("http://www.w3.org/2000/svg", "polygon");
      fabric.setAttribute("fill", "rgba(14, 116, 144, 0.12)");
      fabric.setAttribute("stroke", "rgba(56, 189, 248, 0.2)");
      fabric.setAttribute("stroke-width", "0.8");
      fabricFoldLayer.appendChild(fabric);
      fabricElems.push(fabric);

      // 导销(带发光倒角)
      const pin = document.createElementNS("http://www.w3.org/2000/svg", "g");
      pin.setAttribute("filter", "url(#pinGlow)");
      pin.innerHTML = `
        <circle cx="0" cy="0" r="4.2" fill="#f59e0b" stroke="#ffffff" stroke-width="1.2"/>
        <circle cx="0" cy="0" r="1.5" fill="#451a03"/>
      `;
      pinsLayer.appendChild(pin);
      pinElems.push(pin);
    }

    // 初始化立面 8 根投影杆件
    const sideRibLines = [];
    for (let k = 0; k < NUM_RIBS; k++) {
      const line = document.createElementNS("http://www.w3.org/2000/svg", "line");
      line.setAttribute("stroke", "#38bdf8");
      line.setAttribute("stroke-width", "2.2");
      line.setAttribute("stroke-linecap", "round");
      sideRibs.appendChild(line);
      sideRibLines.push(line);
    }

    /* =========================================================================
       自动播放时间轴与阻尼运动方程
       ========================================================================= */
    let isPlaying = true;
    let animProgress = 0.0; // 0.0 (全开) -> 1.0 (全收拢)
    let lastTimestamp = null;
    let cycleDirection = 1; // 1: 收拢动作, -1: 复位展开动作
    let pauseTimer = 0;

    // 旋转阻尼平滑缓动函数 (模拟阻尼器恒定减速与末端锁紧)
    function easeDamped(t) {
      // 阻尼逼近曲线
      return 1 - Math.exp(-3.8 * t) * Math.cos(1.2 * t);
    }

    /* 动力学状态更新与几何坐标求解渲染 */
    function updateMechanism(progress) {
      // progress: 0 (开) ~ 1 (完全收拢)
      const currentAngleDeg = progress * MAX_ROTATION_DEG;
      const angleRad = (currentAngleDeg * Math.PI) / 180;

      // 1. 旋转轮毂姿态
      rotatingHub.setAttribute("transform", `rotate(${currentAngleDeg.toFixed(2)})`);

      // 2. 55° 行程刻度动态圆弧
      const arcR = 98;
      const arcX = arcR * Math.cos(-angleRad);
      const arcY = arcR * Math.sin(-angleRad);
      angleArc.setAttribute("d", `M ${arcR} 0 A ${arcR} ${arcR} 0 0 0 ${arcX.toFixed(2)} ${arcY.toFixed(2)}`);

      // 3. 扭簧线圈根据角度形变(收伞释能)
      let springD = "M 0 0 ";
      const springCoils = 4;
      const springSteps = 40;
      const springTightness = 1.0 - progress * 0.25; // 收拢时扭簧圈径略微松弛释能
      for (let s = 0; s <= springSteps; s++) {
        const p = s / springSteps;
        const sr = (6 + p * 20) * springTightness;
        const sa = p * springCoils * Math.PI * 2 - angleRad;
        springD += `L ${(sr * Math.cos(sa)).toFixed(1)} ${(sr * Math.sin(sa)).toFixed(1)} `;
      }
      torsionSpring.setAttribute("d", springD);

      // 4. 计算导销位置与伞骨姿态
      // 螺旋槽方程使得旋转 angleRad 时,交点在径向滑轨上的半径由 R_PIN_OPEN 变至 R_PIN_CLOSED
      const currentRadius = R_PIN_OPEN - (R_PIN_OPEN - R_PIN_CLOSED) * progress;
      const currentStrokeMm = (22.0 * progress).toFixed(1);

      const tipCoords = [];

      for (let k = 0; k < NUM_RIBS; k++) {
        const baseAngleDeg = (360 / NUM_RIBS) * k;
        const baseAngleRad = (baseAngleDeg * Math.PI) / 180;

        // 导销受机架约束在径向运动 (xPin, yPin)
        const xPin = currentRadius * Math.cos(baseAngleRad);
        const yPin = currentRadius * Math.sin(baseAngleRad);

        pinElems[k].setAttribute("transform", `translate(${xPin.toFixed(2)}, ${yPin.toFixed(2)})`);

        // 伞骨切向倾角联动:
        // 张开状态下,伞骨沿径向辐射(倾斜夹角很小约 12°)
        // 收束状态下,伞骨向切向偏转 76° 紧紧包覆中棒形成漩涡涡流理想柱体
        const tangentBiasDeg = 14 + progress * 64; 
        const ribTotalAngleRad = ((baseAngleDeg + tangentBiasDeg) * Math.PI) / 180;

        const xTip = xPin + RIB_LENGTH * Math.cos(ribTotalAngleRad);
        const yTip = yPin + RIB_LENGTH * Math.sin(ribTotalAngleRad);
        tipCoords.push({ x: xTip, y: yTip });

        // 更新伞骨 SVG
        const shaft = ribElems[k].querySelector('.rib-shaft');
        const ridge = ribElems[k].querySelector('.rib-ridge');
        const tip = ribElems[k].querySelector('.rib-tip');

        shaft.setAttribute("x1", xPin.toFixed(1));
        shaft.setAttribute("y1", yPin.toFixed(1));
        shaft.setAttribute("x2", xTip.toFixed(1));
        shaft.setAttribute("y2", yTip.toFixed(1));

        ridge.setAttribute("x1", xPin.toFixed(1));
        ridge.setAttribute("y1", yPin.toFixed(1));
        ridge.setAttribute("x2", (xPin + (xTip - xPin) * 0.75).toFixed(1));
        ridge.setAttribute("y2", (yPin + (yTip - yPin) * 0.75).toFixed(1));

        tip.setAttribute("cx", xTip.toFixed(1));
        tip.setAttribute("cy", yTip.toFixed(1));
      }

      // 5. 更新织物切向折痕层
      for (let k = 0; k < NUM_RIBS; k++) {
        const nextK = (k + 1) % NUM_RIBS;
        const baseAngleRad = ((360 / NUM_RIBS) * k * Math.PI) / 180;
        const xPin = currentRadius * Math.cos(baseAngleRad);
        const yPin = currentRadius * Math.sin(baseAngleRad);

        const pts = `${xPin.toFixed(1)},${yPin.toFixed(1)} ${tipCoords[k].x.toFixed(1)},${tipCoords[k].y.toFixed(1)} ${tipCoords[nextK].x.toFixed(1)},${tipCoords[nextK].y.toFixed(1)}`;
        fabricElems[k].setAttribute("points", pts);
      }

      // 6. 联动立面视图投影计算
      // 立面投影中,展开时下摆宽阔(约140px),收束时紧致贴合至中棒周围(18px)
      const sideSpread = 72 * (1 - progress * 0.76); 
      const sideDropY = 160 + progress * 15; // 收拢时稍微向下延伸贴合

      for (let k = 0; k < NUM_RIBS; k++) {
        const phiPhase = (k / NUM_RIBS) * Math.PI * 2 + angleRad;
        const sideX = Math.cos(phiPhase) * sideSpread;
        const line = sideRibLines[k];
        line.setAttribute("x1", (Math.cos(phiPhase) * 24).toFixed(1));
        line.setAttribute("y1", "-60");
        line.setAttribute("x2", sideX.toFixed(1));
        line.setAttribute("y2", sideDropY.toFixed(1));
        line.setAttribute("opacity", (0.35 + 0.65 * Math.abs(Math.cos(phiPhase))).toFixed(2));
      }

      // 立面紧固环与文字状态
      clampingRing.setAttribute("rx", (sideSpread + 4).toFixed(1));
      if (progress > 0.85) {
        clampingRing.setAttribute("stroke", "#10b981");
        sideStateText.textContent = "已贴合锁止 (IFR)";
        sideStateText.setAttribute("fill", "#10b981");
      } else {
        clampingRing.setAttribute("stroke", "#38bdf8");
        sideStateText.textContent = "收束中 / 阻尼均速";
        sideStateText.setAttribute("fill", "#38bdf8");
      }

      // 7. 更新右上角 HUD 仪表参数
      hudAngle.textContent = `${currentAngleDeg.toFixed(1)}° / 55.0°`;
      hudStroke.textContent = `${currentStrokeMm} mm / 22.0 mm`;
      const torqueVal = (0.60 * (1 - progress * 0.7)).toFixed(2);
      hudTorque.textContent = `${torqueVal} N·m`;
      progressSlider.value = (progress * 100).toFixed(1);
    }

    /* =========================================================================
       自动循环主帧动画循环
       ========================================================================= */
    function animateLoop(timestamp) {
      if (!lastTimestamp) lastTimestamp = timestamp;
      const dt = (timestamp - lastTimestamp) / 1000;
      lastTimestamp = timestamp;

      if (isPlaying) {
        if (pauseTimer > 0) {
          pauseTimer -= dt;
        } else {
          // 收束与复位往复平滑播放
          const speed = cycleDirection === 1 ? 0.38 : 0.55; // 收伞适度平稳阻尼,复位展开稍快
          animProgress += cycleDirection * dt * speed;

          if (animProgress >= 1.0) {
            animProgress = 1.0;
            cycleDirection = -1;
            pauseTimer = 1.8; // 完全收束状态驻留展示 1.8秒,突出理想解效果
            cyclePhaseText.textContent = "IFR理想状态 · 8骨自紧密包裹";
            statusDot.style.background = "#10b981";
            statusDot.style.boxShadow = "0 0 10px #10b981";
          } else if (animProgress <= 0.0) {
            animProgress = 0.0;
            cycleDirection = 1;
            pauseTimer = 1.0; // 展开准备阶段驻留 1.0秒
            cyclePhaseText.textContent = "待收伞状态 · 锁止释放触发";
            statusDot.style.background = "#38bdf8";
            statusDot.style.boxShadow = "0 0 10px #38bdf8";
          } else {
            cyclePhaseText.textContent = cycleDirection === 1 
              ? "扭簧释能 · 螺旋切向同步收束中" 
              : "复位展开循环演示";
          }
          updateMechanism(animProgress);
        }
      }

      requestAnimationFrame(animateLoop);
    }

    // 页面加载完成后立即自动启动!
    window.addEventListener('DOMContentLoaded', () => {
      updateMechanism(0);
      requestAnimationFrame(animateLoop);
    });

    /* =========================================================================
       微交互事件绑定
       ========================================================================= */
    // 进度条手动交互拖动体验
    progressSlider.addEventListener('input', (e) => {
      isPlaying = false;
      playPauseBtn.textContent = "继续";
      animProgress = parseFloat(e.target.value) / 100;
      pauseTimer = 0;
      updateMechanism(animProgress);
    });

    // 播放 / 暂停切换
    playPauseBtn.addEventListener('click', () => {
      isPlaying = !isPlaying;
      playPauseBtn.textContent = isPlaying ? "暂停" : "播放";
      if (isPlaying) lastTimestamp = performance.now();
    });

    // 重开演示
    resetBtn.addEventListener('click', () => {
      animProgress = 0.0;
      cycleDirection = 1;
      pauseTimer = 0.2;
      isPlaying = true;
      playPauseBtn.textContent = "暂停";
      updateMechanism(0);
      lastTimestamp = performance.now();
    });
  </script>
</body>
</html>

关键设计与工程特征落实说明

  1. 视觉主角居中,绝不遮挡:

    • 核心机构占据整幅画面约 80% 的主要可视范围。
    • 所有文字、图例、HUD 仪表均采用紧凑微型布局(字号 9px~12px)放置在左上、右上及底边等极限边界区域,留出最大无障碍观察空间。
  2. TRIZ 最终理想解 (IFR) 视觉呈现:

    • 理想状态直达:动画直接体现通过单旋转轮毂与8条螺旋槽实现“自身完成完美收束”的理想解,免去双手反复理伞动作。
    • 巧妙资源利用:仅借用收伞行程释放的中心扭簧(0.6N·m)与旋转阻尼器(0.12N·m·s/rad),将原本各向无序的直线折叠力转换为同步的切向螺旋内裹。
    • 双视窗联动引导:左侧俯视视图完整呈现 8 导销沿螺旋槽由外径滑向内径的 55° 角位移;右侧立面视图实时对比伞面如何由宽张八字伞形平稳收束成紧贴中棒的规整柱状束。
  3. 开箱即播与零依赖规范:

    • 无任何外部 CSS/JS 依赖库,原生现代浏览器均可即开即播。
    • 具有自适应进度滑块,既保持自动无限循环播放,又支持手动拖曳深入推敲每个角度的导销啮合位置。
积分规则:每轮对话扣10分