独立渲染引擎就绪引擎就绪
<!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=Orbitron:wght@400;700;900&family=IBM+Plex+Mono:wght@300;400;500&display=swap" rel="stylesheet">
<style>
:root {
--bg: #060b18;
--primary: #00ffc8;
--primary-dim: #00ffc840;
--accent: #ffaa00;
--alert: #ff4757;
--success: #00ff88;
--text: #a8b8d0;
--text-bright: #e8f0ff;
--surface: #0c1424;
--border: #162040;
}
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
body {
background: var(--bg);
color: var(--text);
font-family: 'IBM Plex Mono', monospace;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
overflow-x: hidden;
}
/* 扫描线效果 */
body::after {
content: '';
position: fixed;
inset: 0;
pointer-events: none;
background: repeating-linear-gradient(
0deg,
transparent,
transparent 2px,
rgba(0, 255, 200, 0.015) 2px,
rgba(0, 255, 200, 0.015) 4px
);
z-index: 9999;
}
.scene-wrapper {
width: 100%;
max-width: 1400px;
flex: 1;
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
position: relative;
}
#scene {
width: 100%;
height: auto;
max-height: 80vh;
border: 1px solid var(--border);
border-radius: 12px;
background: radial-gradient(ellipse at 30% 40%, #0a1628 0%, var(--bg) 70%);
}
/* 控制面板 */
.controls {
width: 100%;
max-width: 1400px;
padding: 16px 24px 24px;
display: flex;
gap: 32px;
justify-content: center;
flex-wrap: wrap;
background: linear-gradient(180deg, transparent, rgba(6,11,24,0.95) 30%);
}
.control-group {
display: flex;
flex-direction: column;
gap: 6px;
min-width: 260px;
}
.control-label {
font-family: 'Orbitron', sans-serif;
font-size: 11px;
font-weight: 700;
letter-spacing: 1px;
color: var(--primary);
text-transform: uppercase;
display: flex;
justify-content: space-between;
align-items: baseline;
}
.control-value {
font-family: 'IBM Plex Mono', monospace;
font-weight: 400;
color: var(--accent);
font-size: 12px;
}
input[type="range"] {
-webkit-appearance: none;
appearance: none;
width: 100%;
height: 6px;
background: var(--border);
border-radius: 3px;
outline: none;
cursor: pointer;
}
input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 18px;
height: 18px;
border-radius: 50%;
background: var(--primary);
border: 2px solid var(--bg);
box-shadow: 0 0 10px var(--primary-dim);
cursor: pointer;
}
input[type="range"]::-moz-range-thumb {
width: 18px;
height: 18px;
border-radius: 50%;
background: var(--primary);
border: 2px solid var(--bg);
box-shadow: 0 0 10px var(--primary-dim);
cursor: pointer;
}
/* 标题栏 */
.title-bar {
width: 100%;
max-width: 1400px;
padding: 20px 24px 8px;
display: flex;
align-items: baseline;
gap: 16px;
}
.title-bar h1 {
font-family: 'Orbitron', sans-serif;
font-size: 18px;
font-weight: 900;
color: var(--primary);
letter-spacing: 2px;
}
.title-bar .subtitle {
font-size: 12px;
color: var(--text);
opacity: 0.6;
}
/* 相位指示器 */
.phase-strip {
width: 100%;
max-width: 1400px;
padding: 0 24px 12px;
display: flex;
gap: 4px;
overflow-x: auto;
}
.phase-chip {
font-family: 'Orbitron', sans-serif;
font-size: 9px;
font-weight: 700;
letter-spacing: 0.5px;
padding: 4px 10px;
border-radius: 4px;
background: var(--surface);
border: 1px solid var(--border);
color: var(--text);
white-space: nowrap;
transition: all 0.4s ease;
}
.phase-chip.active {
background: rgba(0, 255, 200, 0.12);
border-color: var(--primary);
color: var(--primary);
box-shadow: 0 0 12px rgba(0, 255, 200, 0.15);
}
.phase-chip.done {
background: rgba(0, 255, 136, 0.08);
border-color: rgba(0, 255, 136, 0.3);
color: var(--success);
}
@media (prefers-reduced-motion: reduce) {
* { animation-duration: 0s !important; transition-duration: 0s !important; }
}
</style>
</head>
<body>
<div class="title-bar">
<h1>VISUAL-INTENT ENGINE</h1>
<span class="subtitle">视觉-意图预测引擎 · IFR 原理动画</span>
</div>
<div class="phase-strip" id="phaseStrip"></div>
<div class="scene-wrapper">
<svg id="scene" viewBox="0 0 1400 750" xmlns="http://www.w3.org/2000/svg">
<defs>
<!-- 发光滤镜 -->
<filter id="glowPrimary" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur stdDeviation="6" result="blur"/>
<feMerge><feMergeNode in="blur"/><feMergeNode in="SourceGraphic"/></feMerge>
</filter>
<filter id="glowAccent" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur stdDeviation="8" result="blur"/>
<feMerge><feMergeNode in="blur"/><feMergeNode in="SourceGraphic"/></feMerge>
</filter>
<filter id="glowStrong" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur stdDeviation="15" result="blur"/>
<feMerge><feMergeNode in="blur"/><feMergeNode in="SourceGraphic"/></feMerge>
</filter>
<filter id="softBlur" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur stdDeviation="3"/>
</filter>
<!-- 虹膜渐变 -->
<radialGradient id="irisGrad" cx="50%" cy="50%" r="50%">
<stop offset="0%" stop-color="#0a3d2d"/>
<stop offset="40%" stop-color="#0d6b4f"/>
<stop offset="70%" stop-color="#00ffc8"/>
<stop offset="100%" stop-color="#008866"/>
</radialGradient>
<!-- 置信度渐变 -->
<linearGradient id="confGrad" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" stop-color="#ff4757"/>
<stop offset="50%" stop-color="#ffaa00"/>
<stop offset="100%" stop-color="#00ff88"/>
</linearGradient>
<!-- 灯光渐变 -->
<radialGradient id="lampGlow" cx="50%" cy="0%" r="80%">
<stop offset="0%" stop-color="#ffdd44" stop-opacity="0.8"/>
<stop offset="40%" stop-color="#ffaa00" stop-opacity="0.3"/>
<stop offset="100%" stop-color="#ffaa00" stop-opacity="0"/>
</radialGradient>
<!-- 注视光束渐变 -->
<linearGradient id="beamGrad" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" stop-color="#00ffc8" stop-opacity="0.8"/>
<stop offset="50%" stop-color="#00ffc8" stop-opacity="0.4"/>
<stop offset="100%" stop-color="#00ffc8" stop-opacity="0.1"/>
</linearGradient>
<!-- 网格图案 -->
<pattern id="grid" width="40" height="40" patternUnits="userSpaceOnUse">
<path d="M 40 0 L 0 0 0 40" fill="none" stroke="#0f1d35" stroke-width="0.5"/>
</pattern>
<!-- 扫描线图案 -->
<pattern id="scanlines" width="4" height="4" patternUnits="userSpaceOnUse">
<line x1="0" y1="0" x2="4" y2="0" stroke="rgba(0,255,200,0.02)" stroke-width="1"/>
</pattern>
</defs>
<!-- 背景网格 -->
<rect width="1400" height="750" fill="url(#grid)" opacity="0.7"/>
<rect width="1400" height="750" fill="url(#scanlines)"/>
<!-- ======================== 左侧:眼睛 ======================== -->
<g id="eyeGroup" transform="translate(280, 320)">
<!-- 眼眶外框装饰 -->
<ellipse cx="0" cy="0" rx="110" ry="80" fill="none" stroke="#0f2040" stroke-width="1" stroke-dasharray="4 6"/>
<ellipse cx="0" cy="0" rx="125" ry="92" fill="none" stroke="#0a1830" stroke-width="0.5" stroke-dasharray="2 8"/>
<!-- 眼白 -->
<path d="M-90,0 Q-50,-55 0,-60 Q50,-55 90,0 Q50,55 0,60 Q-50,55 -90,0 Z" fill="#dde4f0" stroke="#8899bb" stroke-width="1.5"/>
<!-- 虹膜组(可移动) -->
<g id="irisGroup">
<!-- 虹膜 -->
<circle cx="0" cy="0" r="36" fill="url(#irisGrad)" stroke="#006655" stroke-width="1"/>
<!-- 虹膜纹理 -->
<circle cx="0" cy="0" r="30" fill="none" stroke="#00aa88" stroke-width="0.5" opacity="0.4"/>
<circle cx="0" cy="0" r="24" fill="none" stroke="#00cc99" stroke-width="0.3" opacity="0.3"/>
<!-- 瞳孔 -->
<circle id="pupil" cx="0" cy="0" r="14" fill="#050510"/>
<!-- 高光 -->
<ellipse cx="-10" cy="-10" rx="7" ry="4" fill="rgba(255,255,255,0.75)" transform="rotate(-20 -10 -10)"/>
<ellipse cx="8" cy="6" rx="3" ry="2" fill="rgba(255,255,255,0.3)"/>
</g>
<!-- 上眼睑 -->
<path id="upperLid" d="M-90,0 Q-50,-55 0,-60 Q50,-55 90,0" fill="var(--bg)" stroke="none"/>
<!-- 下眼睑 -->
<path id="lowerLid" d="M-90,0 Q-50,55 0,60 Q50,55 90,0" fill="var(--bg)" stroke="none"/>
<!-- 眼睛轮廓重绘(在眼睑上方) -->
<path d="M-90,0 Q-50,-55 0,-60 Q50,-55 90,0 Q50,55 0,60 Q-50,55 -90,0 Z" fill="none" stroke="#6680aa" stroke-width="2"/>
<!-- 标注文字 -->
<text x="0" y="-95" text-anchor="middle" font-family="Orbitron, sans-serif" font-size="11" font-weight="700" fill="#00ffc8" letter-spacing="1.5" opacity="0.8">EYE TRACKING</text>
<text x="0" y="-80" text-anchor="middle" font-family="'IBM Plex Mono', monospace" font-size="9" fill="#5a7a9a" opacity="0.6">红外眼动追踪模组</text>
</g>
<!-- ======================== 右侧:智能台灯 ======================== -->
<g id="lampGroup" transform="translate(1060, 280)">
<!-- 灯座 -->
<rect x="-40" y="160" width="80" height="12" rx="6" fill="#1a2540" stroke="#2a3a5a" stroke-width="1.5"/>
<!-- 灯杆 -->
<rect x="-5" y="50" width="10" height="112" rx="3" fill="#1a2540" stroke="#2a3a5a" stroke-width="1"/>
<!-- 灯臂 -->
<line x1="0" y1="50" x2="40" y2="-10" stroke="#2a3a5a" stroke-width="6" stroke-linecap="round"/>
<!-- 灯罩 -->
<path d="M15,-10 L65,-10 L55,20 L25,20 Z" fill="#1e2d4a" stroke="#3a4a6a" stroke-width="1.5" stroke-linejoin="round"/>
<!-- 灯泡区域 -->
<circle id="bulb" cx="40" cy="18" r="8" fill="#2a3040" stroke="#3a4a5a" stroke-width="1"/>
<!-- 灯光效果(初始隐藏) -->
<g id="lampLight" opacity="0">
<ellipse cx="40" cy="80" rx="120" ry="100" fill="url(#lampGlow)"/>
<line x1="20" y1="22" x2="-20" y2="120" stroke="#ffdd44" stroke-width="0.5" opacity="0.3"/>
<line x1="40" y1="24" x2="40" y2="130" stroke="#ffdd44" stroke-width="0.5" opacity="0.3"/>
<line x1="60" y1="22" x2="100" y2="120" stroke="#ffdd44" stroke-width="0.5" opacity="0.3"/>
</g>
<!-- 标注文字 -->
<text x="0" y="-50" text-anchor="middle" font-family="Orbitron, sans-serif" font-size="11" font-weight="700" fill="#ffaa00" letter-spacing="1.5" opacity="0.8">SMART LAMP</text>
<text x="0" y="-35" text-anchor="middle" font-family="'IBM Plex Mono', monospace" font-size="9" fill="#5a7a9a" opacity="0.6">目标智能设备</text>
</g>
<!-- ======================== 注视光束 ======================== -->
<g id="beamGroup" opacity="0">
<!-- 主光束线 -->
<line id="beamLine" x1="370" y1="320" x2="1010" y2="280" stroke="url(#beamGrad)" stroke-width="2.5" filter="url(#glowPrimary)"/>
<!-- 光束虚线层 -->
<line id="beamDash" x1="370" y1="320" x2="1010" y2="280" stroke="#00ffc8" stroke-width="1" stroke-dasharray="8 12" opacity="0.5">
<animate attributeName="stroke-dashoffset" from="0" to="-40" dur="1s" repeatCount="indefinite"/>
</line>
<!-- 光束粒子容器 -->
<g id="beamParticles"></g>
</g>
<!-- ======================== 目标瞄准框 ======================== -->
<g id="reticle" transform="translate(1060, 280)" opacity="0">
<circle cx="40" cy="0" r="30" fill="none" stroke="#00ffc8" stroke-width="1.5" stroke-dasharray="6 4" opacity="0.6">
<animateTransform attributeName="transform" type="rotate" from="0 40 0" to="360 40 0" dur="4s" repeatCount="indefinite"/>
</circle>
<circle cx="40" cy="0" r="22" fill="none" stroke="#00ffc8" stroke-width="0.8" opacity="0.4">
<animateTransform attributeName="transform" type="rotate" from="360 40 0" to="0 40 0" dur="3s" repeatCount="indefinite"/>
</circle>
<!-- 十字线 -->
<line x1="40" y1="-35" x2="40" y2="-26" stroke="#00ffc8" stroke-width="1.5" opacity="0.7"/>
<line x1="40" y1="26" x2="40" y2="35" stroke="#00ffc8" stroke-width="1.5" opacity="0.7"/>
<line x1="5" y1="0" x2="14" y2="0" stroke="#00ffc8" stroke-width="1.5" opacity="0.7"/>
<line x1="66" y1="0" x2="75" y2="0" stroke="#00ffc8" stroke-width="1.5" opacity="0.7"/>
</g>
<!-- ======================== 停留计时环 ======================== -->
<g id="dwellRing" transform="translate(1100, 280)" opacity="0">
<circle cx="0" cy="0" r="42" fill="none" stroke="#1a2744" stroke-width="4"/>
<circle id="dwellArc" cx="0" cy="0" r="42" fill="none" stroke="#00ffc8" stroke-width="4" stroke-linecap="round"
stroke-dasharray="0 264" transform="rotate(-90)"/>
<text id="dwellText" x="0" y="4" text-anchor="middle" font-family="'IBM Plex Mono', monospace" font-size="12" font-weight="500" fill="#00ffc8">0ms</text>
</g>
<!-- ======================== 微表情面板 ======================== -->
<g id="expressionPanel" transform="translate(160, 530)" opacity="0">
<rect x="0" y="0" width="200" height="100" rx="8" fill="rgba(12,20,36,0.9)" stroke="#1a2744" stroke-width="1"/>
<text x="100" y="22" text-anchor="middle" font-family="Orbitron, sans-serif" font-size="9" font-weight="700" fill="#ff4757" letter-spacing="1">MICRO-EXPRESSION</text>
<line x1="10" y1="30" x2="190" y2="30" stroke="#1a2744" stroke-width="0.5"/>
<!-- 表情图标 -->
<g id="exprIcon" transform="translate(35, 62)">
<circle cx="0" cy="0" r="18" fill="none" stroke="#ff4757" stroke-width="1.5"/>
<!-- 眼睛 -->
<circle cx="-6" cy="-4" r="2" fill="#ff4757"/>
<circle cx="6" cy="-4" r="2" fill="#ff4757"/>
<!-- 嘴巴(可变) -->
<path id="exprMouth" d="M-7,6 Q0,4 7,6" fill="none" stroke="#ff4757" stroke-width="1.5" stroke-linecap="round"/>
</g>
<!-- 表情数据 -->
<text id="exprLabel" x="85" y="52" font-family="'IBM Plex Mono', monospace" font-size="11" fill="#a8b8d0">中性</text>
<text id="exprConf" x="85" y="70" font-family="'IBM Plex Mono', monospace" font-size="9" fill="#5a7a9a">置信度: --</text>
<text x="85" y="86" font-family="'IBM Plex Mono', monospace" font-size="9" fill="#5a7a9a">情绪倾向: --</text>
</g>
<!-- ======================== 处理流水线 ======================== -->
<g id="pipeline" transform="translate(250, 640)">
<!-- 连接线 -->
<line x1="90" y1="22" x2="280" y2="22" stroke="#1a2744" stroke-width="1.5" stroke-dasharray="4 4"/>
<line x1="370" y1="22" x2="560" y2="22" stroke="#1a2744" stroke-width="1.5" stroke-dasharray="4 4"/>
<line x1="650" y1="22" x2="840" y2="22" stroke="#1a2744" stroke-width="1.5" stroke-dasharray="4 4"/>
<!-- 节点1: 眼动追踪 -->
<g id="node1" class="pipeline-node">
<rect x="0" y="0" width="90" height="44" rx="6" fill="#0c1424" stroke="#1a2744" stroke-width="1.5"/>
<text x="45" y="17" text-anchor="middle" font-family="Orbitron, sans-serif" font-size="8" font-weight="700" fill="#5a7a9a">GAZE</text>
<text x="45" y="34" text-anchor="middle" font-family="'IBM Plex Mono', monospace" font-size="8" fill="#3a5a7a">眼动追踪</text>
</g>
<!-- 节点2: 微表情 -->
<g id="node2" class="pipeline-node" transform="translate(280, 0)">
<rect x="0" y="0" width="90" height="44" rx="6" fill="#0c1424" stroke="#1a2744" stroke-width="1.5"/>
<text x="45" y="17" text-anchor="middle" font-family="Orbitron, sans-serif" font-size="8" font-weight="700" fill="#5a7a9a">EXPR</text>
<text x="45" y="34" text-anchor="middle" font-family="'IBM Plex Mono', monospace" font-size="8" fill="#3a5a7a">微表情</text>
</g>
<!-- 节点3: 边缘计算 -->
<g id="node3" class="pipeline-node" transform="translate(560, 0)">
<rect x="0" y="0" width="90" height="44" rx="6" fill="#0c1424" stroke="#1a2744" stroke-width="1.5"/>
<text x="45" y="17" text-anchor="middle" font-family="Orbitron, sans-serif" font-size="8" font-weight="700" fill="#5a7a9a">EDGE</text>
<text x="45" y="34" text-anchor="middle" font-family="'IBM Plex Mono', monospace" font-size="8" fill="#3a5a7a">边缘计算</text>
</g>
<!-- 节点4: 意图判定 -->
<g id="node4" class="pipeline-node" transform="translate(840, 0)">
<rect x="0" y="0" width="90" height="44" rx="6" fill="#0c1424" stroke="#1a2744" stroke-width="1.5"/>
<text x="45" y="17" text-anchor="middle" font-family="Orbitron, sans-serif" font-size="8" font-weight="700" fill="#5a7a9a">INTENT</text>
<text x="45" y="34" text-anchor="middle" font-family="'IBM Plex Mono', monospace" font-size="8" fill="#3a5a7a">意图判定</text>
</g>
<!-- 数据流动画线 -->
<g id="flowParticles"></g>
</g>
<!-- ======================== 置信度仪表 ======================== -->
<g id="confGauge" transform="translate(550, 530)" opacity="0">
<rect x="0" y="0" width="300" height="70" rx="8" fill="rgba(12,20,36,0.9)" stroke="#1a2744" stroke-width="1"/>
<text x="150" y="18" text-anchor="middle" font-family="Orbitron, sans-serif" font-size="9" font-weight="700" fill="#ffaa00" letter-spacing="1">CONFIDENCE LEVEL</text>
<!-- 进度条背景 -->
<rect x="15" y="28" width="270" height="10" rx="5" fill="#0a1020"/>
<!-- 进度条填充 -->
<rect id="confBar" x="15" y="28" width="0" height="10" rx="5" fill="url(#confGrad)"/>
<!-- 阈值标记 -->
<line id="threshLine" x1="244.5" y1="24" x2="244.5" y2="42" stroke="#ff4757" stroke-width="1.5" stroke-dasharray="2 2"/>
<text id="threshLabel" x="244.5" y="54" text-anchor="middle" font-family="'IBM Plex Mono', monospace" font-size="8" fill="#ff4757">阈值 85%</text>
<!-- 数值 -->
<text id="confValue" x="280" y="18" text-anchor="end" font-family="'IBM Plex Mono', monospace" font-size="14" font-weight="500" fill="#ffaa00">0%</text>
</g>
<!-- ======================== 系统提示气泡 ======================== -->
<g id="sysPrompt" transform="translate(620, 380)" opacity="0">
<rect x="0" y="0" width="280" height="60" rx="10" fill="rgba(0,255,200,0.08)" stroke="#00ffc8" stroke-width="1.5" filter="url(#glowPrimary)"/>
<!-- 小三角 -->
<polygon points="40,60 50,72 60,60" fill="rgba(0,255,200,0.08)" stroke="#00ffc8" stroke-width="1.5"/>
<line x1="41" y1="60" x2="59" y2="60" stroke="rgba(12,20,36,1)" stroke-width="2"/>
<text x="140" y="24" text-anchor="middle" font-family="Orbitron, sans-serif" font-size="9" fill="#00ffc8" letter-spacing="1" opacity="0.7">SYSTEM PROMPT</text>
<text x="140" y="46" text-anchor="middle" font-family="'IBM Plex Mono', monospace" font-size="14" font-weight="500" fill="#e8f0ff">"是否需要打开该设备?"</text>
</g>
<!-- ======================== 用户回复指示 ======================== -->
<g id="userResponse" transform="translate(280, 460)" opacity="0">
<rect x="-55" y="-16" width="110" height="32" rx="16" fill="rgba(0,255,136,0.1)" stroke="#00ff88" stroke-width="1.5"/>
<text x="0" y="5" text-anchor="middle" font-family="Orbitron, sans-serif" font-size="13" font-weight="700" fill="#00ff88" letter-spacing="2">NOD · 是</text>
</g>
<!-- ======================== 数据流粒子容器 ======================== -->
<g id="dataParticles"></g>
<!-- ======================== 装饰性元素 ======================== -->
<!-- 左侧装饰线 -->
<line x1="50" y1="100" x2="50" y2="650" stroke="#0f1d35" stroke-width="0.5"/>
<circle cx="50" cy="100" r="2" fill="#0f1d35"/>
<circle cx="50" cy="650" r="2" fill="#0f1d35"/>
<!-- 右侧装饰线 -->
<line x1="1350" y1="100" x2="1350" y2="650" stroke="#0f1d35" stroke-width="0.5"/>
<circle cx="1350" cy="100" r="2" fill="#0f1d35"/>
<circle cx="1350" cy="650" r="2" fill="#0f1d35"/>
<!-- 角落装饰 -->
<path d="M20,20 L60,20 M20,20 L20,60" stroke="#1a3050" stroke-width="1.5" fill="none"/>
<path d="M1380,20 L1340,20 M1380,20 L1380,60" stroke="#1a3050" stroke-width="1.5" fill="none"/>
<path d="M20,730 L60,730 M20,730 L20,690" stroke="#1a3050" stroke-width="1.5" fill="none"/>
<path d="M1380,730 L1340,730 M1380,730 L1380,690" stroke="#1a3050" stroke-width="1.5" fill="none"/>
<!-- IFR标签 -->
<text x="700" y="725" text-anchor="middle" font-family="Orbitron, sans-serif" font-size="10" fill="#1a3050" letter-spacing="4">IDEAL FINAL RESULT · TRIZ</text>
</svg>
</div>
<!-- 控制面板 -->
<div class="controls">
<div class="control-group">
<div class="control-label">
<span>注视停留判定阈值</span>
<span class="control-value" id="dwellVal">300ms</span>
</div>
<input type="range" id="dwellSlider" min="150" max="600" value="300" step="10"/>
</div>
<div class="control-group">
<div class="control-label">
<span>意图置信度阈值</span>
<span class="control-value" id="confThreshVal">85%</span>
</div>
<input type="range" id="confThreshSlider" min="60" max="98" value="85" step="1"/>
</div>
</div>
<script>
(function() {
'use strict';
// ====== 常量与配置 ======
const PHASE_NAMES = ['IDLE', 'GAZE_LOCK', 'EXPRESSION', 'COMPUTE', 'CONFIRM', 'RESPOND', 'EXECUTE', 'RESET'];
const PHASE_LABELS = ['待机', '视线锁定', '微表情捕捉', '边缘计算', '系统主动确认', '极简回复', '执行动作', '重置'];
const PHASE_DURATIONS = [800, 2500, 2000, 2500, 2000, 1500, 2000, 1500]; // 基础时长(ms)
// ====== 状态变量 ======
let currentPhase = -1;
let phaseTime = 0;
let lastTimestamp = 0;
let dwellThreshold = 300;
let confidenceThreshold = 85;
let confidence = 0;
let dwellProgress = 0;
let animationId = null;
// ====== DOM 引用 ======
const svg = document.getElementById('scene');
const irisGroup = document.getElementById('irisGroup');
const pupil = document.getElementById('pupil');
const upperLid = document.getElementById('upperLid');
const lowerLid = document.getElementById('lowerLid');
const beamGroup = document.getElementById('beamGroup');
const reticle = document.getElementById('reticle');
const dwellRing = document.getElementById('dwellRing');
const dwellArc = document.getElementById('dwellArc');
const dwellText = document.getElementById('dwellText');
const expressionPanel = document.getElementById('expressionPanel');
const exprMouth = document.getElementById('exprMouth');
const exprLabel = document.getElementById('exprLabel');
const exprConf = document.getElementById('exprConf');
const confGauge = document.getElementById('confGauge');
const confBar = document.getElementById('confBar');
const confValue = document.getElementById('confValue');
const threshLine = document.getElementById('threshLine');
const threshLabel = document.getElementById('threshLabel');
const sysPrompt = document.getElementById('sysPrompt');
const userResponse = document.getElementById('userResponse');
const lampLight = document.getElementById('lampLight');
const bulb = document.getElementById('bulb');
const phaseStrip = document.getElementById('phaseStrip');
// 流水线节点
const nodes = [1,2,3,4].map(i => document.getElementById('node' + i));
// ====== 初始化相位指示条 ======
PHASE_LABELS.forEach((label, i) => {
if (i === 0 || i === 7) return; // 跳过IDLE和RESET
const chip = document.createElement('div');
chip.className = 'phase-chip';
chip.dataset.phase = i;
chip.textContent = label;
phaseStrip.appendChild(chip);
});
// ====== 滑块事件 ======
const dwellSlider = document.getElementById('dwellSlider');
const confThreshSlider = document.getElementById('confThreshSlider');
const dwellValEl = document.getElementById('dwellVal');
const confThreshValEl = document.getElementById('confThreshVal');
dwellSlider.addEventListener('input', function() {
dwellThreshold = parseInt(this.value);
dwellValEl.textContent = dwellThreshold + 'ms';
});
confThreshSlider.addEventListener('input', function() {
confidenceThreshold = parseInt(this.value);
confThreshValEl.textContent = confidenceThreshold + '%';
// 更新阈值标记位置
const threshX = 15 + (confidenceThreshold / 100) * 270;
threshLine.setAttribute('x1', threshX);
threshLine.setAttribute('x2', threshX);
threshLabel.setAttribute('x', threshX);
threshLabel.textContent = '阈值 ' + confidenceThreshold + '%';
});
// ====== 工具函数 ======
function lerp(a, b, t) { return a + (b - a) * Math.min(1, Math.max(0, t)); }
function easeInOut(t) { return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t; }
function easeOut(t) { return 1 - Math.pow(1 - t, 3); }
function setOpacity(el, val) { el.setAttribute('opacity', val); }
function activateNode(index, active) {
const node = nodes[index];
if (!node) return;
const rect = node.querySelector('rect');
const texts = node.querySelectorAll('text');
if (active) {
rect.setAttribute('fill', 'rgba(0,255,200,0.1)');
rect.setAttribute('stroke', '#00ffc8');
texts.forEach(t => { t.setAttribute('fill', '#00ffc8'); });
} else {
rect.setAttribute('fill', '#0c1424');
rect.setAttribute('stroke', '#1a2744');
texts[0].setAttribute('fill', '#5a7a9a');
texts[1].setAttribute('fill', '#3a5a7a');
}
}
function updatePhaseStrip(activePhase) {
const chips = phaseStrip.querySelectorAll('.phase-chip');
chips.forEach(chip => {
const p = parseInt(chip.dataset.phase);
chip.classList.remove('active', 'done');
if (p === activePhase) chip.classList.add('active');
else if (p < activePhase) chip.classList.add('done');
});
}
// ====== 眼球动画 ======
let irisTargetX = 0, irisTargetY = 0;
let irisCurrentX = 0, irisCurrentY = 0;
function updateEye(dt) {
// 平滑跟踪
irisCurrentX = lerp(irisCurrentX, irisTargetX, 0.03);
irisCurrentY = lerp(irisCurrentY, irisTargetY, 0.03);
irisGroup.setAttribute('transform', 'translate(' + irisCurrentX.toFixed(1) + ',' + irisCurrentY.toFixed(1) + ')');
}
// ====== 眨眼 ======
let blinkTimer = 0;
let isBlinking = false;
let blinkProgress = 0;
function updateBlink(dt) {
blinkTimer += dt;
if (!isBlinking && blinkTimer > 3000 + Math.random() * 2000) {
isBlinking = true;
blinkProgress = 0;
blinkTimer = 0;
}
if (isBlinking) {
blinkProgress += dt / 180;
if (blinkProgress >= 1) {
isBlinking = false;
blinkProgress = 0;
upperLid.setAttribute('d', 'M-90,0 Q-50,-55 0,-60 Q50,-55 90,0');
lowerLid.setAttribute('d', 'M-90,0 Q-50,55 0,60 Q50,55 90,0');
} else {
const t = blinkProgress < 0.5 ? easeInOut(blinkProgress * 2) : easeInOut(2 - blinkProgress * 2);
const lidY = t * 55;
upperLid.setAttribute('d', 'M-90,' + (0 + lidY * 0.3) + ' Q-50,' + (-55 + lidY) + ' 0,' + (-60 + lidY) + ' Q50,' + (-55 + lidY) + ' 90,' + (0 + lidY * 0.3));
lowerLid.setAttribute('d', 'M-90,' + (0 - lidY * 0.15) + ' Q-50,' + (55 - lidY * 0.3) + ' 0,' + (60 - lidY * 0.3) + ' Q50,' + (55 - lidY * 0.3) + ' 90,' + (0 - lidY * 0.15));
}
}
}
// ====== 光束粒子 ======
const beamParticleEls = [];
const BEAM_PARTICLE_COUNT = 8;
function initBeamParticles() {
const container = document.getElementById('beamParticles');
for (let i = 0; i < BEAM_PARTICLE_COUNT; i++) {
const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
circle.setAttribute('r', '2.5');
circle.setAttribute('fill', '#00ffc8');
circle.setAttribute('opacity', '0');
container.appendChild(circle);
beamParticleEls.push({ el: circle, t: i / BEAM_PARTICLE_COUNT, speed: 0.0004 + Math.random() * 0.0002 });
}
}
function updateBeamParticles(dt) {
const x1 = 370, y1 = 320, x2 = 1010, y2 = 280;
beamParticleEls.forEach(p => {
p.t += p.speed * dt;
if (p.t > 1) p.t -= 1;
const x = lerp(x1, x2, p.t);
const y = lerp(y1, y2, p.t);
const alpha = Math.sin(p.t * Math.PI) * 0.7;
p.el.setAttribute('cx', x);
p.el.setAttribute('cy', y);
p.el.setAttribute('opacity', alpha.toFixed(2));
});
}
// ====== 数据流粒子(流水线) ======
const dataParticleEls = [];
const DATA_PARTICLE_COUNT = 6;
function initDataParticles() {
const container = document.getElementById('dataParticles');
for (let i = 0; i < DATA_PARTICLE_COUNT; i++) {
const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
circle.setAttribute('r', '3');
circle.setAttribute('fill', '#00ffc8');
circle.setAttribute('opacity', '0');
circle.setAttribute('filter', 'url(#glowPrimary)');
container.appendChild(circle);
dataParticleEls.push({ el: circle, active: false });
}
}
// ====== 阶段控制 ======
function getPhaseDuration(phase) {
// 根据滑块值调整关键阶段时长
switch(phase) {
case 1: return PHASE_DURATIONS[1] * (dwellThreshold / 300); // GAZE_LOCK
case 3: return PHASE_DURATIONS[3] * (confidenceThreshold / 85); // COMPUTE
default: return PHASE_DURATIONS[phase];
}
}
function enterPhase(phase) {
currentPhase = phase;
phaseTime = 0;
updatePhaseStrip(phase);
switch(phase) {
case 0: // IDLE
irisTargetX = 0; irisTargetY = 0;
setOpacity(beamGroup, 0);
setOpacity(reticle, 0);
setOpacity(dwellRing, 0);
setOpacity(expressionPanel, 0);
setOpacity(confGauge, 0);
setOpacity(sysPrompt, 0);
setOpacity(userResponse, 0);
setOpacity(lampLight, 0);
nodes.forEach((_, i) => activateNode(i, false));
confidence = 0;
dwellProgress = 0;
bulb.setAttribute('fill', '#2a3040');
break;
case 1: // GAZE_LOCK
irisTargetX = 18; irisTargetY = -3;
setOpacity(beamGroup, 1);
setOpacity(reticle, 1);
setOpacity(dwellRing, 1);
activateNode(0, true);
break;
case 2: // EXPRESSION
setOpacity(expressionPanel, 1);
activateNode(1, true);
break;
case 3: // COMPUTE
setOpacity(confGauge, 1);
activateNode(2, true);
break;
case 4: // CONFIRM
setOpacity(sysPrompt, 1);
activateNode(3, true);
break;
case 5: // RESPOND
setOpacity(userResponse, 1);
break;
case 6: // EXECUTE
break;
case 7: // RESET
break;
}
}
function updatePhase(dt) {
phaseTime += dt;
const duration = getPhaseDuration(currentPhase);
const progress = Math.min(1, phaseTime / duration);
switch(currentPhase) {
case 0: // IDLE - 待机,眼球缓慢移动
irisTargetX = Math.sin(Date.now() * 0.001) * 5;
irisTargetY = Math.cos(Date.now() * 0.0013) * 3;
if (progress >= 1) enterPhase(1);
break;
case 1: // GAZE_LOCK - 视线锁定
{
// 眼球偏向目标
const irisShift = easeOut(progress);
irisTargetX = lerp(0, 22, irisShift);
irisTargetY = lerp(0, -4, irisShift);
// 瞳孔收缩(聚焦)
const pupilR = lerp(14, 10, easeOut(Math.min(1, progress * 1.5)));
pupil.setAttribute('r', pupilR);
// 停留计时
dwellProgress = progress;
const circumference = 2 * Math.PI * 42;
const dashLen = circumference * progress;
dwellArc.setAttribute('stroke-dasharray', dashLen + ' ' + circumference);
dwellText.textContent = Math.round(dwellThreshold * progress) + 'ms';
// 光束渐显
setOpacity(beamGroup, lerp(0.3, 1, easeOut(progress)));
if (progress >= 1) enterPhase(2);
}
break;
case 2: // EXPRESSION - 微表情捕捉
{
const ep = easeOut(progress);
// 表情从中性变为微皱眉
if (progress < 0.4) {
exprMouth.setAttribute('d', 'M-7,6 Q0,4 7,6'); // 中性
exprLabel.textContent = '中性';
} else if (progress < 0.7) {
exprMouth.setAttribute('d', 'M-7,5 Q0,3 7,5'); // 轻微不满
exprLabel.textContent = '轻微关注';
} else {
exprMouth.setAttribute('d', 'M-7,7 Q0,4 7,7'); // 皱眉
exprLabel.textContent = '关注倾向';
}
exprConf.textContent = '置信度: ' + Math.round(ep * 78) + '%';
// 更新面板中的情绪倾向
const emotionText = expressionPanel.querySelectorAll('text')[4];
if (emotionText) {
if (progress < 0.5) emotionText.textContent = '情绪倾向: 中性';
else emotionText.textContent = '情绪倾向: 需求信号';
}
if (progress >= 1) enterPhase(3);
}
break;
case 3: // COMPUTE - 边缘计算
{
const ep = easeInOut(progress);
confidence = ep * 95; // 最高算到95%
confBar.setAttribute('width', (confidence / 100 * 270).toFixed(1));
confValue.textContent = Math.round(confidence) + '%';
// 置信度颜色变化
if (confidence < 50) confValue.setAttribute('fill', '#ff4757');
else if (confidence < confidenceThreshold) confValue.setAttribute('fill', '#ffaa00');
else confValue.setAttribute('fill', '#00ff88');
// 停留环保持满
const circumference = 2 * Math.PI * 42;
dwellArc.setAttribute('stroke-dasharray', circumference + ' ' + circumference);
dwellArc.setAttribute('stroke', '#00ff88');
dwellText.textContent = dwellThreshold + 'ms';
if (progress >= 1) enterPhase(4);
}
break;
case 4: // CONFIRM - 系统主动确认
{
const ep = easeOut(Math.min(1, progress * 1.5));
setOpacity(sysPrompt, ep);
// 瞳孔略微扩大(听到系统提问)
const pupilR = lerp(10, 12, ep);
pupil.setAttribute('r', pupilR);
if (progress >= 1) enterPhase(5);
}
break;
case 5: // RESPOND - 极简回复
{
const ep = easeOut(progress);
setOpacity(userResponse, ep);
// 眼球微动(点头反馈)
irisTargetY = -4 + Math.sin(progress * Math.PI * 2) * 2;
if (progress >= 1) enterPhase(6);
}
break;
case 6: // EXECUTE - 执行动作
{
const ep = easeOut(progress);
// 灯泡亮起
bulb.setAttribute('fill', lerpColor('#2a3040', '#ffdd44', ep));
// 灯光渐显
setOpacity(lampLight, ep * 0.9);
// 瞄准框变绿
reticle.querySelectorAll('line, circle').forEach(el => {
el.setAttribute('stroke', ep > 0.5 ? '#00ff88' : '#00ffc8');
});
// 置信度条变绿
if (ep > 0.3) {
confBar.setAttribute('fill', '#00ff88');
}
if (progress >= 1) enterPhase(7);
}
break;
case 7: // RESET - 重置
{
const ep = easeInOut(progress);
// 所有元素淡出
setOpacity(beamGroup, 1 - ep);
setOpacity(reticle, 1 - ep);
setOpacity(dwellRing, 1 - ep);
setOpacity(expressionPanel, 1 - ep);
setOpacity(confGauge, 1 - ep);
setOpacity(sysPrompt, 1 - ep);
setOpacity(userResponse, 1 - ep);
setOpacity(lampLight, (1 - ep) * 0.9);
// 眼球回中
irisTargetX = lerp(22, 0, ep);
irisTargetY = lerp(-4, 0, ep);
// 瞳孔恢复
pupil.setAttribute('r', lerp(12, 14, ep));
// 停留环颜色重置
dwellArc.setAttribute('stroke', '#00ffc8');
// 灯泡重置
bulb.setAttribute('fill', lerpColor('#ffdd44', '#2a3040', ep));
// 流水线节点重置
if (ep > 0.5) {
nodes.forEach((_, i) => activateNode(i, false));
}
if (progress >= 1) enterPhase(0);
}
break;
}
}
// 颜色插值辅助
function lerpColor(c1, c2, t) {
const r1 = parseInt(c1.slice(1,3), 16), g1 = parseInt(c1.slice(3,5), 16), b1 = parseInt(c1.slice(5,7), 16);
const r2 = parseInt(c2.slice(1,3), 16), g2 = parseInt(c2.slice(3,5), 16), b2 = parseInt(c2.slice(5,7), 16);
const r = Math.round(lerp(r1, r2, t)), g = Math.round(lerp(g1, g2, t)), b = Math.round(lerp(b1, b2, t));
return '#' + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
}
// ====== 主动画循环 ======
function animate(timestamp) {
if (!lastTimestamp) lastTimestamp = timestamp;
const dt = Math.min(50, timestamp - lastTimestamp); // 限制最大帧间隔
lastTimestamp = timestamp;
updateEye(dt);
updateBlink(dt);
updatePhase(dt);
updateBeamParticles(dt);
animationId = requestAnimationFrame(animate);
}
// ====== 初始化 ======
function init() {
initBeamParticles();
initDataParticles();
// 初始化阈值标记位置
const threshX = 15 + (confidenceThreshold / 100) * 270;
threshLine.setAttribute('x1', threshX);
threshLine.setAttribute('x2', threshX);
threshLabel.setAttribute('x', threshX);
// 启动动画
enterPhase(0);
// 短暂延迟后开始第一阶段,给浏览器渲染时间
setTimeout(() => {
enterPhase(0);
lastTimestamp = 0;
animationId = requestAnimationFrame(animate);
}, 100);
}
// 页面加载完成后自动启动
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
// 页面重新可见时重启动画(重开即播)
document.addEventListener('visibilitychange', function() {
if (!document.hidden) {
lastTimestamp = 0;
enterPhase(0);
if (!animationId) {
animationId = requestAnimationFrame(animate);
}
}
});
})();
</script>
</body>
</html>
实现说明
本动画围绕 TRIZ「最终理想解」思想,直接展示"视觉-意图预测引擎"在理想状态下的完整运作原理,无需与传统语音交互做前后对比——因为 IFR 本身就是问题消失后的终极状态。
核心设计决策:
视觉引导:以明亮青色(
#00ffc8)标注所有"系统主动读取人"的关键路径——注视光束、瞄准框、停留计时环,让视线自然跟随"机器读取人"的信息流;而琥珀色(#ffaa00)和珊瑚红(#ff4757)则标记置信度与阈值边界,形成明确的视觉层级。资源利用的直观呈现:底部四节点流水线(GAZE → EXPR → EDGE → INTENT)逐步点亮,展示系统如何复用"人自然行为"(视线方向、微表情)作为输入资源,而非要求人额外付出语音指令的"成本"。
交互式参数控制:两个滑块分别控制「注视停留判定阈值」和「意图置信度阈值」,调整后动画时长和触发条件实时响应——用户可以亲手体验阈值变化如何影响系统判定速度和准确度的权衡。
动画自动循环:页面加载后自动从"待机 → 视线锁定 → 微表情捕捉 → 边缘计算 → 系统主动确认 → 极简回复 → 执行动作 → 重置"无缝循环,每次重开页面均从初始状态自动播放。
细节动效:眼球虹膜平滑追踪目标、周期性眨眼、光束粒子流、停留计时弧线填充、置信度仪表颜色渐变(红→黄→绿越过阈值)、灯泡点亮等,均用缓动函数精心调节,营造科技感与沉浸感。
积分规则:第一轮对话扣减8分,后续每轮扣6分
等待动画代码生成...
