<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>微流通单向释压阀 · 最终理想解原理动画</title>
<link href="https://fonts.googleapis.com/css2?family=Chakra+Petch:wght@300;400;600;700&family=Fira+Code:wght@300;400;500&display=swap" rel="stylesheet">
<style>
:root{
--bg:#060a13;--surface:#0b1019;--card:#101825;--border:#1a2a42;
--text:#c0cede;--dim:#465a73;--cyan:#00e5ff;--amber:#f59e0b;
--pink:#ff4081;--green:#10b981;--metal:#7b8fa0;
}
*{margin:0;padding:0;box-sizing:border-box}
body{background:var(--bg);color:var(--text);font-family:'Fira Code',monospace;
min-height:100vh;display:flex;flex-direction:column;align-items:center;overflow-x:hidden}
.page{width:100%;max-width:1100px;padding:20px 16px 32px}
header{text-align:center;margin-bottom:16px}
header h1{font-family:'Chakra Petch',sans-serif;font-weight:700;font-size:1.75rem;
color:var(--cyan);letter-spacing:.06em;text-shadow:0 0 24px rgba(0,229,255,.25)}
header .sub{font-size:.78rem;color:var(--dim);margin-top:2px;letter-spacing:.12em}
.svg-wrap{width:100%;background:var(--surface);border:1px solid var(--border);
border-radius:12px;overflow:hidden;position:relative}
.svg-wrap svg{display:block;width:100%;height:auto}
.controls{display:flex;align-items:center;justify-content:center;gap:10px;
margin-top:16px;flex-wrap:wrap}
.btn{font-family:'Chakra Petch',sans-serif;font-weight:600;font-size:.8rem;
padding:9px 18px;border:1px solid var(--border);border-radius:7px;
background:var(--card);color:var(--text);cursor:pointer;transition:all .2s;
letter-spacing:.04em;user-select:none}
.btn:hover{border-color:var(--cyan);color:var(--cyan);box-shadow:0 0 12px rgba(0,229,255,.18)}
.btn.primary{background:rgba(0,229,255,.12);border-color:var(--cyan);color:var(--cyan)}
.btn.primary:hover{background:rgba(0,229,255,.22);box-shadow:0 0 18px rgba(0,229,255,.28)}
.btn:disabled{opacity:.35;cursor:not-allowed;pointer-events:none}
.btn.success{border-color:var(--green);color:var(--green);background:rgba(16,185,129,.1)}
.sld{display:flex;align-items:center;gap:6px;margin-left:10px}
.sld label{font-size:.7rem;color:var(--dim);white-space:nowrap}
.sld input[type=range]{-webkit-appearance:none;width:110px;height:3px;
background:var(--border);border-radius:2px;outline:none}
.sld input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;width:13px;height:13px;
border-radius:50%;background:var(--cyan);cursor:pointer;box-shadow:0 0 8px rgba(0,229,255,.45)}
.sld .val{font-size:.72rem;color:var(--cyan);min-width:38px}
.info{margin-top:14px;padding:10px 16px;background:var(--card);border:1px solid var(--border);
border-radius:8px;display:flex;align-items:center;gap:14px;font-size:.76rem;flex-wrap:wrap}
.steps{display:flex;gap:6px;align-items:center}
.sdot{width:7px;height:7px;border-radius:50%;background:var(--border);transition:all .35s}
.sdot.on{background:var(--cyan);box-shadow:0 0 8px rgba(0,229,255,.5)}
.sdot.ok{background:var(--green)}
.stxt{color:var(--dim);flex:1;min-width:160px}
.badge{padding:3px 9px;border-radius:4px;font-size:.66rem;font-weight:600;letter-spacing:.08em;
background:rgba(16,185,129,.13);color:var(--green);border:1px solid rgba(16,185,129,.28);
white-space:nowrap}
@media(prefers-reduced-motion:reduce){*,*::before,*::after{
animation-duration:.01ms!important;transition-duration:.01ms!important}}
</style>
</head>
<body>
<div class="page">
<header>
<h1>微流通单向释压阀</h1>
<div class="sub">IDEAL FINAL RESULT — 原理动画</div>
</header>
<div class="svg-wrap">
<svg id="svg" viewBox="0 0 960 640" xmlns="http://www.w3.org/2000/svg">
<defs>
<!-- 金属渐变 -->
<linearGradient id="gMetal" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#a0b0be"/><stop offset="45%" stop-color="#6d8293"/>
<stop offset="100%" stop-color="#4a5e6e"/>
</linearGradient>
<linearGradient id="gMetalH" x1="0" y1="0" x2="1" y2="0">
<stop offset="0%" stop-color="#7b8fa0"/><stop offset="50%" stop-color="#9db0be"/>
<stop offset="100%" stop-color="#7b8fa0"/>
</linearGradient>
<!-- 硅胶渐变 -->
<linearGradient id="gSilicone" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#ff6b9d"/><stop offset="100%" stop-color="#d81b60"/>
</linearGradient>
<!-- 真空辉光 -->
<radialGradient id="gVacuum" cx=".5" cy=".45" r=".55">
<stop offset="0%" stop-color="#00e5ff" stop-opacity=".22"/>
<stop offset="100%" stop-color="#00e5ff" stop-opacity="0"/>
</radialGradient>
<!-- 发光滤镜 -->
<filter id="fGlow"><feGaussianBlur stdDeviation="4" result="b"/>
<feMerge><feMergeNode in="b"/><feMergeNode in="SourceGraphic"/></feMerge></filter>
<filter id="fGlow2"><feGaussianBlur stdDeviation="6" result="b"/>
<feMerge><feMergeNode in="b"/><feMergeNode in="SourceGraphic"/></feMerge></filter>
<!-- 网格 -->
<pattern id="pGrid" width="24" height="24" patternUnits="userSpaceOnUse">
<path d="M24 0L0 0 0 24" fill="none" stroke="#12203a" stroke-width=".4"/>
</pattern>
<!-- 瓶内填充 -->
<linearGradient id="gContent" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#0a1420" stop-opacity="0"/>
<stop offset="60%" stop-color="#0f1e30" stop-opacity=".6"/>
<stop offset="100%" stop-color="#14283c" stop-opacity=".8"/>
</linearGradient>
</defs>
<!-- 背景网格 -->
<rect width="960" height="640" fill="url(#pGrid)" opacity=".6"/>
<!-- ====== 真空辉光 ====== -->
<g id="vacuumGlow" opacity="1">
<ellipse cx="480" cy="460" rx="120" ry="100" fill="url(#gVacuum)"/>
</g>
<!-- ====== 瓶体剖面 ====== -->
<g id="bottle">
<!-- 左壁 -->
<path d="M340,360 L340,570 Q340,590 360,590 L370,590 Q370,590 370,570 L370,360"
fill="#3a5068" stroke="#4a6580" stroke-width="1"/>
<!-- 右壁 -->
<path d="M590,360 L590,570 Q590,590 570,590 L560,590 Q560,590 560,570 L560,360"
fill="#3a5068" stroke="#4a6580" stroke-width="1"/>
<!-- 底部连接 -->
<path d="M370,590 Q370,600 380,600 L550,600 Q560,600 560,590"
fill="none" stroke="#4a6580" stroke-width="1.2"/>
<!-- 内部空间 -->
<rect x="370" y="360" width="190" height="240" fill="url(#gContent)" opacity=".5"/>
<!-- 瓶口螺纹 (外侧) -->
<g fill="#4a6580" stroke="none">
<rect x="334" y="375" width="8" height="5" rx="1"/>
<rect x="334" y="405" width="8" height="5" rx="1"/>
<rect x="334" y="435" width="8" height="5" rx="1"/>
<rect x="588" y="375" width="8" height="5" rx="1"/>
<rect x="588" y="405" width="8" height="5" rx="1"/>
<rect x="588" y="435" width="8" height="5" rx="1"/>
</g>
<!-- 内容物提示 -->
<text x="465" y="560" font-size="11" fill="#2a4058" text-anchor="middle"
font-family="'Fira Code',monospace" letter-spacing=".1em">VACUUM</text>
</g>
<!-- ====== 密封胶圈 ====== -->
<rect x="345" y="354" width="270" height="7" rx="2"
fill="#2a3848" stroke="#3a5060" stroke-width=".8" id="gasket"/>
<!-- ====== 瓶盖剖面 ====== -->
<g id="capGroup">
<!-- 顶板 -->
<rect x="320" y="310" width="320" height="30" rx="3" fill="url(#gMetalH)" stroke="#5a7a90" stroke-width="1"/>
<!-- 左裙 -->
<rect x="320" y="340" width="28" height="160" rx="2" fill="url(#gMetal)" stroke="#5a7a90" stroke-width=".8"/>
<!-- 右裙 -->
<rect x="612" y="340" width="28" height="160" rx="2" fill="url(#gMetal)" stroke="#5a7a90" stroke-width=".8"/>
<!-- 盖裙内螺纹 -->
<g fill="#5a7a90" stroke="none">
<rect x="346" y="376" width="7" height="5" rx="1"/>
<rect x="346" y="406" width="7" height="5" rx="1"/>
<rect x="346" y="436" width="7" height="5" rx="1"/>
<rect x="607" y="376" width="7" height="5" rx="1"/>
<rect x="607" y="406" width="7" height="5" rx="1"/>
<rect x="607" y="436" width="7" height="5" rx="1"/>
</g>
<!-- 顶板中心孔(背景) -->
<rect x="445" y="310" width="70" height="30" fill="#0c1520" stroke="none"/>
</g>
<!-- ====== 硅胶微阀 ====== -->
<g id="valveGroup">
<!-- 阀体伞面(闭合态) -->
<path id="valveFlange" d="M448,340 Q465,335 480,334 Q495,335 512,340 L512,338
Q495,332 480,331 Q465,332 448,338 Z"
fill="url(#gSilicone)" stroke="#c2185b" stroke-width=".8" opacity=".92"/>
<!-- 阀芯(按压凸起) -->
<g id="valveStem">
<rect x="466" y="296" width="28" height="38" rx="6" fill="url(#gSilicone)"
stroke="#c2185b" stroke-width=".8" opacity=".92" id="stemRect"/>
<!-- 顶部按压指示环 -->
<ellipse cx="480" cy="298" rx="10" ry="3" fill="none" stroke="#ff80ab"
stroke-width="1" opacity=".7" id="stemRing"/>
</g>
<!-- 密封边缘标注线 -->
<line id="sealLine1" x1="448" y1="339" x2="440" y2="348" stroke="#ff80ab"
stroke-width=".6" stroke-dasharray="2,2" opacity=".5"/>
<line id="sealLine2" x1="512" y1="339" x2="520" y2="348" stroke="#ff80ab"
stroke-width=".6" stroke-dasharray="2,2" opacity=".5"/>
</g>
<!-- ====== 手指 ====== -->
<g id="finger" opacity="0" transform="translate(0,0)">
<path d="M462,230 Q462,210 480,205 Q498,210 498,230 L498,275 Q498,282 492,282
L468,282 Q462,282 462,275 Z"
fill="#e8b89d" stroke="#c9956e" stroke-width="1.2" rx="8"/>
<path d="M466,240 Q466,218 480,213 Q494,218 494,240"
fill="none" stroke="#d4a07a" stroke-width=".6" opacity=".5"/>
<!-- 按压力箭头 -->
<g id="forceArrow" opacity=".8">
<line x1="480" y1="188" x2="480" y2="218" stroke="var(--amber)" stroke-width="2.5"
filter="url(#fGlow)"/>
<polygon points="474,218 480,228 486,218" fill="var(--amber)" filter="url(#fGlow)"/>
<text x="480" y="183" font-size="10" fill="var(--amber)" text-anchor="middle"
font-family="'Chakra Petch',sans-serif" font-weight="600">F</text>
</g>
</g>
<!-- ====== 真空力箭头 ====== -->
<g id="vacuumArrows" opacity=".7">
<g class="vArr" transform="translate(420,400)">
<line x1="0" y1="0" x2="0" y2="30" stroke="#00e5ff" stroke-width="1.5" opacity=".6"/>
<polygon points="-4,30 0,38 4,30" fill="#00e5ff" opacity=".6"/>
</g>
<g class="vArr" transform="translate(480,390)">
<line x1="0" y1="0" x2="0" y2="35" stroke="#00e5ff" stroke-width="1.5" opacity=".7"/>
<polygon points="-4,35 0,43 4,35" fill="#00e5ff" opacity=".7"/>
</g>
<g class="vArr" transform="translate(540,400)">
<line x1="0" y1="0" x2="0" y2="30" stroke="#00e5ff" stroke-width="1.5" opacity=".6"/>
<polygon points="-4,30 0,38 4,30" fill="#00e5ff" opacity=".6"/>
</g>
<!-- 向上吸力箭头(作用于盖) -->
<g class="vArr" transform="translate(400,330)">
<line x1="0" y1="20" x2="0" y2="-5" stroke="#00bcd4" stroke-width="1.2" opacity=".5"/>
<polygon points="-3,-5 0,-12 3,-5" fill="#00bcd4" opacity=".5"/>
</g>
<g class="vArr" transform="translate(560,330)">
<line x1="0" y1="20" x2="0" y2="-5" stroke="#00bcd4" stroke-width="1.2" opacity=".5"/>
<polygon points="-3,-5 0,-12 3,-5" fill="#00bcd4" opacity=".5"/>
</g>
</g>
<!-- ====== 进气粒子容器 ====== -->
<g id="particles"></g>
<!-- ====== 声波涟漪容器 ====== -->
<g id="ripples"></g>
<!-- ====== 压差仪表 ====== -->
<g id="pressureGauge" transform="translate(830,200)">
<rect x="-40" y="-10" width="80" height="260" rx="6" fill="#0d1520"
stroke="#1e2d4a" stroke-width="1"/>
<text x="0" y="8" font-size="9" fill="#465a73" text-anchor="middle"
font-family="'Chakra Petch',sans-serif" letter-spacing=".08em">压差 ΔP</text>
<!-- 刻度 -->
<line x1="-25" y1="30" x2="25" y2="30" stroke="#1e2d4a" stroke-width=".6"/>
<text x="30" y="34" font-size="8" fill="#465a73" font-family="'Fira Code',monospace">1.0</text>
<line x1="-25" y1="82" x2="25" y2="82" stroke="#1e2d4a" stroke-width=".6"/>
<text x="30" y="86" font-size="8" fill="#465a73" font-family="'Fira Code',monospace">0.5</text>
<line x1="-25" y1="134" x2="25" y2="134" stroke="#1e2d4a" stroke-width=".6"/>
<text x="30" y="138" font-size="8" fill="#465a73" font-family="'Fira Code',monospace">0</text>
<!-- 柱状指示 -->
<rect id="pBar" x="-18" y="30" width="36" height="104" rx="3"
fill="rgba(0,229,255,.25)" stroke="none"/>
<rect id="pBarFill" x="-18" y="30" width="36" height="104" rx="3"
fill="rgba(0,229,255,.4)" stroke="none"/>
<!-- 数值 -->
<text id="pVal" x="0" y="165" font-size="14" fill="#00e5ff" text-anchor="middle"
font-family="'Chakra Petch',sans-serif" font-weight="700">0.80</text>
<text x="0" y="180" font-size="8" fill="#465a73" text-anchor="middle"
font-family="'Fira Code',monospace">atm</text>
<!-- 状态标签 -->
<g id="pStatus">
<rect x="-28" y="195" width="56" height="20" rx="4" fill="rgba(0,229,255,.12)"
stroke="rgba(0,229,255,.3)" stroke-width=".8"/>
<text id="pStatusText" x="0" y="209" font-size="8" fill="#00e5ff" text-anchor="middle"
font-family="'Chakra Petch',sans-serif" font-weight="600">负压锁定</text>
</g>
</g>
<!-- ====== 标注文字 ====== -->
<g id="annotations" font-family="'Fira Code',monospace">
<!-- 硅胶微阀标注 -->
<g id="annValve" opacity=".85">
<line x1="512" y1="315" x2="575" y2="280" stroke="#ff80ab" stroke-width=".7"
stroke-dasharray="3,2"/>
<circle cx="512" cy="315" r="2" fill="#ff80ab"/>
<text x="580" y="278" font-size="10" fill="#ff80ab" font-weight="500">硅胶微阀</text>
<text x="580" y="292" font-size="8" fill="#9c5070">Umbrella Valve</text>
</g>
<!-- 密封面标注 -->
<g id="annSeal" opacity=".7">
<line x1="448" y1="345" x2="395" y2="375" stroke="#ff80ab" stroke-width=".5"
stroke-dasharray="2,2"/>
<text x="340" y="383" font-size="8.5" fill="#9c5070">密封边缘</text>
</g>
<!-- 马口铁瓶盖标注 -->
<g opacity=".6">
<line x1="620" y1="325" x2="660" y2="310" stroke="#5a7a90" stroke-width=".5"
stroke-dasharray="2,2"/>
<text x="665" y="314" font-size="8.5" fill="#5a7a90">马口铁盖体</text>
</g>
<!-- 螺纹标注 -->
<g opacity=".5">
<line x1="352" y1="440" x2="290" y2="460" stroke="#5a7a90" stroke-width=".5"
stroke-dasharray="2,2"/>
<text x="240" y="464" font-size="8" fill="#5a7a90">螺纹摩擦</text>
</g>
<!-- 真空区标注 -->
<g id="annVacuum" opacity=".7">
<text x="465" y="475" font-size="11" fill="#0097a7" text-anchor="middle"
font-family="'Chakra Petch',sans-serif" font-weight="600"
filter="url(#fGlow)">负压区</text>
</g>
<!-- IFR 解耦标注 -->
<g id="annIFR" opacity="0" transform="translate(160,460)">
<rect x="-10" y="-14" width="185" height="72" rx="6" fill="rgba(16,185,129,.08)"
stroke="rgba(16,185,129,.25)" stroke-width=".8"/>
<text x="0" y="0" font-size="9.5" fill="#10b981"
font-family="'Chakra Petch',sans-serif" font-weight="600">IFR 解耦原理</text>
<text x="0" y="18" font-size="8" fill="#4a8a70">❶ 微力破真空 → ΔP=0</text>
<text x="0" y="33" font-size="8" fill="#4a8a70">❷ 常规力拧盖 → 轻松旋开</text>
<text x="0" y="48" font-size="7.5" fill="#3a6a55" font-style="italic">
复合难题 → 两步简单动作</text>
</g>
</g>
<!-- ====== 旋开箭头 ====== -->
<g id="unscrewArrow" opacity="0">
<path d="M640,380 Q660,360 650,340" fill="none" stroke="var(--green)"
stroke-width="2" stroke-linecap="round" filter="url(#fGlow2)"/>
<polygon points="646,342 650,332 656,342" fill="var(--green)" filter="url(#fGlow2)"/>
<text x="668" y="358" font-size="9" fill="var(--green)"
font-family="'Chakra Petch',sans-serif" font-weight="600">轻松旋开</text>
</g>
<!-- ====== 点击提示 ====== -->
<g id="clickHint" opacity=".6" transform="translate(480,270)">
<ellipse cx="0" cy="0" rx="22" ry="8" fill="none" stroke="#ff80ab"
stroke-width="1" stroke-dasharray="3,2">
<animate attributeName="rx" values="22;26;22" dur="2s" repeatCount="indefinite"/>
<animate attributeName="ry" values="8;10;8" dur="2s" repeatCount="indefinite"/>
</ellipse>
<text x="0" y="-16" font-size="7.5" fill="#ff80ab" text-anchor="middle"
font-family="'Fira Code',monospace">点击按压</text>
</g>
<!-- 交互热区 -->
<rect id="hotspot" x="450" y="280" width="60" height="60" fill="transparent"
cursor="pointer" style="pointer-events:all"/>
</svg>
</div>
<!-- 控制面板 -->
<div class="controls">
<button class="btn primary" id="btnPlay" onclick="playDemo()">开始演示</button>
<button class="btn" id="btnPress" onclick="doPress()" disabled>按压微阀</button>
<button class="btn" id="btnOpen" onclick="doOpen()" disabled>旋开瓶盖</button>
<button class="btn" id="btnReset" onclick="doReset()">重置</button>
<div class="sld">
<label>初始压差</label>
<input type="range" id="sldPressure" min="0.2" max="1" step="0.05" value="0.8"
oninput="updatePressure(this.value)"/>
<span class="val" id="sldVal">0.80 atm</span>
</div>
</div>
<!-- 信息栏 -->
<div class="info">
<div class="steps">
<div class="sdot" id="d0"></div>
<div class="sdot" id="d1"></div>
<div class="sdot" id="d2"></div>
<div class="sdot" id="d3"></div>
</div>
<span class="stxt" id="statusText">等待操作 — 点击「开始演示」或直接点击瓶盖阀门区域</span>
<span class="badge" id="ifrBadge" style="opacity:0">IFR 理想解</span>
</div>
</div>
<script>
/* ===== 状态管理 ===== */
const S={IDLE:0,PRESSING:1,OPENING:2,EQUALIZED:3,UNSCREWING:4,DONE:5};
let state=S.IDLE, pressure=0.80, animT=0, animTarget=0, rafId=null;
let particles=[], ripples=[], vacPulse=0;
/* ===== DOM 引用 ===== */
const $=id=>document.getElementById(id);
const svg=$('svg');
const valveFlange=$('valveFlange');
const valveStem=$('valveStem');
const stemRect=$('stemRect');
const stemRing=$('stemRing');
const fingerG=$('finger');
const vacuumGlow=$('vacuumGlow');
const vacuumArrows=$('vacuumArrows');
const pBarFill=$('pBarFill');
const pVal=$('pVal');
const pStatusText=$('pStatusText');
const pStatus=$('pStatus');
const annIFR=$('annIFR');
const annVacuum=$('annVacuum');
const unscrewArrow=$('unscrewArrow');
const clickHint=$('clickHint');
const capGroup=$('capGroup');
const particlesG=$('particles');
const ripplesG=$('ripples');
const hotspot=$('hotspot');
/* ===== 瓶盖偏移(旋开时) ===== */
let capOffsetY=0, capRotation=0;
/* ===== 辅助函数 ===== */
function lerp(a,b,t){return a+(b-a)*t}
function clamp(v,mn,mx){return Math.max(mn,Math.min(mx,v))}
/* 伞面路径生成: t=0闭合, t=1全开 */
function flangePath(t){
const cx=480, yBase=339;
const edgeLift=t*14, centerDip=t*8;
const halfW=32;
const pts=[
{x:cx-halfW, y:yBase-edgeLift},
{x:cx-halfW*0.6, y:yBase+centerDip*0.3},
{x:cx, y:yBase+centerDip},
{x:cx+halfW*0.6, y:yBase+centerDip*0.3},
{x:cx+halfW, y:yBase-edgeLift}
];
return `M${pts[0].x},${pts[0].y} Q${pts[1].x},${pts[1].y} ${pts[2].x},${pts[2].y}
Q${pts[3].x},${pts[3].y} ${pts[4].x},${pts[4].y}
L${pts[4].x},${pts[4].y-2}
Q${pts[3].x},${pts[3].y-2} ${pts[2].x},${pts[2].y-2}
Q${pts[1].x},${pts[1].y-2} ${pts[0].x},${pts[0].y-2} Z`;
}
/* ===== 压差仪表更新 ===== */
function updateGauge(p){
const h=clamp(p,0,1)*104;
pBarFill.setAttribute('height',h);
pBarFill.setAttribute('y',134-h);
pVal.textContent=p.toFixed(2);
if(p>0.3){
pStatusText.textContent='负压锁定';
pStatus.querySelector('rect').setAttribute('fill','rgba(0,229,255,.12)');
pStatus.querySelector('rect').setAttribute('stroke','rgba(0,229,255,.3)');
pStatusText.setAttribute('fill','#00e5ff');
}else if(p>0.05){
pStatusText.textContent='正在释压';
pStatus.querySelector('rect').setAttribute('fill','rgba(245,158,11,.12)');
pStatus.querySelector('rect').setAttribute('stroke','rgba(245,158,11,.3)');
pStatusText.setAttribute('fill','#f59e0b');
}else{
pStatusText.textContent='气压平衡';
pStatus.querySelector('rect').setAttribute('fill','rgba(16,185,129,.12)');
pStatus.querySelector('rect').setAttribute('stroke','rgba(16,185,129,.3)');
pStatusText.setAttribute('fill','#10b981');
}
}
/* ===== 粒子系统 ===== */
function spawnParticle(){
const cx=480, startY=295+Math.random()*10;
const targetY=460+Math.random()*80;
const spread=(Math.random()-0.5)*50;
const p={
x:cx+(Math.random()-0.5)*16,
y:startY,
vx:spread*0.02,
vy:1.5+Math.random()*2,
targetY:targetY,
r:1.5+Math.random()*1.5,
life:1,
el:document.createElementNS('http://www.w3.org/2000/svg','circle')
};
p.el.setAttribute('r',p.r);
p.el.setAttribute('fill','#00e5ff');
p.el.setAttribute('opacity','0.8');
particlesG.appendChild(p.el);
particles.push(p);
}
function updateParticles(){
for(let i=particles.length-1;i>=0;i--){
const p=particles[i];
p.x+=p.vx;
p.y+=p.vy;
p.vx*=0.99;
if(p.y>p.targetY) p.life-=0.03;
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',clamp(p.life*0.8,0,1));
}
}
}
/* ===== 涟漪系统 ===== */
function spawnRipple(){
const r={
cx:480, cy:310, radius:5, maxRadius:60+Math.random()*30,
life:1,
el:document.createElementNS('http://www.w3.org/2000/svg','circle')
};
r.el.setAttribute('fill','none');
r.el.setAttribute('stroke','#00e5ff');
r.el.setAttribute('stroke-width','1.5');
ripplesG.appendChild(r.el);
ripples.push(r);
}
function updateRipples(){
for(let i=ripples.length-1;i>=0;i--){
const r=ripples[i];
r.radius+=1.2;
r.life=1-(r.radius/r.maxRadius);
if(r.life<=0){
r.el.remove();
ripples.splice(i,1);
}else{
r.el.setAttribute('cx',r.cx);
r.el.setAttribute('cy',r.cy);
r.el.setAttribute('r',r.radius);
r.el.setAttribute('opacity',clamp(r.life*0.6,0,1));
r.el.setAttribute('stroke-width',clamp(r.life*1.5,0.3,2));
}
}
}
/* ===== 真空脉冲 ===== */
function updateVacuumPulse(){
vacPulse+=0.03;
const s=1+Math.sin(vacPulse)*0.05;
const o=0.5+Math.sin(vacPulse)*0.15;
vacuumGlow.setAttribute('transform',`translate(480,460) scale(${s}) translate(-480,-460)`);
vacuumGlow.setAttribute('opacity',clamp(o*pressure,0,1));
}
/* ===== 主动画循环 ===== */
let lastTime=0, pressProgress=0, openProgress=0;
let particleTimer=0, rippleTimer=0;
let currentPressure=0.8;
function animate(time){
const dt=Math.min((time-lastTime)/1000,0.05);
lastTime=time;
/* 真空脉冲(仅IDLE/PRESSING态) */
if(state<=S.PRESSING) updateVacuumPulse();
/* 按压动画 */
if(state===S.PRESSING){
pressProgress=clamp(pressProgress+dt*2.5,0,1);
const eased=1-Math.pow(1-pressProgress,3);
/* 手指下移 */
const fingerY=lerp(0,28,eased);
fingerG.setAttribute('transform',`translate(0,${fingerY})`);
fingerG.setAttribute('opacity',1);
/* 阀芯下移 */
const stemY=lerp(0,6,eased);
valveStem.setAttribute('transform',`translate(0,${stemY})`);
/* 伞面变形 */
valveFlange.setAttribute('d',flangePath(eased*0.3));
/* 点击提示消失 */
clickHint.setAttribute('opacity',Math.max(0,1-pressProgress*3));
if(pressProgress>=1){
state=S.OPENING;
pressProgress=0;
setStatus(2,'阀口微变形 → 外部空气涌入瓶内');
setDots(2);
$('btnPress').disabled=true;
}
}
/* 开阀进气动画 */
if(state===S.OPENING){
openProgress=clamp(openProgress+dt*1.2,0,1);
const eased=1-Math.pow(1-openProgress,2);
/* 伞面继续变形 */
valveFlange.setAttribute('d',flangePath(0.3+eased*0.7));
/* 阀芯保持下压并微弹 */
const bounce=Math.sin(openProgress*Math.PI)*2;
valveStem.setAttribute('transform',`translate(0,${6-bounce})`);
/* 压差下降 */
currentPressure=lerp(pressure,0,eased);
updateGauge(currentPressure);
/* 真空箭头淡出 */
vacuumArrows.setAttribute('opacity',clamp(1-eased*1.5,0,0.7));
/* 真空辉光淡出 */
vacuumGlow.setAttribute('opacity',clamp((1-eased)*0.7,0,1));
annVacuum.setAttribute('opacity',clamp(1-eased*1.5,0,0.7));
/* 生成粒子 */
particleTimer+=dt;
if(particleTimer>0.04 && eased<0.9){
spawnParticle();
if(Math.random()<0.3) spawnParticle();
particleTimer=0;
}
/* 生成涟漪 */
rippleTimer+=dt;
if(rippleTimer>0.15 && eased<0.7){
spawnRipple();
rippleTimer=0;
}
if(openProgress>=1){
state=S.EQUALIZED;
openProgress=0;
setStatus(3,'压差归零 — 真空吸力已消除,可轻松旋开');
setDots(3);
$('btnOpen').disabled=false;
/* IFR标注渐入 */
annIFR.setAttribute('opacity',1);
$('ifrBadge').style.opacity='1';
/* 延迟自动提示旋开 */
setTimeout(()=>{
if(state===S.EQUALIZED){
unscrewArrow.setAttribute('opacity',1);
}
},800);
}
}
/* 旋开动画 */
if(state===S.UNSCREWING){
openProgress=clamp(openProgress+dt*0.8,0,1);
const eased=1-Math.pow(1-openProgress,3);
/* 瓶盖上移+微旋 */
capOffsetY=lerp(0,-80,eased);
capRotation=lerp(0,15,eased);
capGroup.setAttribute('transform',
`translate(0,${capOffsetY}) rotate(${capRotation},480,340)`);
/* 阀门也随盖移动 */
$('valveGroup').setAttribute('transform',
`translate(0,${capOffsetY}) rotate(${capRotation},480,340)`);
/* 手指淡出 */
fingerG.setAttribute('opacity',Math.max(0,1-eased*2));
/* 旋开箭头淡出 */
unscrewArrow.setAttribute('opacity',Math.max(0,1-eased*2));
if(openProgress>=1){
state=S.DONE;
setStatus(4,'完成 — 瓶盖已轻松旋开,IFR 理想解验证成功');
$('btnOpen').disabled=true;
$('btnOpen').classList.add('success');
}
}
/* 粒子/涟漪更新 */
updateParticles();
updateRipples();
rafId=requestAnimationFrame(animate);
}
/* ===== 状态UI ===== */
function setStatus(step,txt){
$('statusText').textContent=txt;
}
function setDots(n){
for(let i=0;i<4;i++){
const d=$('d'+i);
d.className='sdot'+(i<n?' ok':'')+(i===n?' on':'');
}
}
/* ===== 交互操作 ===== */
function playDemo(){
if(state!==S.IDLE)return;
$('btnPlay').disabled=true;
doPress();
}
function doPress(){
if(state!==S.IDLE)return;
state=S.PRESSING;
pressProgress=0;
currentPressure=pressure;
setStatus(1,'手指下压微阀中心 → 伞面阀口产生微观弹性变形');
setDots(1);
$('btnPress').disabled=true;
$('btnPlay').disabled=true;
}
function doOpen(){
if(state!==S.EQUALIZED)return;
state=S.UNSCREWING;
openProgress=0;
setStatus(4,'摩擦力已足够轻松旋开瓶盖...');
$('btnOpen').disabled=true;
}
function doReset(){
state=S.IDLE;
pressProgress=0;openProgress=0;capOffsetY=0;capRotation=0;
currentPressure=pressure;
/* 清除粒子/涟漪 */
particles.forEach(p=>p.el.remove());particles=[];
ripples.forEach(r=>r.el.remove());ripples=[];
/* 复位SVG元素 */
valveFlange.setAttribute('d',flangePath(0));
valveStem.setAttribute('transform','translate(0,0)');
fingerG.setAttribute('opacity',0);
fingerG.setAttribute('transform','translate(0,0)');
vacuumArrows.setAttribute('opacity',0.7);
vacuumGlow.setAttribute('opacity',0.5);
annVacuum.setAttribute('opacity',0.7);
annIFR.setAttribute('opacity',0);
unscrewArrow.setAttribute('opacity',0);
clickHint.setAttribute('opacity',0.6);
capGroup.setAttribute('transform','translate(0,0)');
$('valveGroup').setAttribute('transform','translate(0,0)');
updateGauge(pressure);
setStatus(0,'等待操作 — 点击「开始演示」或直接点击瓶盖阀门区域');
setDots(0);
$('btnPlay').disabled=false;
$('btnPress').disabled=false;
$('btnOpen').disabled=true;
$('btnOpen').classList.remove('success');
$('ifrBadge').style.opacity='0';
}
function updatePressure(v){
pressure=parseFloat(v);
currentPressure=state>=S.OPENING?currentPressure:pressure;
$('sldVal').textContent=pressure.toFixed(2)+' atm';
if(state===S.IDLE) updateGauge(pressure);
}
/* 热区点击 */
hotspot.addEventListener('click',()=>{
if(state===S.IDLE) doPress();
});
/* 热区悬停 */
hotspot.addEventListener('mouseenter',()=>{
if(state===S.IDLE) clickHint.setAttribute('opacity','1');
});
hotspot.addEventListener('mouseleave',()=>{
if(state===S.IDLE) clickHint.setAttribute('opacity','0.6');
});
/* ===== 初始化 ===== */
updateGauge(pressure);
setDots(0);
$('btnOpen').disabled=true;
rafId=requestAnimationFrame(animate);
</script>
</body>
</html>
这个动画实现了一个完整的「微流通单向释压阀」工作原理演示,核心设计思路如下:
视觉引导策略
- 硅胶微阀使用粉红色高亮,与冷色调金属瓶盖形成鲜明对比,一眼锁定核心创新点
- 真空负压以青色脉冲辉光+方向箭头可视化,让不可见的压差变得直观可感
- 进气过程通过粒子流+涟漪波纹双重反馈,配合"哧"声的视觉化(同心圆扩散),强化瞬间释压的"顿悟感"
- 右侧压差仪表实时变化,从"负压锁定"→"正在释压"→"气压平衡",数值与颜色同步演变
IFR 思想表达
- 不做前后对比,直接演示理想解如何工作
- 当压差归零后,自动浮现「IFR 解耦原理」标注框,将核心逻辑一语道破:复合难题 → 两步简单动作
- 旋开箭头在释压完成后才出现,强调"先破真空、再拧盖"的时序解耦
交互设计
- 三种触发方式:点击「开始演示」按钮 / 点击「按压微阀」按钮 / 直接点击 SVG 中阀门区域
- 压差滑块可调节初始真空度(0.2~1.0 atm),体验不同压差下的释压效果
- 四步状态指示灯 + 底部文字实时解说当前物理过程
积分规则:第一轮对话扣减6分,后续每轮扣4分
等待动画代码生成...
