分享图
动画工坊
引擎就绪
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PE手套微负压腔吸附原理</title>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@300;400;700;900&family=Oxanium:wght@400;600;700;800&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css">
<style>
:root {
  --bg: #060b18;
  --bg2: #0d1425;
  --card: #111b2e;
  --fg: #e8e0d4;
  --muted: #5e7291;
  --accent: #00e5ff;
  --accent-glow: rgba(0,229,255,0.25);
  --glove: #d4a847;
  --glove-fill: rgba(212,168,71,0.55);
  --glass: #7fb8d8;
  --skin: #e8b89d;
  --danger: #ff6b6b;
  --success: #4ade80;
  --border: rgba(94,114,145,0.2);
}
*{margin:0;padding:0;box-sizing:border-box;}
html,body{height:100%;overflow:hidden;}
body{
  background:var(--bg);
  color:var(--fg);
  font-family:'Noto Sans SC',sans-serif;
  display:flex;flex-direction:column;
}
/* 背景网格 */
body::before{
  content:'';position:fixed;inset:0;z-index:0;pointer-events:none;
  background-image:
    linear-gradient(rgba(0,229,255,0.03) 1px,transparent 1px),
    linear-gradient(90deg,rgba(0,229,255,0.03) 1px,transparent 1px);
  background-size:60px 60px;
}
header{
  position:relative;z-index:1;
  padding:18px 32px 10px;
  display:flex;align-items:baseline;gap:18px;
  border-bottom:1px solid var(--border);
  background:linear-gradient(180deg,rgba(13,20,37,0.95),rgba(6,11,24,0.85));
  backdrop-filter:blur(10px);
}
header h1{
  font-family:'Oxanium',sans-serif;
  font-weight:800;font-size:22px;letter-spacing:1px;
  color:var(--accent);
  text-shadow:0 0 20px var(--accent-glow);
}
header p{
  font-size:13px;color:var(--muted);font-weight:300;
}
main{
  flex:1;display:flex;position:relative;z-index:1;
  overflow:hidden;
}
/* 左侧主动画区 */
.main-view{
  flex:1;display:flex;align-items:center;justify-content:center;
  position:relative;padding:12px;
}
.main-view svg{width:100%;height:100%;max-height:calc(100vh - 80px);}
/* 右侧面板 */
.side-panel{
  width:320px;min-width:280px;
  border-left:1px solid var(--border);
  background:var(--bg2);
  display:flex;flex-direction:column;
  overflow-y:auto;
  scrollbar-width:thin;scrollbar-color:var(--muted) transparent;
}
.panel-section{
  padding:18px 20px;
  border-bottom:1px solid var(--border);
}
.panel-section:last-child{border-bottom:none;}
.panel-title{
  font-family:'Oxanium',sans-serif;
  font-size:11px;font-weight:700;
  text-transform:uppercase;letter-spacing:2px;
  color:var(--muted);margin-bottom:12px;
}
/* 六边形阵列小图 */
.hex-view{width:100%;aspect-ratio:1;border-radius:8px;overflow:hidden;
  background:rgba(0,0,0,0.3);border:1px solid var(--border);}
.hex-view svg{width:100%;height:100%;}
/* 参数列表 */
.param-list{list-style:none;}
.param-list li{
  display:flex;justify-content:space-between;align-items:center;
  padding:7px 0;border-bottom:1px solid rgba(94,114,145,0.1);
  font-size:13px;
}
.param-list li:last-child{border-bottom:none;}
.param-label{color:var(--muted);}
.param-value{
  font-family:'Oxanium',sans-serif;font-weight:600;
  color:var(--fg);font-size:13px;
}
.param-value.accent{color:var(--accent);}
/* 阶段指示器 */
.phase-list{list-style:none;display:flex;flex-direction:column;gap:6px;}
.phase-item{
  display:flex;align-items:center;gap:10px;
  padding:8px 12px;border-radius:8px;
  font-size:13px;color:var(--muted);
  transition:all 0.4s ease;
  background:transparent;
}
.phase-item.active{
  background:rgba(0,229,255,0.08);
  color:var(--fg);
  box-shadow:inset 0 0 0 1px rgba(0,229,255,0.2);
}
.phase-dot{
  width:8px;height:8px;border-radius:50%;
  background:var(--muted);flex-shrink:0;
  transition:all 0.4s ease;
}
.phase-item.active .phase-dot{
  background:var(--accent);
  box-shadow:0 0 8px var(--accent);
}
/* 控制区 */
.control-row{
  display:flex;align-items:center;gap:10px;margin-bottom:12px;
}
.control-row:last-child{margin-bottom:0;}
.btn{
  display:inline-flex;align-items:center;justify-content:center;
  width:36px;height:36px;border-radius:8px;border:1px solid var(--border);
  background:var(--card);color:var(--fg);cursor:pointer;
  transition:all 0.2s;font-size:14px;
}
.btn:hover{border-color:var(--accent);color:var(--accent);}
.btn.active{background:rgba(0,229,255,0.12);border-color:var(--accent);color:var(--accent);}
.slider-wrap{flex:1;display:flex;flex-direction:column;gap:4px;}
.slider-label{font-size:11px;color:var(--muted);display:flex;justify-content:space-between;}
.slider-label span:last-child{font-family:'Oxanium',sans-serif;color:var(--fg);}
input[type=range]{
  -webkit-appearance:none;width:100%;height:6px;
  border-radius:3px;background:var(--card);outline:none;
  border:1px solid var(--border);
}
input[type=range]::-webkit-slider-thumb{
  -webkit-appearance:none;width:16px;height:16px;
  border-radius:50%;background:var(--accent);cursor:pointer;
  box-shadow:0 0 8px var(--accent-glow);
}
/* IFR 卡片 */
.ifr-card{
  background:linear-gradient(135deg,rgba(0,229,255,0.06),rgba(0,229,255,0.02));
  border:1px solid rgba(0,229,255,0.15);
  border-radius:10px;padding:14px;
}
.ifr-card .ifr-tag{
  font-family:'Oxanium',sans-serif;font-size:10px;font-weight:700;
  text-transform:uppercase;letter-spacing:2px;
  color:var(--accent);margin-bottom:8px;
}
.ifr-card .ifr-text{
  font-size:13px;line-height:1.7;color:var(--fg);font-weight:300;
}
.ifr-card .ifr-text strong{color:var(--accent);font-weight:700;}
/* SVG 内文字样式 */
.svg-label{
  font-family:'Oxanium',sans-serif;fill:var(--muted);font-size:11px;
}
.svg-label-bright{
  font-family:'Oxanium',sans-serif;fill:var(--accent);font-size:12px;font-weight:700;
}
.svg-dim-line{
  stroke:var(--muted);stroke-width:0.8;stroke-dasharray:4 3;
  fill:none;
}
.svg-dim-text{
  font-family:'Oxanium',sans-serif;fill:var(--fg);font-size:10px;
}
/* 动画 - 呼吸效果 */
@keyframes pulse-glow{
  0%,100%{opacity:0.5;}
  50%{opacity:1;}
}
@keyframes float-particle{
  0%,100%{transform:translateY(0);}
  50%{transform:translateY(-3px);}
}
/* 响应式 */
@media(max-width:900px){
  .side-panel{width:260px;min-width:220px;}
}
@media(max-width:700px){
  main{flex-direction:column;}
  .side-panel{width:100%;max-height:40vh;border-left:none;border-top:1px solid var(--border);}
}
</style>
</head>
<body>

<header>
  <h1>MICRO-SUCTION PE GLOVE</h1>
  <p>微负压腔防滑原理 — 化"薄"为利,主动吸附</p>
</header>

<main>
  <div class="main-view">
    <svg id="mainSvg" viewBox="0 0 960 620" preserveAspectRatio="xMidYMid meet">
      <defs>
        <!-- 皮肤渐变 -->
        <linearGradient id="skinGrad" x1="0" y1="0" x2="0" y2="1">
          <stop offset="0%" stop-color="#f0c8a8"/>
          <stop offset="100%" stop-color="#d4a080"/>
        </linearGradient>
        <!-- 手套渐变 -->
        <linearGradient id="gloveGrad" x1="0" y1="0" x2="0" y2="1">
          <stop offset="0%" stop-color="rgba(212,168,71,0.7)"/>
          <stop offset="100%" stop-color="rgba(180,140,50,0.85)"/>
        </linearGradient>
        <!-- 玻璃渐变 -->
        <linearGradient id="glassGrad" x1="0" y1="0" x2="0" y2="1">
          <stop offset="0%" stop-color="#9ccce6"/>
          <stop offset="40%" stop-color="#7fb8d8"/>
          <stop offset="100%" stop-color="#5a9ab8"/>
        </linearGradient>
        <!-- 负压发光 -->
        <radialGradient id="vacuumGlow" cx="50%" cy="50%" r="50%">
          <stop offset="0%" stop-color="rgba(0,229,255,0.5)"/>
          <stop offset="70%" stop-color="rgba(0,229,255,0.15)"/>
          <stop offset="100%" stop-color="rgba(0,229,255,0)"/>
        </radialGradient>
        <!-- 空气颜色 -->
        <radialGradient id="airGrad" cx="50%" cy="50%" r="50%">
          <stop offset="0%" stop-color="rgba(136,204,255,0.5)"/>
          <stop offset="100%" stop-color="rgba(136,204,255,0.1)"/>
        </radialGradient>
        <!-- 发光滤镜 -->
        <filter id="glow" x="-50%" y="-50%" width="200%" height="200%">
          <feGaussianBlur in="SourceGraphic" stdDeviation="4" result="blur"/>
          <feMerge><feMergeNode in="blur"/><feMergeNode in="SourceGraphic"/></feMerge>
        </filter>
        <filter id="glowStrong" x="-50%" y="-50%" width="200%" height="200%">
          <feGaussianBlur in="SourceGraphic" stdDeviation="8" result="blur"/>
          <feMerge><feMergeNode in="blur"/><feMergeNode in="SourceGraphic"/></feMerge>
        </filter>
        <!-- 箭头标记 -->
        <marker id="arrowDown" markerWidth="8" markerHeight="8" refX="4" refY="8" orient="auto">
          <path d="M1,0 L4,8 L7,0" fill="var(--danger)" stroke="none"/>
        </marker>
        <marker id="arrowUp" markerWidth="8" markerHeight="8" refX="4" refY="0" orient="auto">
          <path d="M1,8 L4,0 L7,8" fill="var(--accent)" stroke="none"/>
        </marker>
        <marker id="arrowRight" markerWidth="8" markerHeight="8" refX="8" refY="4" orient="auto">
          <path d="M0,1 L8,4 L0,7" fill="var(--muted)" stroke="none"/>
        </marker>
      </defs>

      <!-- 背景装饰 -->
      <rect x="0" y="0" width="960" height="620" fill="none"/>

      <!-- 玻璃表面 (固定) -->
      <g id="glassGroup">
        <rect x="80" y="490" width="800" height="100" rx="2" fill="url(#glassGrad)" opacity="0.9"/>
        <!-- 玻璃高光 -->
        <line x1="100" y1="492" x2="860" y2="492" stroke="rgba(255,255,255,0.4)" stroke-width="1"/>
        <line x1="100" y1="495" x2="700" y2="495" stroke="rgba(255,255,255,0.15)" stroke-width="0.5"/>
        <!-- 玻璃标签 -->
        <text x="480" y="555" text-anchor="middle" font-family="'Oxanium',sans-serif" font-size="13" fill="rgba(255,255,255,0.6)" font-weight="600">SMOOTH SURFACE / 光滑表面</text>
      </g>

      <!-- 手指+手套 组 (整体移动) -->
      <g id="assemblyGroup">
        <!-- 手指截面 -->
        <g id="fingerGroup">
          <path id="fingerPath" d="M200,300 Q200,100 480,80 Q760,100 760,300 Z" fill="url(#skinGrad)" stroke="rgba(180,130,100,0.3)" stroke-width="1"/>
          <!-- 手指纹理线 -->
          <path d="M280,200 Q480,185 680,200" fill="none" stroke="rgba(180,130,100,0.15)" stroke-width="0.8"/>
          <path d="M300,230 Q480,218 660,230" fill="none" stroke="rgba(180,130,100,0.12)" stroke-width="0.8"/>
          <path d="M320,260 Q480,250 640,260" fill="none" stroke="rgba(180,130,100,0.1)" stroke-width="0.8"/>
          <!-- 手指标签 -->
          <text x="480" y="190" text-anchor="middle" font-family="'Oxanium',sans-serif" font-size="14" fill="rgba(100,70,50,0.6)" font-weight="700">FINGER</text>
        </g>

        <!-- PE手套层 -->
        <g id="gloveGroup">
          <!-- 手套主体 (非凹坑区域) -->
          <path id="gloveBody" d="" fill="url(#gloveGrad)" stroke="rgba(212,168,71,0.4)" stroke-width="0.8"/>
          <!-- 凹坑组 -->
          <g id="pitsGroup"></g>
          <!-- 薄膜组 -->
          <g id="filmsGroup"></g>
        </g>

        <!-- 空气粒子组 -->
        <g id="airParticlesGroup"></g>

        <!-- 负压效果组 -->
        <g id="vacuumGroup"></g>
      </g>

      <!-- 力箭头组 -->
      <g id="forceArrowsGroup"></g>

      <!-- 尺寸标注组 -->
      <g id="dimensionsGroup"></g>

      <!-- 关键提示 -->
      <g id="calloutGroup" opacity="0">
        <rect x="620" y="200" width="280" height="70" rx="8" fill="rgba(0,229,255,0.08)" stroke="rgba(0,229,255,0.3)" stroke-width="1"/>
        <text x="635" y="224" font-family="'Oxanium',sans-serif" font-size="11" fill="var(--accent)" font-weight="700" letter-spacing="1">VACUUM FORMED</text>
        <text x="635" y="244" font-family="'Noto Sans SC',sans-serif" font-size="13" fill="var(--fg)" font-weight="400">薄膜顺应表面 → 密封 → 微负压腔</text>
        <text x="635" y="261" font-family="'Noto Sans SC',sans-serif" font-size="12" fill="var(--muted)" font-weight="300">化被动防滑为主动吸附</text>
        <!-- 连接线 -->
        <line id="calloutLine" x1="620" y1="250" x2="550" y2="380" stroke="rgba(0,229,255,0.4)" stroke-width="1" stroke-dasharray="4 3"/>
      </g>
    </svg>
  </div>

  <aside class="side-panel">
    <!-- 六边形阵列俯视图 -->
    <div class="panel-section">
      <div class="panel-title">Hexagonal Micro-Pit Array</div>
      <div class="hex-view">
        <svg id="hexSvg" viewBox="0 0 280 280"></svg>
      </div>
    </div>

    <!-- 关键参数 -->
    <div class="panel-section">
      <div class="panel-title">Key Parameters</div>
      <ul class="param-list">
        <li><span class="param-label">凹坑直径</span><span class="param-value">1.5 mm</span></li>
        <li><span class="param-label">凹坑深度</span><span class="param-value">0.15 mm</span></li>
        <li><span class="param-label">凹坑区壁厚</span><span class="param-value accent">0.025 mm</span></li>
        <li><span class="param-label">常规区壁厚</span><span class="param-value">0.04 mm</span></li>
        <li><span class="param-label">凹坑形状</span><span class="param-value">六边形</span></li>
        <li><span class="param-label">中指长度(小号)</span><span class="param-value">75 mm</span></li>
      </ul>
    </div>

    <!-- 动画阶段 -->
    <div class="panel-section">
      <div class="panel-title">Animation Phase</div>
      <ul class="phase-list" id="phaseList">
        <li class="phase-item" data-phase="0"><span class="phase-dot"></span>接触光滑表面</li>
        <li class="phase-item" data-phase="1"><span class="phase-dot"></span>施加按压力</li>
        <li class="phase-item" data-phase="2"><span class="phase-dot"></span>薄膜变形排气</li>
        <li class="phase-item" data-phase="3"><span class="phase-dot"></span>微负压吸附</li>
        <li class="phase-item" data-phase="4"><span class="phase-dot"></span>破真空释放</li>
      </ul>
      <!-- 进度条 -->
      <div style="margin-top:12px;height:3px;background:var(--card);border-radius:2px;overflow:hidden;">
        <div id="progressBar" style="height:100%;width:0%;background:var(--accent);transition:width 0.1s;border-radius:2px;"></div>
      </div>
    </div>

    <!-- 控制 -->
    <div class="panel-section">
      <div class="panel-title">Controls</div>
      <div class="control-row">
        <button class="btn active" id="btnPlay" title="播放/暂停"><i class="fas fa-pause"></i></button>
        <button class="btn" id="btnReset" title="重置"><i class="fas fa-redo"></i></button>
        <button class="btn" id="btnManual" title="手动模式"><i class="fas fa-sliders-h"></i></button>
      </div>
      <div class="slider-wrap" id="manualSliderWrap" style="display:none;">
        <div class="slider-label"><span>按压力度</span><span id="pressureVal">0%</span></div>
        <input type="range" id="pressureSlider" min="0" max="100" value="0">
      </div>
      <div class="slider-wrap">
        <div class="slider-label"><span>播放速度</span><span id="speedVal">1.0x</span></div>
        <input type="range" id="speedSlider" min="20" max="200" value="100">
      </div>
    </div>

    <!-- IFR 卡片 -->
    <div class="panel-section">
      <div class="ifr-card">
        <div class="ifr-tag">Ideal Final Result</div>
        <div class="ifr-text">
          手套<strong>自行吸附</strong>光滑表面,无需增加任何外部机构——将PE薄膜"软且薄"的缺陷<strong>转化为</strong>能顺应表面产生密封的优势,化被动防滑为<strong>主动吸附</strong>。
        </div>
      </div>
    </div>
  </aside>
</main>

<script>
(function(){
  /* ====== 配置 ====== */
  const NS = 'http://www.w3.org/2000/svg';
  const PIT_COUNT = 5;
  const PIT_CENTERS = [220, 340, 460, 580, 700]; // 凹坑中心X
  const PIT_HALF_TOP = 38;   // 凹坑口半宽
  const PIT_HALF_BOT = 22;   // 凹坑底半宽
  const PIT_DEPTH = 55;      // 凹坑深度
  const GLOVE_Y = 300;       // 手套顶面Y(非凹坑区域底面)
  const GLOVE_THICK = 42;    // 手套厚度(非凹坑区)
  const GLASS_Y = 490;       // 玻璃表面Y
  const MAX_PRESS_OFFSET = 100; // 最大下压位移
  const PARTICLES_PER_PIT = 7;

  /* ====== 状态 ====== */
  let progress = 0;         // 0~1 循环进度
  let playing = true;
  let manualMode = false;
  let speed = 1.0;
  let lastTime = 0;

  /* ====== 工具函数 ====== */
  function easeInOut(t){ return t<0.5?2*t*t:1-Math.pow(-2*t+2,2)/2; }
  function easeOut(t){ return 1-Math.pow(1-t,3); }
  function lerp(a,b,t){ return a+(b-a)*t; }
  function clamp(v,mn,mx){ return Math.max(mn,Math.min(mx,v)); }

  /* 根据全局进度计算各阶段局部进度 */
  function getPhase(p){
    // 阶段边界
    const bounds = [0, 0.10, 0.28, 0.52, 0.74, 1.0];
    const names = ['接触','按压','排气密封','负压吸附','释放'];
    for(let i=0;i<5;i++){
      if(p >= bounds[i] && p < bounds[i+1]){
        return {phase:i, name:names[i], local:clamp((p-bounds[i])/(bounds[i+1]-bounds[i]),0,1)};
      }
    }
    return {phase:4, name:'释放', local:1};
  }

  /* 计算下压位移 */
  function getPressOffset(phaseInfo){
    const {phase, local} = phaseInfo;
    if(phase===0) return lerp(0, MAX_PRESS_OFFSET*0.6, easeInOut(local));
    if(phase===1) return lerp(MAX_PRESS_OFFSET*0.6, MAX_PRESS_OFFSET, easeInOut(local));
    if(phase===2) return MAX_PRESS_OFFSET;
    if(phase===3) return MAX_PRESS_OFFSET;
    return lerp(MAX_PRESS_OFFSET, 0, easeInOut(local));
  }

  /* 计算凹坑变形量 0=未变形 1=完全贴合 */
  function getDeformation(phaseInfo){
    const {phase, local} = phaseInfo;
    if(phase<=1) return 0;
    if(phase===2) return easeOut(local);
    if(phase===3) return 1;
    return lerp(1, 0, easeInOut(local));
  }

  /* 计算负压强度 0~1 */
  function getVacuumIntensity(phaseInfo){
    const {phase, local} = phaseInfo;
    if(phase<=1) return 0;
    if(phase===2) return easeOut(Math.max(0, local-0.3)/0.7);
    if(phase===3){
      // 脉冲效果
      return 0.7 + 0.3*Math.sin(local*Math.PI*3);
    }
    return lerp(0.7, 0, easeInOut(local));
  }

  /* 计算排气程度 0~1 */
  function getAirExpel(phaseInfo){
    const {phase, local} = phaseInfo;
    if(phase<=0) return 0;
    if(phase===1) return easeInOut(local)*0.5;
    if(phase===2) return lerp(0.5, 1, easeOut(local));
    if(phase===3) return 1;
    return lerp(1, 0, easeInOut(local));
  }

  /* ====== 创建SVG元素 ====== */
  const mainSvg = document.getElementById('mainSvg');
  const assemblyGroup = document.getElementById('assemblyGroup');
  const pitsGroup = document.getElementById('pitsGroup');
  const filmsGroup = document.getElementById('filmsGroup');
  const airParticlesGroup = document.getElementById('airParticlesGroup');
  const vacuumGroup = document.getElementById('vacuumGroup');
  const forceArrowsGroup = document.getElementById('forceArrowsGroup');
  const dimensionsGroup = document.getElementById('dimensionsGroup');
  const calloutGroup = document.getElementById('calloutGroup');

  /* 创建凹坑区域 */
  const pitData = [];
  for(let i=0;i<PIT_COUNT;i++){
    const cx = PIT_CENTERS[i];
    // 凹坑填充(空气/负压)
    const pitFill = document.createElementNS(NS,'path');
    pitFill.setAttribute('fill','rgba(136,204,255,0.25)');
    pitFill.setAttribute('stroke','none');
    pitsGroup.appendChild(pitFill);

    // 凹坑壁
    const pitWall = document.createElementNS(NS,'path');
    pitWall.setAttribute('fill','none');
    pitWall.setAttribute('stroke','rgba(212,168,71,0.6)');
    pitWall.setAttribute('stroke-width','1.2');
    pitsGroup.appendChild(pitWall);

    // 薄膜底线(加粗高亮)
    const filmLine = document.createElementNS(NS,'path');
    filmLine.setAttribute('fill','none');
    filmLine.setAttribute('stroke','#d4a847');
    filmLine.setAttribute('stroke-width','2.5');
    filmLine.setAttribute('stroke-linecap','round');
    filmsGroup.appendChild(filmLine);

    // 负压发光
    const vacGlow = document.createElementNS(NS,'ellipse');
    vacGlow.setAttribute('fill','url(#vacuumGlow)');
    vacGlow.setAttribute('opacity','0');
    vacuumGroup.appendChild(vacGlow);

    // 负压小箭头(向上吸力)
    const suctionArrows = [];
    for(let j=0;j<3;j++){
      const arrow = document.createElementNS(NS,'line');
      arrow.setAttribute('stroke','var(--accent)');
      arrow.setAttribute('stroke-width','1.5');
      arrow.setAttribute('marker-end','url(#arrowUp)');
      arrow.setAttribute('opacity','0');
      vacuumGroup.appendChild(arrow);
      suctionArrows.push(arrow);
    }

    // 空气粒子
    const particles = [];
    for(let j=0;j<PARTICLES_PER_PIT;j++){
      const p = document.createElementNS(NS,'circle');
      p.setAttribute('r','2.5');
      p.setAttribute('fill','rgba(136,204,255,0.7)');
      p.setAttribute('opacity','0.8');
      // 初始位置:凹坑内部随机
      const angle = (j/PARTICLES_PER_PIT)*Math.PI*2 + Math.random()*0.5;
      const radius = 8 + Math.random()*12;
      p._baseX = cx + Math.cos(angle)*radius;
      p._baseY = GLOVE_Y + GLOVE_THICK + PIT_DEPTH*0.4 + Math.sin(angle)*radius*0.5;
      p._offsetX = (Math.random()-0.5)*20;
      p._offsetY = -30 - Math.random()*50;
      p._phase = Math.random()*Math.PI*2;
      airParticlesGroup.appendChild(p);
      particles.push(p);
    }

    pitData.push({cx, pitFill, pitWall, filmLine, vacGlow, suctionArrows, particles});
  }

  /* 手套主体路径(含凹坑轮廓) */
  const gloveBody = document.getElementById('gloveBody');

  /* 力箭头 */
  const pressArrow = document.createElementNS(NS,'line');
  pressArrow.setAttribute('stroke','var(--danger)');
  pressArrow.setAttribute('stroke-width','2.5');
  pressArrow.setAttribute('marker-end','url(#arrowDown)');
  pressArrow.setAttribute('opacity','0');
  forceArrowsGroup.appendChild(pressArrow);

  const pressLabel = document.createElementNS(NS,'text');
  pressLabel.setAttribute('font-family','"Oxanium",sans-serif');
  pressLabel.setAttribute('font-size','11');
  pressLabel.setAttribute('fill','var(--danger)');
  pressLabel.setAttribute('font-weight','600');
  pressLabel.setAttribute('opacity','0');
  forceArrowsGroup.appendChild(pressLabel);

  // 侧滑阻力箭头
  const slideArrow = document.createElementNS(NS,'line');
  slideArrow.setAttribute('stroke','var(--muted)');
  slideArrow.setAttribute('stroke-width','2');
  slideArrow.setAttribute('marker-end','url(#arrowRight)');
  slideArrow.setAttribute('opacity','0');
  slideArrow.setAttribute('stroke-dasharray','6 3');
  forceArrowsGroup.appendChild(slideArrow);

  const resistArrow = document.createElementNS(NS,'line');
  resistArrow.setAttribute('stroke','var(--accent)');
  resistArrow.setAttribute('stroke-width','2.5');
  resistArrow.setAttribute('marker-end','url(#arrowRight)');  // 会被动态改方向
  resistArrow.setAttribute('opacity','0');
  forceArrowsGroup.appendChild(resistArrow);

  /* 尺寸标注 */
  function createDimLine(x1,y1,x2,y2,text,side){
    const g = document.createElementNS(NS,'g');
    g.setAttribute('opacity','0.7');
    const line = document.createElementNS(NS,'line');
    line.setAttribute('x1',x1);line.setAttribute('y1',y1);
    line.setAttribute('x2',x2);line.setAttribute('y2',y2);
    line.setAttribute('class','svg-dim-line');
    g.appendChild(line);
    // 端点
    [x1,x2].forEach((x,i)=>{
      const tick = document.createElementNS(NS,'line');
      const yy = i===0?y1:y2;
      tick.setAttribute('x1',x);tick.setAttribute('y1',yy-4);
      tick.setAttribute('x2',x);tick.setAttribute('y2',yy+4);
      tick.setAttribute('stroke','var(--muted)');tick.setAttribute('stroke-width','0.8');
      g.appendChild(tick);
    });
    const txt = document.createElementNS(NS,'text');
    const mx = (x1+x2)/2, my = (y1+y2)/2;
    txt.setAttribute('x', mx + (side==='h'?0:8));
    txt.setAttribute('y', my + (side==='h'?-6:4));
    txt.setAttribute('text-anchor', side==='h'?'middle':'start');
    txt.setAttribute('class','svg-dim-text');
    txt.textContent = text;
    g.appendChild(txt);
    dimensionsGroup.appendChild(g);
    return g;
  }

  /* 六边形俯视图 */
  function buildHexView(){
    const svg = document.getElementById('hexSvg');
    const cx = 140, cy = 140;
    const r = 14;
    const rows = 9, cols = 9;
    const sqrt3 = Math.sqrt(3);
    const dx = sqrt3 * r;
    const dy = 1.5 * r;

    for(let row=0;row<rows;row++){
      for(let col=0;col<cols;col++){
        const hx = cx + (col - cols/2)*dx + (row%2?dx/2:0);
        const hy = cy + (row - rows/2)*dy;
        // 判断是否在指尖区域(中心椭圆内)
        const distX = (hx-cx)/70;
        const distY = (hy-cy)/90;
        const inActive = distX*distX + distY*distY < 1;

        const hex = document.createElementNS(NS,'polygon');
        let pts = '';
        for(let k=0;k<6;k++){
          const angle = Math.PI/6 + k*Math.PI/3;
          const px = hx + r*Math.cos(angle);
          const py = hy + r*Math.sin(angle);
          pts += `${px},${py} `;
        }
        hex.setAttribute('points',pts.trim());
        if(inActive){
          hex.setAttribute('fill','rgba(0,229,255,0.12)');
          hex.setAttribute('stroke','rgba(0,229,255,0.5)');
          hex.setAttribute('stroke-width','1');
        } else {
          hex.setAttribute('fill','rgba(212,168,71,0.08)');
          hex.setAttribute('stroke','rgba(212,168,71,0.2)');
          hex.setAttribute('stroke-width','0.5');
        }
        svg.appendChild(hex);

        // 活跃区域加内部凹坑
        if(inActive){
          const inner = document.createElementNS(NS,'circle');
          inner.setAttribute('cx',hx);inner.setAttribute('cy',hy);
          inner.setAttribute('r',r*0.45);
          inner.setAttribute('fill','rgba(0,229,255,0.08)');
          inner.setAttribute('stroke','rgba(0,229,255,0.3)');
          inner.setAttribute('stroke-width','0.5');
          svg.appendChild(inner);
        }
      }
    }
    // 标签
    const label1 = document.createElementNS(NS,'text');
    label1.setAttribute('x',cx);label1.setAttribute('y',20);
    label1.setAttribute('text-anchor','middle');
    label1.setAttribute('font-family','"Oxanium",sans-serif');
    label1.setAttribute('font-size','9');label1.setAttribute('fill','var(--accent)');
    label1.setAttribute('font-weight','600');label1.setAttribute('letter-spacing','1.5');
    label1.textContent='ACTIVE ZONE';
    svg.appendChild(label1);

    const label2 = document.createElementNS(NS,'text');
    label2.setAttribute('x',cx);label2.setAttribute('y',268);
    label2.setAttribute('text-anchor','middle');
    label2.setAttribute('font-family','"Noto Sans SC",sans-serif');
    label2.setAttribute('font-size','9');label2.setAttribute('fill','var(--muted)');
    label2.textContent='指尖/掌心前1/3 区域';
    svg.appendChild(label2);
  }
  buildHexView();

  /* ====== 更新函数 ====== */
  function updateGloveBody(offset, deformation){
    const baseY = GLOVE_Y + offset;
    const bottomY = baseY + GLOVE_THICK;

    // 构建手套底面路径(含凹坑轮廓)
    let d = `M160,${baseY} L${PIT_CENTERS[0]-PIT_HALF_TOP},${bottomY} `;

    for(let i=0;i<PIT_COUNT;i++){
      const cx = PIT_CENTERS[i];
      const pitOpenY = bottomY;
      const pitBotY = bottomY + PIT_DEPTH * (1 - deformation*0.65);
      const curveAmt = 10 * (1 - deformation);

      // 左壁
      d += `L${cx-PIT_HALF_BOT},${pitBotY - curveAmt} `;
      // 底部曲线
      d += `Q${cx},${pitBotY + curveAmt} ${cx+PIT_HALF_BOT},${pitBotY - curveAmt} `;
      // 右壁到下一个凹坑口或结尾
      if(i < PIT_COUNT-1){
        const nextCx = PIT_CENTERS[i+1];
        d += `L${nextCx-PIT_HALF_TOP},${bottomY} `;
      }
    }
    d += `L800,${bottomY} L800,${baseY} Z`;

    gloveBody.setAttribute('d', d);
  }

  function updatePits(offset, deformation, airExpel, vacuumIntensity){
    const baseY = GLOVE_Y + offset;
    const bottomY = baseY + GLOVE_THICK;

    pitData.forEach((pit,i)=>{
      const cx = pit.cx;
      const pitBotY = bottomY + PIT_DEPTH * (1 - deformation*0.65);
      const curveAmt = 10 * (1 - deformation);

      // 凹坑填充路径
      const fillD = `M${cx-PIT_HALF_TOP},${bottomY} L${cx-PIT_HALF_BOT},${pitBotY - curveAmt} Q${cx},${pitBotY + curveAmt} ${cx+PIT_HALF_BOT},${pitBotY - curveAmt} L${cx+PIT_HALF_TOP},${bottomY} Z`;
      pit.pitFill.setAttribute('d', fillD);

      // 凹坑壁路径
      const wallD = `M${cx-PIT_HALF_TOP},${bottomY} L${cx-PIT_HALF_BOT},${pitBotY - curveAmt} Q${cx},${pitBotY + curveAmt} ${cx+PIT_HALF_BOT},${pitBotY - curveAmt} L${cx+PIT_HALF_TOP},${bottomY}`;
      pit.pitWall.setAttribute('d', wallD);

      // 薄膜底线
      const filmD = `M${cx-PIT_HALF_BOT},${pitBotY - curveAmt} Q${cx},${pitBotY + curveAmt} ${cx+PIT_HALF_BOT},${pitBotY - curveAmt}`;
      pit.filmLine.setAttribute('d', filmD);

      // 薄膜厚度视觉反馈:变形时变亮变细
      if(deformation > 0.3){
        pit.filmLine.setAttribute('stroke-width', lerp(2.5, 1.5, deformation));
        pit.filmLine.setAttribute('stroke', `rgba(0,229,255,${0.3+deformation*0.5})`);
      } else {
        pit.filmLine.setAttribute('stroke-width', '2.5');
        pit.filmLine.setAttribute('stroke', '#d4a847');
      }

      // 负压发光
      const glowCy = (bottomY + pitBotY) / 2;
      pit.vacGlow.setAttribute('cx', cx);
      pit.vacGlow.setAttribute('cy', glowCy);
      pit.vacGlow.setAttribute('rx', PIT_HALF_BOT + 5);
      pit.vacGlow.setAttribute('ry', (pitBotY - bottomY)/2 + 5);
      pit.vacGlow.setAttribute('opacity', vacuumIntensity * 0.8);

      // 凹坑填充颜色:空气→负压
      if(vacuumIntensity > 0.1){
        const r = Math.round(lerp(136, 0, vacuumIntensity));
        const g = Math.round(lerp(204, 229, vacuumIntensity));
        const b = Math.round(lerp(255, 255, vacuumIntensity));
        const a = lerp(0.25, 0.4, vacuumIntensity);
        pit.pitFill.setAttribute('fill', `rgba(${r},${g},${b},${a})`);
      } else {
        pit.pitFill.setAttribute('fill', 'rgba(136,204,255,0.25)');
      }

      // 吸力小箭头
      pit.suctionArrows.forEach((arrow,j)=>{
        const arrowX = cx + (j-1)*14;
        const arrowBot = pitBotY - 5;
        const arrowTop = lerp(pitBotY - 5, bottomY + 5, vacuumIntensity);
        arrow.setAttribute('x1', arrowX);
        arrow.setAttribute('y1', arrowBot);
        arrow.setAttribute('x2', arrowX);
        arrow.setAttribute('y2', arrowTop);
        arrow.setAttribute('opacity', vacuumIntensity * 0.8);
      });

      // 空气粒子
      pit.particles.forEach((p,j)=>{
        const floatT = performance.now()/1000;
        const floatX = Math.sin(floatT*1.5 + p._phase)*2;
        const floatY = Math.cos(floatT*2 + p._phase)*1.5;

        if(airExpel < 0.1){
          // 在凹坑内浮动
          p.setAttribute('cx', p._baseX + offset*0 + floatX);
          p.setAttribute('cy', p._baseY + offset + floatY);
          p.setAttribute('opacity', '0.7');
          p.setAttribute('fill','rgba(136,204,255,0.7)');
          p.setAttribute('r','2.5');
        } else {
          // 被挤出凹坑
          const ex = p._baseX + p._offsetX * airExpel + floatX * (1-airExpel);
          const ey = p._baseY + p._offsetY * airExpel + offset;
          p.setAttribute('cx', ex);
          p.setAttribute('cy', ey);
          p.setAttribute('opacity', clamp(1 - airExpel*0.7, 0.1, 0.7));
          if(airExpel > 0.8){
            p.setAttribute('fill','rgba(136,204,255,0.3)');
            p.setAttribute('r','1.5');
          } else {
            p.setAttribute('fill','rgba(136,204,255,0.7)');
            p.setAttribute('r','2.5');
          }
        }
      });
    });
  }

  function updateForceArrows(offset, phaseInfo){
    const baseY = GLOVE_Y + offset;
    const {phase, local} = phaseInfo;

    // 按压力箭头
    if(phase >= 0 && phase <= 2){
      const arrowOpacity = phase===0? easeInOut(local) : (phase===2? 1-local*0.5 : 1);
      pressArrow.setAttribute('x1','480');
      pressArrow.setAttribute('y1', String(baseY - 40));
      pressArrow.setAttribute('x2','480');
      pressArrow.setAttribute('y2', String(baseY - 10));
      pressArrow.setAttribute('opacity', String(arrowOpacity * 0.9));

      pressLabel.setAttribute('x','500');
      pressLabel.setAttribute('y', String(baseY - 20));
      pressLabel.textContent = 'PRESS';
      pressLabel.setAttribute('opacity', String(arrowOpacity * 0.9));
    } else {
      pressArrow.setAttribute('opacity','0');
      pressLabel.setAttribute('opacity','0');
    }

    // 侧滑尝试 & 阻力(负压吸附阶段)
    if(phase === 3){
      const slideOpacity = 0.4 + 0.3*Math.sin(local*Math.PI*4);
      slideArrow.setAttribute('x1','350');
      slideArrow.setAttribute('y1', String(baseY + GLOVE_THICK + PIT_DEPTH*0.35));
      slideArrow.setAttribute('x2','420');
      slideArrow.setAttribute('y2', String(baseY + GLOVE_THICK + PIT_DEPTH*0.35));
      slideArrow.setAttribute('opacity', String(slideOpacity));

      // 阻力标记(X号表示无法滑动)
      resistArrow.setAttribute('opacity', String(slideOpacity*1.2));
    } else {
      slideArrow.setAttribute('opacity','0');
      resistArrow.setAttribute('opacity','0');
    }
  }

  function updateDimensions(offset, deformation, phaseInfo){
    // 清空
    while(dimensionsGroup.firstChild) dimensionsGroup.removeChild(dimensionsGroup.firstChild);

    const baseY = GLOVE_Y + offset;
    const bottomY = baseY + GLOVE_THICK;
    const {phase} = phaseInfo;

    if(phase >= 1){
      // 凹坑直径标注
      const cx = PIT_CENTERS[2]; // 中间凹坑
      createDimLine(
        cx - PIT_HALF_TOP, bottomY + PIT_DEPTH + 15,
        cx + PIT_HALF_TOP, bottomY + PIT_DEPTH + 15,
        '⌀1.5mm', 'h'
      );

      // 凹坑深度标注
      const dimX = cx + PIT_HALF_TOP + 18;
      createDimLine(
        dimX, bottomY,
        dimX, bottomY + PIT_DEPTH*(1-deformation*0.65),
        '0.15mm', 'v'
      );

      // 薄膜厚度标注(高亮)
      if(deformation > 0.3){
        const filmY = bottomY + PIT_DEPTH*(1-deformation*0.65);
        const g = document.createElementNS(NS,'g');
        g.setAttribute('opacity','0.9');
        const rect = document.createElementNS(NS,'rect');
        rect.setAttribute('x', cx - 8);
        rect.setAttribute('y', filmY - 2);
        rect.setAttribute('width', 16);
        rect.setAttribute('height', 4);
        rect.setAttribute('fill','none');
        rect.setAttribute('stroke','var(--accent)');
        rect.setAttribute('stroke-width','1');
        rect.setAttribute('rx','1');
        g.appendChild(rect);
        const txt = document.createElementNS(NS,'text');
        txt.setAttribute('x', cx + 15);
        txt.setAttribute('y', filmY + 3);
        txt.setAttribute('font-family','"Oxanium",sans-serif');
        txt.setAttribute('font-size','9');
        txt.setAttribute('fill','var(--accent)');
        txt.setAttribute('font-weight','700');
        txt.textContent = '0.025mm';
        g.appendChild(txt);
        dimensionsGroup.appendChild(g);
      }
    }
  }

  function updateCallout(vacuumIntensity, offset){
    const baseY = GLOVE_Y + offset;
    calloutGroup.setAttribute('opacity', String(clamp(vacuumIntensity*1.5, 0, 1)));
    // 更新连接线终点
    const line = document.getElementById('calloutLine');
    if(line){
      line.setAttribute('y2', String(baseY + GLOVE_THICK + PIT_DEPTH*0.3));
    }
  }

  function updatePhaseUI(phaseInfo){
    const items = document.querySelectorAll('.phase-item');
    items.forEach((item,i)=>{
      item.classList.toggle('active', i===phaseInfo.phase);
    });
    document.getElementById('progressBar').style.width = (progress*100)+'%';
  }

  /* ====== 主循环 ====== */
  function tick(timestamp){
    if(!lastTime) lastTime = timestamp;
    const dt = (timestamp - lastTime) / 1000;
    lastTime = timestamp;

    if(playing && !manualMode){
      progress += dt * 0.09 * speed; // ~11秒一个周期
      if(progress > 1) progress = 0;
    }

    const phaseInfo = getPhase(progress);
    const offset = getPressOffset(phaseInfo);
    const deformation = getDeformation(phaseInfo);
    const airExpel = getAirExpel(phaseInfo);
    const vacuum = getVacuumIntensity(phaseInfo);

    // 更新组件位移
    assemblyGroup.setAttribute('transform', `translate(0, ${offset})`);

    // 更新各部分
    updateGloveBody(0, deformation); // 手套体在assembly内部,offset由group处理
    updatePits(0, deformation, airExpel, vacuum);
    updateForceArrows(0, phaseInfo);
    updateDimensions(0, deformation, phaseInfo);
    updateCallout(vacuum, 0);
    updatePhaseUI(phaseInfo);

    // 同步手动滑块
    if(manualMode){
      document.getElementById('pressureSlider').value = Math.round(progress*100);
      document.getElementById('pressureVal').textContent = Math.round(progress*100)+'%';
    }

    requestAnimationFrame(tick);
  }

  /* ====== UI 控制 ====== */
  const btnPlay = document.getElementById('btnPlay');
  const btnReset = document.getElementById('btnReset');
  const btnManual = document.getElementById('btnManual');
  const manualSliderWrap = document.getElementById('manualSliderWrap');
  const pressureSlider = document.getElementById('pressureSlider');
  const speedSlider = document.getElementById('speedSlider');

  btnPlay.addEventListener('click', ()=>{
    playing = !playing;
    btnPlay.innerHTML = playing ? '<i class="fas fa-pause"></i>' : '<i class="fas fa-play"></i>';
    btnPlay.classList.toggle('active', playing);
  });

  btnReset.addEventListener('click', ()=>{
    progress = 0;
    lastTime = 0;
    if(!playing){
      playing = true;
      btnPlay.innerHTML = '<i class="fas fa-pause"></i>';
      btnPlay.classList.toggle('active', true);
    }
  });

  btnManual.addEventListener('click', ()=>{
    manualMode = !manualMode;
    btnManual.classList.toggle('active', manualMode);
    manualSliderWrap.style.display = manualMode ? 'block' : 'none';
    if(manualMode){
      pressureSlider.value = Math.round(progress*100);
      document.getElementById('pressureVal').textContent = Math.round(progress*100)+'%';
    }
  });

  pressureSlider.addEventListener('input', (e)=>{
    if(manualMode){
      progress = parseInt(e.target.value)/100;
      document.getElementById('pressureVal').textContent = e.target.value+'%';
    }
  });

  speedSlider.addEventListener('input', (e)=>{
    speed = parseInt(e.target.value)/100;
    document.getElementById('speedVal').textContent = speed.toFixed(1)+'x';
  });

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

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

这个实现的核心设计思路:

视觉叙事聚焦 IFR(最终理想解):动画直接展示微负压腔的工作原理,不做前后对比,而是通过5个连续阶段——接触→按压→排气密封→负压吸附→释放——完整呈现"化薄为利"的核心创新。

关键视觉引导

  • 颜色语言:琥珀色代表PE手套基材,天青色(#00e5ff)专用于负压效果,当薄膜密封形成时,凹坑内部从淡蓝(空气)渐变为发光的青色(真空),配合脉动效果强调吸附力
  • 薄膜高亮:变形阶段薄膜线从琥珀色过渡到青色发光,视觉上直接指向创新核心
  • 吸力箭头:负压阶段每个凹坑内出现向上的青色箭头,直观表达"主动吸附"
  • 尺寸标注:关键参数(0.025mm 薄膜厚度)在密封形成时以高亮标注浮现

交互控制

  • 播放/暂停、重置、速度调节
  • 手动模式:切换后可通过滑块精确控制按压力度(0~100%),自由探索每个变形细节
  • 阶段指示器实时高亮当前阶段

六边形俯视图:右侧面板展示微凹坑阵列的顶视布局,高亮指尖/掌心活跃区域,帮助理解空间分布。

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