<!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=Chakra+Petch:wght@400;600;700&family=Noto+Sans+SC:wght@300;400;500;700&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: #050910;
--fg: #d8e2f0;
--muted: #3e5274;
--accent: #00ddb5;
--accent2: #ff8c38;
--card: #0a1220;
--border: #152040;
--surface: #0d1628;
}
*{margin:0;padding:0;box-sizing:border-box}
body{
background:var(--bg);color:var(--fg);
font-family:'Noto Sans SC',sans-serif;
min-height:100vh;display:flex;flex-direction:column;align-items:center;
overflow-x:hidden;
background-image:
radial-gradient(ellipse 80% 50% at 20% 80%, rgba(0,221,181,0.03) 0%, transparent 60%),
radial-gradient(ellipse 60% 40% at 85% 30%, rgba(255,140,56,0.025) 0%, transparent 50%);
}
.page-header{text-align:center;padding:28px 20px 8px;width:100%;max-width:1360px}
.page-header h1{
font-family:'Chakra Petch',sans-serif;font-weight:700;font-size:clamp(1rem,2.5vw,1.5rem);
letter-spacing:3px;color:var(--accent);text-transform:uppercase;
}
.page-header .sub{
font-size:0.82rem;color:var(--muted);margin-top:4px;font-weight:300;letter-spacing:1px;
}
.main-wrap{
width:100%;max-width:1360px;display:flex;flex-direction:column;align-items:center;
gap:18px;padding:0 16px 40px;
}
.svg-box{
width:100%;background:var(--card);border:1px solid var(--border);border-radius:14px;
overflow:hidden;position:relative;
box-shadow:0 0 60px rgba(0,221,181,0.04), inset 0 0 80px rgba(0,0,0,0.3);
}
.svg-box svg{width:100%;height:auto;display:block}
.ctrl-grid{
width:100%;display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:14px;
}
.ctrl-card{
background:var(--card);border:1px solid var(--border);border-radius:12px;padding:18px 20px;
transition:border-color .3s;
}
.ctrl-card:hover{border-color:#1e3860}
.ctrl-card h3{
font-family:'Chakra Petch',sans-serif;font-size:0.78rem;font-weight:600;
color:var(--muted);text-transform:uppercase;letter-spacing:2px;margin-bottom:12px;
}
.ctrl-row{display:flex;align-items:center;gap:12px;margin-bottom:8px}
.ctrl-row label{font-size:0.8rem;color:#7a90b0;min-width:90px;white-space:nowrap}
.ctrl-row input[type=range]{
flex:1;-webkit-appearance:none;appearance:none;height:6px;border-radius:3px;
background:var(--border);outline:none;cursor:pointer;
}
.ctrl-row input[type=range]::-webkit-slider-thumb{
-webkit-appearance:none;width:18px;height:18px;border-radius:50%;
background:var(--accent);border:2px solid var(--bg);cursor:pointer;
box-shadow:0 0 8px rgba(0,221,181,0.4);
}
.ctrl-row .val{
font-family:'Chakra Petch',sans-serif;font-size:0.85rem;font-weight:600;
color:var(--accent);min-width:44px;text-align:right;
}
.btn-row{display:flex;gap:10px;margin-top:10px;flex-wrap:wrap}
.btn{
font-family:'Chakra Petch',sans-serif;font-size:0.78rem;font-weight:600;
letter-spacing:1px;text-transform:uppercase;
padding:8px 18px;border-radius:8px;border:1px solid var(--border);
background:var(--surface);color:var(--fg);cursor:pointer;
transition:all .25s;display:inline-flex;align-items:center;gap:6px;
}
.btn:hover{border-color:var(--accent);color:var(--accent);background:#0e1a2e}
.btn.active{border-color:var(--accent);color:var(--bg);background:var(--accent)}
.indicator{
display:flex;align-items:center;gap:8px;padding:6px 12px;border-radius:6px;
background:rgba(0,221,181,0.06);border:1px solid rgba(0,221,181,0.15);
font-size:0.75rem;color:var(--accent);margin-top:6px;
}
.indicator i{font-size:0.7rem}
.info-badge{
font-size:0.7rem;padding:4px 10px;border-radius:4px;
background:rgba(255,140,56,0.08);border:1px solid rgba(255,140,56,0.18);
color:var(--accent2);display:inline-block;margin-top:6px;
}
@keyframes pulseGlow{
0%,100%{opacity:0.6}50%{opacity:1}
}
@keyframes rayFlow{
from{stroke-dashoffset:32}to{stroke-dashoffset:0}
}
@keyframes fadeSlideIn{
from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}
}
@media(max-width:640px){
.ctrl-grid{grid-template-columns:1fr}
.page-header h1{font-size:1rem}
}
</style>
</head>
<body>
<header class="page-header">
<h1>Prone Periscope Optics</h1>
<p class="sub">IFR 最终理想解 · 零砸脸风险 · 光学亮度衰减 · 重力资源化利用</p>
</header>
<main class="main-wrap">
<div class="svg-box">
<svg id="mainSvg" viewBox="0 0 1200 700" xmlns="http://www.w3.org/2000/svg">
<defs>
<!-- 发光滤镜 -->
<filter id="glow" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur stdDeviation="5" result="b"/>
<feMerge><feMergeNode in="b"/><feMergeNode in="SourceGraphic"/></feMerge>
</filter>
<filter id="glowBig" x="-80%" y="-80%" width="260%" height="260%">
<feGaussianBlur stdDeviation="12" result="b"/>
<feMerge><feMergeNode in="b"/><feMergeNode in="SourceGraphic"/></feMerge>
</filter>
<filter id="glowSoft" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur stdDeviation="3" result="b"/>
<feMerge><feMergeNode in="b"/><feMergeNode in="SourceGraphic"/></feMerge>
</filter>
<!-- 渐变 -->
<linearGradient id="rayWarm" x1="0" y1="1" x2="0" y2="0">
<stop offset="0%" stop-color="#ff8c38"/><stop offset="100%" stop-color="#ffaa55"/>
</linearGradient>
<linearGradient id="rayCool" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#00ddb5"/><stop offset="100%" stop-color="#00aa90"/>
</linearGradient>
<linearGradient id="rayTransmit" x1="0" y1="0" x2="1" y2="0">
<stop offset="0%" stop-color="#ff8c38" stop-opacity="0.5"/>
<stop offset="100%" stop-color="#ff8c38" stop-opacity="0"/>
</linearGradient>
<linearGradient id="mirrorShine" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#cceeff"/><stop offset="40%" stop-color="#88bbdd"/>
<stop offset="100%" stop-color="#5599bb"/>
</linearGradient>
<linearGradient id="beamSplitShine" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#aaeeff" stop-opacity="0.5"/>
<stop offset="50%" stop-color="#77ccdd" stop-opacity="0.3"/>
<stop offset="100%" stop-color="#55aacc" stop-opacity="0.5"/>
</linearGradient>
<linearGradient id="phoneGrad" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#ffaa55"/><stop offset="100%" stop-color="#ff7722"/>
</linearGradient>
<radialGradient id="eyeComfort" cx="50%" cy="50%" r="50%">
<stop offset="0%" stop-color="#00ddb5" stop-opacity="0.25"/>
<stop offset="100%" stop-color="#00ddb5" stop-opacity="0"/>
</radialGradient>
<!-- 网格 -->
<pattern id="grid" width="50" height="50" patternUnits="userSpaceOnUse">
<path d="M 50 0 L 0 0 0 50" fill="none" stroke="#0c1628" stroke-width="0.5"/>
</pattern>
<!-- 虚线标注样式 -->
<marker id="arrowTeal" viewBox="0 0 10 6" refX="10" refY="3" markerWidth="8" markerHeight="5" orient="auto">
<path d="M0,0 L10,3 L0,6 Z" fill="#00ddb5" opacity="0.7"/>
</marker>
<marker id="arrowAmber" viewBox="0 0 10 6" refX="10" refY="3" markerWidth="8" markerHeight="5" orient="auto">
<path d="M0,0 L10,3 L0,6 Z" fill="#ff8c38" opacity="0.7"/>
</marker>
</defs>
<!-- ========== 背景层 ========== -->
<rect width="1200" height="700" fill="#050910"/>
<rect width="1200" height="700" fill="url(#grid)" opacity="0.6"/>
<!-- 天花板线 -->
<line x1="50" y1="85" x2="1150" y2="85" stroke="#101828" stroke-width="1" stroke-dasharray="10 5"/>
<text x="600" y="76" text-anchor="middle" fill="#1a2a44" font-size="11" font-family="'Noto Sans SC'">天花板</text>
<!-- ========== 安全区指示 (IFR: 零砸脸) ========== -->
<g id="safeZone" opacity="0.35">
<rect x="790" y="120" width="120" height="70" rx="8" fill="none" stroke="#00ddb5" stroke-width="1" stroke-dasharray="6 4"/>
<line x1="800" y1="130" x2="900" y2="180" stroke="#00ddb5" stroke-width="1.2" opacity="0.6"/>
<line x1="900" y1="130" x2="800" y2="180" stroke="#00ddb5" stroke-width="1.2" opacity="0.6"/>
<text x="850" y="205" text-anchor="middle" fill="#00ddb5" font-size="12" font-weight="700" font-family="'Chakra Petch'">0g</text>
<text x="850" y="220" text-anchor="middle" fill="#2a5a6a" font-size="9" font-family="'Noto Sans SC'">零砸脸风险</text>
</g>
<!-- ========== 床 ========== -->
<g id="bed">
<rect x="70" y="535" width="1060" height="40" rx="6" fill="#141c32" stroke="#1e2e50" stroke-width="1.2"/>
<rect x="70" y="570" width="1060" height="14" rx="4" fill="#0e1524" stroke="#162040" stroke-width="0.8"/>
<!-- 床头 -->
<rect x="55" y="490" width="30" height="95" rx="5" fill="#111828" stroke="#1a2844" stroke-width="1"/>
<!-- 枕头 -->
<rect x="770" y="512" width="150" height="26" rx="13" fill="#182040" stroke="#223058" stroke-width="1"/>
<!-- 被子暗示 -->
<path d="M 70 540 Q 200 530 400 540 Q 600 550 740 540 L 740 570 L 70 570 Z" fill="#0e1524" opacity="0.4"/>
</g>
<!-- ========== 人物 (仰卧侧视) ========== -->
<g id="person">
<!-- 身体 -->
<path d="M 340 532 L 340 505 C 340 494 352 488 370 488 L 770 488 C 788 488 798 494 800 505 L 800 532 Z"
fill="#101a2e" stroke="#1a2c4a" stroke-width="1.2"/>
<!-- 颈部 -->
<path d="M 800 498 C 805 488 812 482 820 480 L 835 480 C 828 488 820 498 815 510 L 805 510 Z"
fill="#101a2e" stroke="#1a2c4a" stroke-width="0.8"/>
<!-- 头部 (后脑勺在枕头上,脸朝上) -->
<ellipse cx="848" cy="498" rx="34" ry="28" fill="#111c30" stroke="#1a2c4a" stroke-width="1.2"/>
<!-- 头发 -->
<path d="M 815 498 C 815 478 830 468 848 466 C 866 468 878 478 880 492"
fill="#0a1220" stroke="none"/>
<!-- 耳朵 -->
<ellipse cx="815" cy="500" rx="5" ry="10" fill="#111c30" stroke="#1a2c4a" stroke-width="0.8"/>
<!-- 面部特征 (朝上) -->
<!-- 鼻子 (朝天花板) -->
<path d="M 878 494 L 886 485 L 880 491" fill="none" stroke="#2a4060" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<!-- 嘴巴 (放松微张) -->
<path d="M 870 504 Q 876 506 882 504" fill="none" stroke="#223050" stroke-width="1.2" stroke-linecap="round"/>
<!-- 眼睛位置占位 (由眼罩覆盖) -->
<ellipse cx="860" cy="482" rx="5" ry="3.5" fill="#0a1220" stroke="none"/>
<!-- 手臂 -->
<path d="M 400 498 L 398 530" stroke="#1a2c4a" stroke-width="9" stroke-linecap="round" fill="none"/>
<path d="M 510 494 L 508 530" stroke="#1a2c4a" stroke-width="9" stroke-linecap="round" fill="none"/>
</g>
<!-- ========== 手机托盘与手机 ========== -->
<g id="phoneTray">
<!-- 托盘支架 -->
<rect x="175" y="510" width="50" height="8" rx="2" fill="#1a2844" stroke="#243860" stroke-width="0.8"/>
<rect x="192" y="518" width="16" height="18" rx="2" fill="#1a2844" stroke="#243860" stroke-width="0.8"/>
<!-- 手机 -->
<rect id="phone" x="180" y="492" width="40" height="20" rx="3" fill="#1a2040" stroke="#2a3860" stroke-width="1"/>
<!-- 手机屏幕 (发光) -->
<rect id="phoneScreen" x="183" y="495" width="34" height="14" rx="2" fill="url(#phoneGrad)" opacity="0.85" filter="url(#glowSoft)"/>
<!-- 屏幕内容暗示 -->
<rect x="186" y="498" width="12" height="3" rx="1" fill="#ffe0b0" opacity="0.4"/>
<rect x="186" y="503" width="18" height="2" rx="1" fill="#ffe0b0" opacity="0.25"/>
<rect x="186" y="507" width="8" height="2" rx="1" fill="#ffe0b0" opacity="0.2"/>
</g>
<!-- ========== 重力资源指示 ========== -->
<g id="gravityIndicator" opacity="0.5">
<line x1="145" y1="470" x2="145" y2="510" stroke="#ff8c38" stroke-width="1.5" marker-end="url(#arrowAmber)"/>
<text x="145" y="462" text-anchor="middle" fill="#ff8c38" font-size="9" font-family="'Noto Sans SC'">重力</text>
<text x="145" y="525" text-anchor="middle" fill="#3a5070" font-size="8" font-family="'Noto Sans SC'">免费资源</text>
</g>
<!-- ========== 镜面1: 45°高反率平面镜 ========== -->
<g id="mirror1Group">
<!-- 镜面支架 -->
<line x1="200" y1="348" x2="200" y2="492" stroke="#1a2844" stroke-width="2" stroke-dasharray="4 3"/>
<rect x="194" y="488" width="12" height="6" rx="1" fill="#1a2844"/>
<!-- 镜面 (45° \ 形, 反射向上→向右) -->
<line id="mirror1" x1="178" y1="348" x2="222" y2="304" stroke="url(#mirrorShine)" stroke-width="4" stroke-linecap="round" filter="url(#glowSoft)"/>
<!-- 镜面背板 -->
<line x1="176" y1="346" x2="220" y2="302" stroke="#2a4060" stroke-width="6" stroke-linecap="round" opacity="0.3"/>
<line id="mirror1Top" x1="178" y1="348" x2="222" y2="304" stroke="url(#mirrorShine)" stroke-width="3.5" stroke-linecap="round"/>
<!-- 镜面反射高光 -->
<line x1="186" y1="338" x2="210" y2="314" stroke="#cceeff" stroke-width="1" stroke-linecap="round" opacity="0.4"/>
</g>
<!-- ========== 镜面2: 45°分光镜 (眼罩组件) ========== -->
<g id="mirror2Group">
<!-- 眼罩框架 (潜望镜式延伸) -->
<!-- 左侧支撑杆 -->
<line x1="830" y1="326" x2="830" y2="468" stroke="#1e3050" stroke-width="2.5" stroke-linecap="round"/>
<!-- 右侧支撑杆 -->
<line x1="880" y1="326" x2="880" y2="468" stroke="#1e3050" stroke-width="2.5" stroke-linecap="round"/>
<!-- 顶部横梁 -->
<rect x="825" y="318" width="60" height="12" rx="4" fill="#182844" stroke="#223860" stroke-width="0.8"/>
<!-- 分光镜 (45° / 形, 反射向右→向下) -->
<line x1="828" y1="352" x2="878" y2="302" stroke="#2a4060" stroke-width="6" stroke-linecap="round" opacity="0.25"/>
<line id="mirror2" x1="830" y1="350" x2="876" y2="304" stroke="url(#beamSplitShine)" stroke-width="3.5" stroke-linecap="round"/>
<!-- 半透明效果线 -->
<line x1="840" y1="340" x2="866" y2="314" stroke="#aaeeff" stroke-width="0.8" stroke-linecap="round" opacity="0.3" stroke-dasharray="3 3"/>
<!-- 眼罩面罩部分 (潜水镜式) -->
<path d="M 822 468 C 822 458 830 452 848 450 C 866 452 878 458 878 468 L 878 484 C 878 490 870 494 860 494 L 840 494 C 830 494 822 490 822 484 Z"
fill="#101a2e" stroke="#1e3050" stroke-width="1.5" opacity="0.9"/>
<!-- 软质遮光裙边 -->
<path d="M 820 466 C 816 466 812 470 812 476 L 812 486 C 812 492 816 498 822 498 L 826 498"
fill="none" stroke="#162840" stroke-width="3" stroke-linecap="round" opacity="0.6"/>
<path d="M 880 466 C 884 466 888 470 888 476 L 888 486 C 888 492 884 498 878 498 L 874 498"
fill="none" stroke="#162840" stroke-width="3" stroke-linecap="round" opacity="0.6"/>
<!-- 镜片区域 (分光镜近眼端) -->
<ellipse cx="850" cy="475" rx="18" ry="10" fill="rgba(0,221,181,0.04)" stroke="rgba(0,221,181,0.15)" stroke-width="0.8"/>
</g>
<!-- ========== 光线路径 ========== -->
<!-- 光线1: 手机 → 镜面1 (向上) -->
<line id="ray1" x1="200" y1="492" x2="200" y2="326"
stroke="#ff8c38" stroke-width="2.5" opacity="0.8"
stroke-dasharray="16 16" style="animation:rayFlow 0.8s linear infinite" filter="url(#glowSoft)"/>
<!-- 光线1 辉光底层 -->
<line x1="200" y1="492" x2="200" y2="326"
stroke="#ff8c38" stroke-width="6" opacity="0.15" stroke-linecap="round"/>
<!-- 光线2: 镜面1 → 镜面2 (水平向右) -->
<line id="ray2" x1="222" y1="326" x2="828" y2="326"
stroke="#ff8c38" stroke-width="2.5" opacity="0.75"
stroke-dasharray="16 16" style="animation:rayFlow 1.2s linear infinite" filter="url(#glowSoft)"/>
<line x1="222" y1="326" x2="828" y2="326"
stroke="#ff8c38" stroke-width="6" opacity="0.12" stroke-linecap="round"/>
<!-- 光线3a: 镜面2 → 眼睛 (向下, 反射光, 经分光镜衰减) -->
<line id="ray3a" x1="855" y1="348" x2="855" y2="470"
stroke="#00ddb5" stroke-width="2.2" opacity="0.55"
stroke-dasharray="12 12" style="animation:rayFlow 0.7s linear infinite" filter="url(#glowSoft)"/>
<line x1="855" y1="348" x2="855" y2="470"
stroke="#00ddb5" stroke-width="5" opacity="0.1" stroke-linecap="round"/>
<!-- 光线3b: 透射光 (继续向右, 衰减消失) -->
<line id="ray3b" x1="878" y1="326" x2="1100" y2="326"
stroke="url(#rayTransmit)" stroke-width="2" opacity="0.35"
stroke-dasharray="10 10" style="animation:rayFlow 1s linear infinite"/>
<line x1="878" y1="326" x2="1100" y2="326"
stroke="#ff8c38" stroke-width="4" opacity="0.05" stroke-linecap="round"/>
<!-- ========== 反射点高亮 ========== -->
<circle id="pulse1" cx="200" cy="326" r="6" fill="#ff8c38" opacity="0.5" filter="url(#glow)">
<animate attributeName="r" values="4;8;4" dur="2s" repeatCount="indefinite"/>
<animate attributeName="opacity" values="0.4;0.8;0.4" dur="2s" repeatCount="indefinite"/>
</circle>
<circle id="pulse2" cx="852" cy="326" r="6" fill="#00ddb5" opacity="0.5" filter="url(#glow)">
<animate attributeName="r" values="4;8;4" dur="2s" repeatCount="indefinite" begin="0.5s"/>
<animate attributeName="opacity" values="0.4;0.8;0.4" dur="2s" repeatCount="indefinite" begin="0.5s"/>
</circle>
<!-- ========== 眼睛舒适指示 ========== -->
<circle id="eyeComfortGlow" cx="850" cy="475" r="22" fill="url(#eyeComfort)" opacity="0.6">
<animate attributeName="r" values="18;26;18" dur="3s" repeatCount="indefinite"/>
<animate attributeName="opacity" values="0.3;0.7;0.3" dur="3s" repeatCount="indefinite"/>
</circle>
<!-- ========== 粒子容器 ========== -->
<g id="particles"></g>
<!-- ========== 标注层 ========== -->
<g id="annotations" font-family="'Noto Sans SC'" font-size="11">
<!-- 手机标注 -->
<line x1="225" y1="502" x2="270" y2="502" stroke="#3e5274" stroke-width="0.8" stroke-dasharray="3 2"/>
<text x="275" y="506" fill="#7a90b0" font-size="10.5">手机 (屏幕朝上)</text>
<!-- 镜面1标注 -->
<line x1="225" y1="326" x2="270" y2="290" stroke="#3e5274" stroke-width="0.8" stroke-dasharray="3 2"/>
<text x="275" y="286" fill="#88bbdd" font-size="10.5" font-weight="500">45° 高反率平面镜</text>
<text x="275" y="300" fill="#4a6a8a" font-size="9">反射率 ≈ 95%</text>
<!-- 镜面2标注 -->
<line x1="880" y1="310" x2="930" y2="280" stroke="#3e5274" stroke-width="0.8" stroke-dasharray="3 2"/>
<text x="935" y="276" fill="#00ddb5" font-size="10.5" font-weight="500">45° 分光镜</text>
<text x="935" y="290" fill="#4a7a7a" font-size="9" id="beamRatioLabel">透射:反射 = 7:3</text>
<!-- 眼罩标注 -->
<line x1="888" y1="480" x2="930" y2="500" stroke="#3e5274" stroke-width="0.8" stroke-dasharray="3 2"/>
<text x="935" y="497" fill="#7a90b0" font-size="10.5">轻量化眼罩框架</text>
<text x="935" y="511" fill="#4a6a8a" font-size="9">含遮光裙边 · 随头移动</text>
<!-- 焦距补偿标注 -->
<g id="focalAnnotation">
<line x1="900" y1="348" x2="920" y2="348" stroke="#2a5a6a" stroke-width="0.6"/>
<line x1="900" y1="468" x2="920" y2="468" stroke="#2a5a6a" stroke-width="0.6"/>
<line x1="910" y1="348" x2="910" y2="468" stroke="#2a5a6a" stroke-width="0.8" marker-end="url(#arrowTeal)"/>
<text x="920" y="412" fill="#2a7a7a" font-size="9" id="focalLabel">焦距补偿 ≈ 8cm</text>
</g>
<!-- 视线折转标注 -->
<g opacity="0.55">
<path d="M 870 472 Q 880 460 890 466" fill="none" stroke="#00ddb5" stroke-width="1.2" marker-end="url(#arrowTeal)"/>
<text x="896" y="462" fill="#00ddb5" font-size="9">视线折转90°</text>
</g>
<!-- 光线衰减标注 -->
<g opacity="0.5">
<text x="950" y="330" fill="#ff8c38" font-size="9">透射光 (衰减)</text>
<path d="M 948 334 L 920 334" stroke="#ff8c38" stroke-width="0.6" stroke-dasharray="2 2"/>
</g>
<!-- IFR 核心要点 -->
<g transform="translate(60, 140)" opacity="0.7">
<rect width="180" height="105" rx="6" fill="rgba(0,221,181,0.04)" stroke="rgba(0,221,181,0.12)" stroke-width="0.8"/>
<text x="12" y="20" fill="#00ddb5" font-size="10" font-weight="700" font-family="'Chakra Petch'" letter-spacing="1">IFR 理想解</text>
<line x1="12" y1="27" x2="168" y2="27" stroke="rgba(0,221,181,0.15)" stroke-width="0.5"/>
<text x="12" y="44" fill="#6a9aaa" font-size="9.5">✦ 物理重量与面部完全解耦</text>
<text x="12" y="60" fill="#6a9aaa" font-size="9.5">✦ 光学路径天然衰减亮度</text>
<text x="12" y="76" fill="#6a9aaa" font-size="9.5">✦ 重力作为免费固定资源</text>
<text x="12" y="92" fill="#6a9aaa" font-size="9.5">✦ 眼罩随动 · 翻身无需调整</text>
</g>
<!-- 原理说明 -->
<g transform="translate(60, 270)" opacity="0.55">
<rect width="180" height="70" rx="6" fill="rgba(255,140,56,0.03)" stroke="rgba(255,140,56,0.1)" stroke-width="0.8"/>
<text x="12" y="20" fill="#ff8c38" font-size="10" font-weight="600" font-family="'Chakra Petch'" letter-spacing="1">光路原理</text>
<text x="12" y="38" fill="#7a8a70" font-size="9">手机 → 45°镜反射 → 水平传输</text>
<text x="12" y="52" fill="#7a8a70" font-size="9">→ 分光镜衰减 → 入眼 (亮度↓)</text>
</g>
</g>
<!-- ========== 动态亮度指示条 ========== -->
<g id="brightnessBar" transform="translate(1040, 360)">
<rect width="16" height="120" rx="4" fill="#0a1220" stroke="#1a2844" stroke-width="0.8"/>
<rect id="brightnessFill" x="2" y="80" width="12" height="38" rx="3" fill="#00ddb5" opacity="0.5"/>
<text x="8" y="-6" text-anchor="middle" fill="#4a6a8a" font-size="8" font-family="'Chakra Petch'">亮度</text>
<text x="8" y="136" text-anchor="middle" fill="#2a4a5a" font-size="7" id="brightnessPct">30%</text>
</g>
</svg>
</div>
<!-- ========== 控制面板 ========== -->
<div class="ctrl-grid">
<!-- 分光镜控制 -->
<div class="ctrl-card">
<h3><i class="fas fa-adjust" style="margin-right:6px;color:var(--accent)"></i>分光镜参数</h3>
<div class="ctrl-row">
<label>透射率</label>
<input type="range" id="transmitSlider" min="10" max="90" value="70" step="5">
<span class="val" id="transmitVal">70%</span>
</div>
<div class="ctrl-row">
<label>反射率</label>
<input type="range" id="reflectSlider" min="10" max="90" value="30" step="5">
<span class="val" id="reflectVal">30%</span>
</div>
<div class="indicator" id="beamIndicator">
<i class="fas fa-eye"></i>
<span id="beamInfo">分光比 7:3 · 亮度衰减至 30% · 护眼模式</span>
</div>
<div class="info-badge" id="comfortBadge"><i class="fas fa-shield-alt" style="margin-right:4px"></i>舒适观看亮度</div>
</div>
<!-- 焦距与动画控制 -->
<div class="ctrl-card">
<h3><i class="fas fa-ruler" style="margin-right:6px;color:var(--accent2)"></i>焦距与播放</h3>
<div class="ctrl-row">
<label>焦距补偿</label>
<input type="range" id="focalSlider" min="5" max="10" value="8" step="0.5">
<span class="val" id="focalVal">8cm</span>
</div>
<div class="ctrl-row">
<label>光子速度</label>
<input type="range" id="speedSlider" min="0.3" max="3" value="1" step="0.1">
<span class="val" id="speedVal">1.0x</span>
</div>
<div class="btn-row">
<button class="btn active" id="btnPlay"><i class="fas fa-pause"></i> 暂停</button>
<button class="btn" id="btnReset"><i class="fas fa-redo"></i> 重置</button>
</div>
</div>
</div>
</main>
<script>
// ========== 核心变量 ==========
const svg = document.getElementById('mainSvg');
const particleGroup = document.getElementById('particles');
const ray1 = document.getElementById('ray1');
const ray2 = document.getElementById('ray2');
const ray3a = document.getElementById('ray3a');
const ray3b = document.getElementById('ray3b');
const brightnessFill = document.getElementById('brightnessFill');
const brightnessPct = document.getElementById('brightnessPct');
const beamRatioLabel = document.getElementById('beamRatioLabel');
const focalLabel = document.getElementById('focalLabel');
const beamInfo = document.getElementById('beamInfo');
const comfortBadge = document.getElementById('comfortBadge');
const eyeComfortGlow = document.getElementById('eyeComfortGlow');
// 控制滑块
const transmitSlider = document.getElementById('transmitSlider');
const reflectSlider = document.getElementById('reflectSlider');
const focalSlider = document.getElementById('focalSlider');
const speedSlider = document.getElementById('speedSlider');
let isPlaying = true;
let animSpeed = 1;
let reflectRatio = 0.3;
let focalDist = 8;
// ========== 光路定义 ==========
// 路径分段: 手机→镜面1(向上), 镜面1→镜面2(水平), 镜面2→眼睛(向下), 透射(向右消失)
const PATH = {
seg1: { from: [200, 492], to: [200, 326] }, // 手机→镜面1
seg2: { from: [222, 326], to: [828, 326] }, // 镜面1→镜面2
seg3r: { from: [855, 348], to: [855, 470] }, // 镜面2→眼睛(反射)
seg3t: { from: [878, 326], to: [1080, 326] }, // 镜面2透射
};
function segLen(seg) {
const dx = seg.to[0] - seg.from[0];
const dy = seg.to[1] - seg.from[1];
return Math.sqrt(dx * dx + dy * dy);
}
const len1 = segLen(PATH.seg1);
const len2 = segLen(PATH.seg2);
const len3r = segLen(PATH.seg3r);
const len3t = segLen(PATH.seg3t);
// ========== 粒子系统 ==========
const MAX_PARTICLES = 40;
let particles = [];
function createParticle() {
// 随机决定此粒子在分光镜处的命运
const isReflected = Math.random() < reflectRatio;
return {
progress: 0, // 0~1 在整条路径上的进度
isReflected: isReflected,
speed: (60 + Math.random() * 40) * animSpeed, // 像素/秒
size: 1.5 + Math.random() * 1.5,
born: performance.now(),
};
}
function getParticlePos(p) {
const totalBeforeSplit = len1 + len2;
const totalReflected = totalBeforeSplit + len3r;
const totalTransmitted = totalBeforeSplit + len3t;
const dist = p.progress;
if (dist <= len1) {
// 在第一段 (向上)
const t = dist / len1;
return [
PATH.seg1.from[0] + (PATH.seg1.to[0] - PATH.seg1.from[0]) * t,
PATH.seg1.from[1] + (PATH.seg1.to[1] - PATH.seg1.from[1]) * t,
'warm'
];
} else if (dist <= totalBeforeSplit) {
// 在第二段 (水平)
const segDist = dist - len1;
const t = segDist / len2;
return [
PATH.seg2.from[0] + (PATH.seg2.to[0] - PATH.seg2.from[0]) * t,
PATH.seg2.from[1] + (PATH.seg2.to[1] - PATH.seg2.from[1]) * t,
'warm'
];
} else {
// 在第三段
if (p.isReflected) {
const segDist = dist - totalBeforeSplit;
const t = Math.min(1, segDist / len3r);
return [
PATH.seg3r.from[0] + (PATH.seg3r.to[0] - PATH.seg3r.from[0]) * t,
PATH.seg3r.from[1] + (PATH.seg3r.to[1] - PATH.seg3r.from[1]) * t,
'cool'
];
} else {
const segDist = dist - totalBeforeSplit;
const t = Math.min(1, segDist / len3t);
return [
PATH.seg3t.from[0] + (PATH.seg3t.to[0] - PATH.seg3t.from[0]) * t,
PATH.seg3t.from[1] + (PATH.seg3t.to[1] - PATH.seg3t.from[1]) * t,
'transmit'
];
}
}
}
// 预创建 SVG 圆形元素
const particleEls = [];
for (let i = 0; i < MAX_PARTICLES; i++) {
const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
circle.setAttribute('r', '2');
circle.setAttribute('fill', '#ff8c38');
circle.setAttribute('opacity', '0');
particleGroup.appendChild(circle);
particleEls.push(circle);
}
// ========== 生成粒子 ==========
let spawnTimer = 0;
const SPAWN_INTERVAL = 80; // 毫秒
function spawnParticle() {
if (particles.length < MAX_PARTICLES) {
particles.push(createParticle());
}
}
// ========== 动画主循环 ==========
let lastTime = 0;
let animFrameId = null;
function animate(timestamp) {
if (!lastTime) lastTime = timestamp;
const dt = (timestamp - lastTime) / 1000;
lastTime = timestamp;
if (isPlaying) {
// 生成新粒子
spawnTimer += dt * 1000 * animSpeed;
while (spawnTimer >= SPAWN_INTERVAL) {
spawnParticle();
spawnTimer -= SPAWN_INTERVAL;
}
// 更新粒子
const totalBeforeSplit = len1 + len2;
const maxDist = totalBeforeSplit + Math.max(len3r, len3t);
for (let i = particles.length - 1; i >= 0; i--) {
const p = particles[i];
p.progress += p.speed * dt;
if (p.progress > maxDist) {
particles.splice(i, 1);
}
}
}
// 渲染粒子
for (let i = 0; i < MAX_PARTICLES; i++) {
if (i < particles.length) {
const p = particles[i];
const [x, y, type] = getParticlePos(p);
const el = particleEls[i];
let color, opacity;
if (type === 'warm') {
color = '#ff8c38';
opacity = 0.85;
} else if (type === 'cool') {
color = '#00ddb5';
// 反射光亮度由反射率决定
opacity = 0.3 + reflectRatio * 1.2;
} else {
color = '#ff8c38';
// 透射光逐渐变暗
const totalBeforeSplit2 = len1 + len2;
const segDist = p.progress - totalBeforeSplit2;
const fadeT = segDist / len3t;
opacity = (1 - fadeT) * 0.4;
}
// 在分光镜之前,粒子还是暖色
if (p.progress <= totalBeforeSplit) {
color = '#ff8c38';
opacity = 0.8;
}
el.setAttribute('cx', x);
el.setAttribute('cy', y);
el.setAttribute('r', p.size * (type === 'cool' ? 0.85 : 1));
el.setAttribute('fill', color);
el.setAttribute('opacity', Math.max(0, Math.min(1, opacity)));
} else {
particleEls[i].setAttribute('opacity', '0');
}
}
animFrameId = requestAnimationFrame(animate);
}
// ========== 交互控制 ==========
function updateBeamSplitter() {
const transmit = parseInt(transmitSlider.value);
const reflect = parseInt(reflectSlider.value);
reflectRatio = reflect / 100;
document.getElementById('transmitVal').textContent = transmit + '%';
document.getElementById('reflectVal').textContent = reflect + '%';
// 更新光线3a(反射)的透明度
const ray3aOpacity = 0.2 + reflectRatio * 1.0;
ray3a.setAttribute('opacity', Math.min(0.9, ray3aOpacity));
// 更新光线3b(透射)的透明度
const transmitRatio = transmit / 100;
ray3b.setAttribute('opacity', 0.1 + transmitRatio * 0.4);
// 更新亮度指示条
const barH = 116; // 总高度
const fillH = Math.max(4, reflectRatio * barH);
const fillY = barH - fillH + 2;
brightnessFill.setAttribute('y', fillY);
brightnessFill.setAttribute('height', fillH);
brightnessFill.setAttribute('opacity', 0.3 + reflectRatio * 0.5);
brightnessPct.textContent = reflect + '%';
// 更新标签
const t = transmit / 10;
const r = reflect / 10;
beamRatioLabel.textContent = `透射:反射 = ${t.toFixed ? transmit/10 : transmit/10}:${reflect/10}`;
// 更新信息
let info, badge;
if (reflect <= 20) {
info = `分光比 ${transmit/10}:${reflect/10} · 亮度极低 · 深度护眼`;
badge = '深度护眼模式 · 适合暗环境';
comfortBadge.style.background = 'rgba(0,221,181,0.1)';
comfortBadge.style.borderColor = 'rgba(0,221,181,0.25)';
comfortBadge.style.color = '#00ddb5';
} else if (reflect <= 40) {
info = `分光比 ${transmit/10}:${reflect/10} · 亮度衰减至 ${reflect}% · 护眼模式`;
badge = '舒适观看亮度';
comfortBadge.style.background = 'rgba(0,221,181,0.08)';
comfortBadge.style.borderColor = 'rgba(0,221,181,0.18)';
comfortBadge.style.color = '#00ddb5';
} else if (reflect <= 60) {
info = `分光比 ${transmit/10}:${reflect/10} · 亮度 ${reflect}% · 标准模式`;
badge = '标准亮度 · 注意用眼时长';
comfortBadge.style.background = 'rgba(255,200,80,0.08)';
comfortBadge.style.borderColor = 'rgba(255,200,80,0.18)';
comfortBadge.style.color = '#ddaa44';
} else {
info = `分光比 ${transmit/10}:${reflect/10} · 亮度 ${reflect}% · 高亮模式`;
badge = '亮度偏高 · 不建议长时间使用';
comfortBadge.style.background = 'rgba(255,100,80,0.08)';
comfortBadge.style.borderColor = 'rgba(255,100,80,0.18)';
comfortBadge.style.color = '#dd6644';
}
beamInfo.textContent = info;
comfortBadge.innerHTML = '<i class="fas fa-shield-alt" style="margin-right:4px"></i>' + badge;
// 更新眼罩舒适光晕
eyeComfortGlow.setAttribute('opacity', 0.2 + reflectRatio * 0.5);
}
// 联动滑块
transmitSlider.addEventListener('input', () => {
const t = parseInt(transmitSlider.value);
reflectSlider.value = 100 - t;
updateBeamSplitter();
});
reflectSlider.addEventListener('input', () => {
const r = parseInt(reflectSlider.value);
transmitSlider.value = 100 - r;
updateBeamSplitter();
});
focalSlider.addEventListener('input', () => {
focalDist = parseFloat(focalSlider.value);
document.getElementById('focalVal').textContent = focalDist + 'cm';
focalLabel.textContent = `焦距补偿 ≈ ${focalDist}cm`;
// 视觉上调整眼罩框架长度(模拟焦距变化)
const mirror2Group = document.getElementById('mirror2Group');
const scale = 0.85 + (focalDist - 5) * 0.05;
// 调整反射光线长度来暗示焦距
const newEndY = 470 - (focalDist - 8) * 3;
ray3a.setAttribute('y2', Math.max(430, Math.min(490, newEndY)));
});
speedSlider.addEventListener('input', () => {
animSpeed = parseFloat(speedSlider.value);
document.getElementById('speedVal').textContent = animSpeed.toFixed(1) + 'x';
// 调整CSS动画速度
const rayEls = [ray1, ray2, ray3a, ray3b];
rayEls.forEach(r => {
const dur = parseFloat(r.style.animationDuration || 0.8);
r.style.animationDuration = (0.8 / animSpeed) + 's';
});
});
// 播放/暂停
const btnPlay = document.getElementById('btnPlay');
btnPlay.addEventListener('click', () => {
isPlaying = !isPlaying;
btnPlay.innerHTML = isPlaying
? '<i class="fas fa-pause"></i> 暂停'
: '<i class="fas fa-play"></i> 播放';
btnPlay.classList.toggle('active', isPlaying);
// 暂停CSS动画
const svgLines = svg.querySelectorAll('line[style*="animation"]');
svgLines.forEach(l => {
l.style.animationPlayState = isPlaying ? 'running' : 'paused';
});
// 暂停SVG animate元素
const animates = svg.querySelectorAll('animate');
animates.forEach(a => {
if (isPlaying) a.beginElement && a.parentElement.appendChild(a);
});
});
// 重置
document.getElementById('btnReset').addEventListener('click', () => {
transmitSlider.value = 70;
reflectSlider.value = 30;
focalSlider.value = 8;
speedSlider.value = 1;
animSpeed = 1;
reflectRatio = 0.3;
focalDist = 8;
particles = [];
updateBeamSplitter();
document.getElementById('focalVal').textContent = '8cm';
document.getElementById('speedVal').textContent = '1.0x';
focalLabel.textContent = '焦距补偿 ≈ 8cm';
ray3a.setAttribute('y2', 470);
// 重置CSS动画速度
[ray1, ray2, ray3a, ray3b].forEach(r => {
r.style.animationDuration = '';
});
});
// ========== 初始化 ==========
updateBeamSplitter();
animFrameId = requestAnimationFrame(animate);
// 响应 prefers-reduced-motion
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
isPlaying = false;
btnPlay.innerHTML = '<i class="fas fa-play"></i> 播放';
btnPlay.classList.remove('active');
}
</script>
</body>
</html>
实现说明
设计美学方向:技术蓝图 / 光学工程制图风
- 深色背景配合微弱网格,营造精密光学仪器的技术氛围
- 光路采用暖色(琥珀 #ff8c38)→ 冷色(青绿 #00ddb5)的色彩转换,直观表达分光镜的亮度衰减效应
- 所有标注采用细线引出 + 小字体,模拟工程制图的标注规范
IFR 核心思想可视化:
- 零砸脸风险:脸部上方用虚线框 + X + "0g" 标记,强调无物理重量悬于面部
- 重力资源化:手机旁标注"重力 → 免费资源",展示无需额外固定结构
- 光学亮度衰减:分光镜处光子粒子分为两路——反射(青绿色,到达眼睛)与透射(琥珀色,渐隐消失),比例随滑块实时变化
- 眼罩随动:标注"随头移动 · 翻身无需调整"
交互功能:
- 分光镜透射/反射率联动滑块:调整比例会实时改变到达眼睛的光线亮度、粒子颜色分布、右侧亮度指示条、舒适度评级
- 焦距补偿滑块:调整反射光路长度,模拟5-10cm的焦距补偿距离
- 光子速度控制:调节粒子流动速率
- 播放/暂停/重置:完整控制动画状态
积分规则:第一轮对话扣减6分,后续每轮扣4分
等待动画代码生成...
