独立渲染引擎就绪引擎就绪
<!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 href="https://fonts.googleapis.com/css2?family=Rajdhani:wght@300;500;700&family=IBM+Plex+Mono:wght@300;400;500&display=swap" rel="stylesheet">
<style>
:root {
--bg: #04080f;
--bio: #00f0d4;
--neural: #3388ff;
--intent: #f5c842;
--execute: #00ff88;
--voice: #ff5577;
--text: #b8c8da;
--dim: #14203a;
--panel: #0a1424;
}
*{margin:0;padding:0;box-sizing:border-box;}
body{
background:var(--bg);
color:var(--text);
font-family:'Rajdhani',sans-serif;
min-height:100vh;
display:flex;
flex-direction:column;
align-items:center;
justify-content:center;
overflow:hidden;
position:relative;
}
body::before{
content:'';
position:fixed;inset:0;
background:
radial-gradient(ellipse 60% 50% at 25% 40%, rgba(0,240,212,0.04) 0%, transparent 70%),
radial-gradient(ellipse 50% 50% at 72% 35%, rgba(51,136,255,0.04) 0%, transparent 70%),
radial-gradient(ellipse 40% 40% at 50% 80%, rgba(245,200,66,0.03) 0%, transparent 60%);
pointer-events:none;
z-index:0;
}
.container{
width:96vw;max-width:1500px;
position:relative;z-index:1;
}
.svg-wrap{width:100%;position:relative;}
.svg-wrap svg{width:100%;height:auto;display:block;}
/* 控制面板 */
.controls{
position:fixed;bottom:20px;left:50%;transform:translateX(-50%);
display:flex;align-items:center;gap:18px;
background:rgba(10,20,36,0.92);
backdrop-filter:blur(14px);
border:1px solid var(--dim);
border-radius:14px;
padding:10px 28px;
z-index:20;
font-family:'IBM Plex Mono',monospace;
}
.controls label{font-size:12px;color:var(--bio);white-space:nowrap;letter-spacing:0.5px;}
.controls input[type=range]{
-webkit-appearance:none;width:160px;height:3px;
background:var(--dim);border-radius:2px;outline:none;
}
.controls input[type=range]::-webkit-slider-thumb{
-webkit-appearance:none;width:14px;height:14px;border-radius:50%;
background:var(--bio);cursor:pointer;box-shadow:0 0 10px var(--bio);
}
.threshold-val{font-size:14px;font-weight:500;color:var(--intent);min-width:38px;text-align:right;}
.ctrl-btn{
background:transparent;border:1px solid var(--dim);
color:var(--text);font-family:'Rajdhani',sans-serif;
font-size:13px;padding:5px 16px;border-radius:8px;
cursor:pointer;transition:all .3s;letter-spacing:0.5px;
}
.ctrl-btn:hover{border-color:var(--bio);color:var(--bio);}
.ctrl-btn.active{border-color:var(--execute);color:var(--execute);}
/* 阶段指示器 */
.phase-bar{
position:fixed;top:16px;left:50%;transform:translateX(-50%);
display:flex;gap:6px;z-index:20;
}
.phase-dot{
width:8px;height:8px;border-radius:50%;
background:var(--dim);transition:all .4s;
}
.phase-dot.active{background:var(--bio);box-shadow:0 0 8px var(--bio);}
.phase-dot.done{background:rgba(0,240,212,0.3);}
/* SVG 内动画 */
@keyframes pulseGlow{
0%,100%{opacity:0.3;r:6;}
50%{opacity:0.9;r:10;}
}
@keyframes ringPulse{
0%{r:10;opacity:0.7;stroke-width:2;}
100%{r:35;opacity:0;stroke-width:0.5;}
}
@keyframes engineSpin{
to{transform:rotate(360deg);}
}
@keyframes engineSpinR{
to{transform:rotate(-360deg);}
}
@keyframes coreBreath{
0%,100%{r:22;opacity:0.6;}
50%{r:28;opacity:1;}
}
@keyframes dashFlow{
to{stroke-dashoffset:-24;}
}
@keyframes fadeUp{
from{opacity:0;transform:translateY(8px);}
to{opacity:1;transform:translateY(0);}
}
@keyframes execFlash{
0%{r:30;opacity:0.9;}
60%{r:80;opacity:0.4;}
100%{r:120;opacity:0;}
}
@keyframes voiceWave{
0%,100%{transform:scaleY(0.3);}
50%{transform:scaleY(1);}
}
@keyframes timelineGrow{
from{width:0;}
}
@keyframes particleTrail{
0%{opacity:0.9;r:3;}
100%{opacity:0;r:1;}
}
@media(prefers-reduced-motion:reduce){
*,*::before,*::after{
animation-duration:0.01ms!important;
transition-duration:0.01ms!important;
}
}
</style>
</head>
<body>
<!-- 阶段指示器 -->
<div class="phase-bar" id="phaseBar">
<div class="phase-dot" data-idx="0"></div>
<div class="phase-dot" data-idx="1"></div>
<div class="phase-dot" data-idx="2"></div>
<div class="phase-dot" data-idx="3"></div>
<div class="phase-dot" data-idx="4"></div>
</div>
<div class="container">
<div class="svg-wrap">
<svg id="mainSvg" viewBox="0 0 1400 800" xmlns="http://www.w3.org/2000/svg">
<defs>
<!-- 发光滤镜 -->
<filter id="glowBio" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur stdDeviation="4" result="b"/>
<feMerge><feMergeNode in="b"/><feMergeNode in="SourceGraphic"/></feMerge>
</filter>
<filter id="glowNeural" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur stdDeviation="6" result="b"/>
<feMerge><feMergeNode in="b"/><feMergeNode in="SourceGraphic"/></feMerge>
</filter>
<filter id="glowIntent" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur stdDeviation="5" result="b"/>
<feMerge><feMergeNode in="b"/><feMergeNode in="SourceGraphic"/></feMerge>
</filter>
<filter id="glowExec" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur stdDeviation="8" result="b"/>
<feMerge><feMergeNode in="b"/><feMergeNode in="SourceGraphic"/></feMerge>
</filter>
<filter id="softGlow" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur stdDeviation="12" result="b"/>
<feMerge><feMergeNode in="b"/><feMergeNode in="SourceGraphic"/></feMerge>
</filter>
<!-- 渐变 -->
<radialGradient id="engineAura" cx="50%" cy="50%" r="50%">
<stop offset="0%" stop-color="#3388ff" stop-opacity="0.15"/>
<stop offset="100%" stop-color="#3388ff" stop-opacity="0"/>
</radialGradient>
<radialGradient id="execAura" cx="50%" cy="50%" r="50%">
<stop offset="0%" stop-color="#00ff88" stop-opacity="0.2"/>
<stop offset="100%" stop-color="#00ff88" stop-opacity="0"/>
</radialGradient>
<linearGradient id="timelineTradGrad" x1="0" y1="0" x2="1" y2="0">
<stop offset="0%" stop-color="#ff5577" stop-opacity="0.8"/>
<stop offset="100%" stop-color="#ff5577" stop-opacity="0.3"/>
</linearGradient>
<linearGradient id="timelineIFRGrad" x1="0" y1="0" x2="1" y2="0">
<stop offset="0%" stop-color="#00f0d4" stop-opacity="0.8"/>
<stop offset="100%" stop-color="#00ff88" stop-opacity="0.3"/>
</linearGradient>
<!-- 信号路径(不可见,用于粒子运动) -->
<path id="sigPath1" d="M 260 278 Q 420 220 580 270" fill="none" stroke="none"/>
<path id="sigPath2" d="M 310 400 Q 440 340 580 290" fill="none" stroke="none"/>
</defs>
<!-- ========== 背景层 ========== -->
<g id="bgLayer">
<!-- 网格 -->
<pattern id="grid" width="40" height="40" patternUnits="userSpaceOnUse">
<path d="M 40 0 L 0 0 0 40" fill="none" stroke="#0e1a30" stroke-width="0.5"/>
</pattern>
<rect width="1400" height="800" fill="url(#grid)" opacity="0.5"/>
<!-- 扫描线装饰 -->
<line x1="0" y1="400" x2="1400" y2="400" stroke="#0e1a30" stroke-width="0.5" stroke-dasharray="4 8"/>
</g>
<!-- ========== 人形图层 ========== -->
<g id="humanLayer" opacity="0">
<!-- 人形轮廓(侧面,面向右) -->
<g transform="translate(60, 80)">
<!-- 头部 -->
<ellipse cx="155" cy="95" rx="52" ry="62" fill="none" stroke="#1e3456" stroke-width="1.5"/>
<!-- 面部特征线 -->
<path d="M 195 75 Q 210 85 207 100" fill="none" stroke="#1e3456" stroke-width="1" opacity="0.6"/>
<path d="M 200 105 L 195 115" fill="none" stroke="#1e3456" stroke-width="1" opacity="0.5"/>
<!-- 颈部 -->
<path d="M 140 155 L 138 180 M 168 155 L 170 180" fill="none" stroke="#1e3456" stroke-width="1.5"/>
<!-- 肩膀和躯干 -->
<path d="M 138 180 Q 80 190 60 230 L 55 370 Q 55 385 70 385 L 130 385 Q 140 385 140 375 L 142 280 L 145 375 Q 146 385 155 385 L 215 385 Q 228 385 225 370 L 210 230 Q 200 195 170 180"
fill="none" stroke="#1e3456" stroke-width="1.5"/>
<!-- 右臂(伸向设备) -->
<path d="M 210 230 Q 240 250 270 280 Q 290 300 300 310"
fill="none" stroke="#1e3456" stroke-width="1.5" stroke-linecap="round"/>
<!-- 左臂 -->
<path d="M 60 230 Q 40 260 35 310 Q 33 340 40 360"
fill="none" stroke="#1e3456" stroke-width="1.5" stroke-linecap="round"/>
<!-- 设备(手持控制器/耳机) -->
<rect x="285" y="295" width="55" height="35" rx="6" fill="none" stroke="#1e3456" stroke-width="1.5"/>
<rect x="290" y="300" width="18" height="25" rx="3" fill="none" stroke="#1e3456" stroke-width="0.8" opacity="0.5"/>
<!-- 内部神经/肌肉信号路径 -->
<path d="M 170 130 Q 175 140 172 155" fill="none" stroke="rgba(0,240,212,0.15)" stroke-width="1" stroke-dasharray="2 3"/>
<path d="M 172 155 Q 185 190 210 225" fill="none" stroke="rgba(0,240,212,0.15)" stroke-width="1" stroke-dasharray="2 3"/>
<path d="M 210 225 Q 240 250 270 275" fill="none" stroke="rgba(0,240,212,0.15)" stroke-width="1" stroke-dasharray="2 3"/>
</g>
<!-- 传感器节点 - 喉部 -->
<g id="sensorThroat" transform="translate(230, 238)">
<circle r="6" fill="var(--bio)" opacity="0.8" filter="url(#glowBio)">
<animate attributeName="r" values="5;8;5" dur="1.5s" repeatCount="indefinite"/>
<animate attributeName="opacity" values="0.6;1;0.6" dur="1.5s" repeatCount="indefinite"/>
</circle>
<circle r="10" fill="none" stroke="var(--bio)" stroke-width="1" opacity="0">
<animate attributeName="r" values="8;28;8" dur="2s" repeatCount="indefinite"/>
<animate attributeName="opacity" values="0.5;0;0.5" dur="2s" repeatCount="indefinite"/>
</circle>
</g>
<!-- 传感器节点 - 手部 -->
<g id="sensorHand" transform="translate(358, 360)">
<circle r="5" fill="var(--bio)" opacity="0.7" filter="url(#glowBio)">
<animate attributeName="r" values="4;7;4" dur="1.8s" repeatCount="indefinite"/>
<animate attributeName="opacity" values="0.5;0.9;0.5" dur="1.8s" repeatCount="indefinite"/>
</circle>
<circle r="9" fill="none" stroke="var(--bio)" stroke-width="1" opacity="0">
<animate attributeName="r" values="7;24;7" dur="2.2s" repeatCount="indefinite"/>
<animate attributeName="opacity" values="0.4;0;0.4" dur="2.2s" repeatCount="indefinite"/>
</circle>
</g>
<!-- 传感器标签 -->
<text x="195" y="225" fill="var(--bio)" font-family="IBM Plex Mono" font-size="9" opacity="0.7">sEMG 1000Hz</text>
<text x="330" y="348" fill="var(--bio)" font-family="IBM Plex Mono" font-size="9" opacity="0.7">μ-Radar</text>
</g>
<!-- ========== 信号流动层 ========== -->
<g id="signalLayer" opacity="0">
<!-- 可见信号路径线 -->
<path d="M 260 278 Q 420 220 580 270" fill="none" stroke="var(--bio)" stroke-width="1.5"
stroke-dasharray="8 4" opacity="0.5" filter="url(#glowBio)">
<animate attributeName="stroke-dashoffset" from="0" to="-24" dur="0.8s" repeatCount="indefinite"/>
</path>
<path d="M 310 400 Q 440 340 580 290" fill="none" stroke="var(--bio)" stroke-width="1.2"
stroke-dasharray="6 5" opacity="0.4" filter="url(#glowBio)">
<animate attributeName="stroke-dashoffset" from="0" to="-22" dur="1s" repeatCount="indefinite"/>
</path>
<!-- 信号波形装饰 - 喉部 -->
<g transform="translate(248, 260)" opacity="0.6">
<path d="M 0 0 Q 5 -8 10 0 Q 15 8 20 0 Q 25 -6 30 0 Q 35 5 40 0" fill="none" stroke="var(--bio)" stroke-width="1">
<animate attributeName="d"
values="M 0 0 Q 5 -8 10 0 Q 15 8 20 0 Q 25 -6 30 0 Q 35 5 40 0;
M 0 0 Q 5 6 10 0 Q 15 -7 20 0 Q 25 8 30 0 Q 35 -5 40 0;
M 0 0 Q 5 -8 10 0 Q 15 8 20 0 Q 25 -6 30 0 Q 35 5 40 0"
dur="0.6s" repeatCount="indefinite"/>
</path>
</g>
</g>
<!-- ========== 粒子层 ========== -->
<g id="particleLayer"></g>
<!-- ========== 意图引擎层 ========== -->
<g id="engineLayer" opacity="0">
<!-- 引擎光晕 -->
<circle cx="700" cy="280" r="120" fill="url(#engineAura)"/>
<!-- 外环 - 旋转 -->
<g transform="translate(700,280)">
<circle r="90" fill="none" stroke="#1a3060" stroke-width="1" stroke-dasharray="3 6">
<animateTransform attributeName="transform" type="rotate" from="0" to="360" dur="20s" repeatCount="indefinite"/>
</circle>
<!-- 六边形外框 -->
<polygon points="0,-78 67.5,-39 67.5,39 0,78 -67.5,39 -67.5,-39"
fill="none" stroke="var(--neural)" stroke-width="1.2" opacity="0.5">
<animateTransform attributeName="transform" type="rotate" from="0" to="360" dur="15s" repeatCount="indefinite"/>
</polygon>
<!-- 内六边形 -->
<polygon points="0,-55 47.6,-27.5 47.6,27.5 0,55 -47.6,27.5 -47.6,-27.5"
fill="none" stroke="var(--neural)" stroke-width="0.8" opacity="0.3">
<animateTransform attributeName="transform" type="rotate" from="0" to="-360" dur="10s" repeatCount="indefinite"/>
</polygon>
<!-- 核心脉冲 -->
<circle r="22" fill="rgba(51,136,255,0.15)" stroke="var(--neural)" stroke-width="1.5" opacity="0.7">
<animate attributeName="r" values="20;26;20" dur="2s" repeatCount="indefinite"/>
<animate attributeName="opacity" values="0.5;1;0.5" dur="2s" repeatCount="indefinite"/>
</circle>
<!-- 核心点 -->
<circle r="6" fill="var(--neural)" opacity="0.9" filter="url(#glowNeural)">
<animate attributeName="r" values="5;8;5" dur="1.5s" repeatCount="indefinite"/>
</circle>
<!-- 数据节点 -->
<g opacity="0.6">
<circle cx="0" cy="-65" r="3" fill="var(--bio)">
<animateTransform attributeName="transform" type="rotate" from="0" to="360" dur="8s" repeatCount="indefinite"/>
</circle>
<circle cx="56" cy="33" r="2.5" fill="var(--intent)">
<animateTransform attributeName="transform" type="rotate" from="0" to="-360" dur="6s" repeatCount="indefinite"/>
</circle>
<circle cx="-56" cy="33" r="2" fill="var(--execute)">
<animateTransform attributeName="transform" type="rotate" from="0" to="360" dur="12s" repeatCount="indefinite"/>
</circle>
</g>
</g>
<!-- 引擎标签 -->
<text x="700" y="395" text-anchor="middle" fill="var(--neural)" font-family="Rajdhani" font-size="16" font-weight="700" letter-spacing="3">意图预判引擎</text>
<text x="700" y="412" text-anchor="middle" fill="var(--neural)" font-family="IBM Plex Mono" font-size="9" opacity="0.5">INTENT PRE-JUDGMENT ENGINE</text>
</g>
<!-- ========== 候选动作层 ========== -->
<g id="candidateLayer" opacity="0">
<!-- 候选卡片1 - 高置信度 -->
<g id="cand1" transform="translate(830, 180)" opacity="0">
<rect x="0" y="0" width="130" height="50" rx="6" fill="rgba(10,20,40,0.85)" stroke="var(--intent)" stroke-width="1"/>
<text x="12" y="20" fill="var(--intent)" font-family="Rajdhani" font-size="14" font-weight="600">开 灯</text>
<text x="12" y="36" fill="var(--text)" font-family="IBM Plex Mono" font-size="10" opacity="0.6">confidence</text>
<rect x="82" y="24" width="38" height="6" rx="3" fill="var(--dim)"/>
<rect x="82" y="24" width="0" height="6" rx="3" fill="var(--intent)" id="cand1Bar"/>
<text x="122" y="20" fill="var(--intent)" font-family="IBM Plex Mono" font-size="11" text-anchor="end" id="cand1Val">0%</text>
</g>
<!-- 候选卡片2 -->
<g id="cand2" transform="translate(850, 245)" opacity="0">
<rect x="0" y="0" width="130" height="50" rx="6" fill="rgba(10,20,40,0.85)" stroke="var(--neural)" stroke-width="0.8"/>
<text x="12" y="20" fill="var(--neural)" font-family="Rajdhani" font-size="14" font-weight="600">导航回家</text>
<text x="12" y="36" fill="var(--text)" font-family="IBM Plex Mono" font-size="10" opacity="0.6">confidence</text>
<rect x="82" y="24" width="38" height="6" rx="3" fill="var(--dim)"/>
<rect x="82" y="24" width="0" height="6" rx="3" fill="var(--neural)" id="cand2Bar"/>
<text x="122" y="20" fill="var(--neural)" font-family="IBM Plex Mono" font-size="11" text-anchor="end" id="cand2Val">0%</text>
</g>
<!-- 候选卡片3 -->
<g id="cand3" transform="translate(835, 310)" opacity="0">
<rect x="0" y="0" width="130" height="50" rx="6" fill="rgba(10,20,40,0.85)" stroke="var(--dim)" stroke-width="0.8"/>
<text x="12" y="20" fill="var(--text)" font-family="Rajdhani" font-size="14" font-weight="500" opacity="0.6">播放音乐</text>
<text x="12" y="36" fill="var(--text)" font-family="IBM Plex Mono" font-size="10" opacity="0.4">confidence</text>
<rect x="82" y="24" width="38" height="6" rx="3" fill="var(--dim)"/>
<rect x="82" y="24" width="0" height="6" rx="3" fill="var(--text)" opacity="0.4" id="cand3Bar"/>
<text x="122" y="20" fill="var(--text)" font-family="IBM Plex Mono" font-size="11" text-anchor="end" opacity="0.5" id="cand3Val">0%</text>
</g>
<!-- 候选卡片4 -->
<g id="cand4" transform="translate(820, 375)" opacity="0">
<rect x="0" y="0" width="130" height="50" rx="6" fill="rgba(10,20,40,0.85)" stroke="var(--dim)" stroke-width="0.6"/>
<text x="12" y="20" fill="var(--text)" font-family="Rajdhani" font-size="14" font-weight="500" opacity="0.4">调节温度</text>
<text x="12" y="36" fill="var(--text)" font-family="IBM Plex Mono" font-size="10" opacity="0.3">confidence</text>
<rect x="82" y="24" width="38" height="6" rx="3" fill="var(--dim)"/>
<rect x="82" y="24" width="0" height="6" rx="3" fill="var(--text)" opacity="0.3" id="cand4Bar"/>
<text x="122" y="20" fill="var(--text)" font-family="IBM Plex Mono" font-size="11" text-anchor="end" opacity="0.35" id="cand4Val">0%</text>
</g>
<!-- 阈值线 -->
<g id="thresholdLine" opacity="0">
<line x1="815" y1="195" x2="965" y2="195" stroke="var(--voice)" stroke-width="1" stroke-dasharray="4 3" opacity="0.5"/>
<text x="968" y="199" fill="var(--voice)" font-family="IBM Plex Mono" font-size="9" opacity="0.6" id="thresholdLabel">阈值 85%</text>
</g>
</g>
<!-- ========== 语音确认层 ========== -->
<g id="voiceLayer" opacity="0">
<!-- 语音波形 -->
<g transform="translate(280, 310)">
<rect x="0" y="-15" width="3" height="30" rx="1.5" fill="var(--voice)" opacity="0.7" transform="scaleY(0.3)">
<animate attributeName="transform" values="scaleY(0.3);scaleY(1);scaleY(0.3)" dur="0.3s" repeatCount="indefinite"/>
</rect>
<rect x="6" y="-15" width="3" height="30" rx="1.5" fill="var(--voice)" opacity="0.7" transform="scaleY(0.5)">
<animate attributeName="transform" values="scaleY(0.5);scaleY(0.8);scaleY(0.5)" dur="0.35s" repeatCount="indefinite"/>
</rect>
<rect x="12" y="-15" width="3" height="30" rx="1.5" fill="var(--voice)" opacity="0.7" transform="scaleY(0.4)">
<animate attributeName="transform" values="scaleY(0.4);scaleY(1);scaleY(0.4)" dur="0.25s" repeatCount="indefinite"/>
</rect>
<rect x="18" y="-15" width="3" height="30" rx="1.5" fill="var(--voice)" opacity="0.7" transform="scaleY(0.6)">
<animate attributeName="transform" values="scaleY(0.6);scaleY(0.9);scaleY(0.6)" dur="0.4s" repeatCount="indefinite"/>
</rect>
<rect x="24" y="-15" width="3" height="30" rx="1.5" fill="var(--voice)" opacity="0.7" transform="scaleY(0.3)">
<animate attributeName="transform" values="scaleY(0.3);scaleY(0.7);scaleY(0.3)" dur="0.32s" repeatCount="indefinite"/>
</rect>
</g>
<text x="310" y="348" text-anchor="middle" fill="var(--voice)" font-family="IBM Plex Mono" font-size="10" opacity="0.6">语音确认</text>
</g>
<!-- ========== 执行层 ========== -->
<g id="executionLayer" opacity="0">
<!-- 执行闪光 -->
<circle cx="1100" cy="280" r="30" fill="none" stroke="var(--execute)" stroke-width="2" opacity="0" id="execFlash1">
<animate attributeName="r" values="30;90" dur="1s" fill="freeze"/>
<animate attributeName="opacity" values="0.9;0" dur="1s" fill="freeze"/>
</circle>
<circle cx="1100" cy="280" r="20" fill="var(--execute)" opacity="0" id="execFlash2" filter="url(#glowExec)">
<animate attributeName="r" values="15;50" dur="0.8s" fill="freeze"/>
<animate attributeName="opacity" values="0.8;0" dur="0.8s" fill="freeze"/>
</circle>
<!-- 执行目标 - 智能家居图标 -->
<g transform="translate(1070, 240)" opacity="0" id="execTarget">
<!-- 灯泡图标 -->
<path d="M 30 0 Q 50 0 50 25 Q 50 38 38 42 L 38 52 L 22 52 L 22 42 Q 10 38 10 25 Q 10 0 30 0 Z"
fill="none" stroke="var(--execute)" stroke-width="2"/>
<line x1="24" y1="55" x2="36" y2="55" stroke="var(--execute)" stroke-width="1.5"/>
<line x1="26" y1="59" x2="34" y2="59" stroke="var(--execute)" stroke-width="1.5"/>
<!-- 灯光射线 -->
<line x1="30" y1="-12" x2="30" y2="-6" stroke="var(--intent)" stroke-width="1.5" opacity="0.7"/>
<line x1="50" y1="-2" x2="56" y2="-8" stroke="var(--intent)" stroke-width="1.5" opacity="0.7"/>
<line x1="10" y1="-2" x2="4" y2="-8" stroke="var(--intent)" stroke-width="1.5" opacity="0.7"/>
<line x1="58" y1="18" x2="64" y2="18" stroke="var(--intent)" stroke-width="1.5" opacity="0.5"/>
<line x1="2" y1="18" x2="-4" y2="18" stroke="var(--intent)" stroke-width="1.5" opacity="0.5"/>
</g>
<text x="1100" y="330" text-anchor="middle" fill="var(--execute)" font-family="Rajdhani" font-size="15" font-weight="700" opacity="0" id="execLabel">即时执行</text>
<text x="1100" y="348" text-anchor="middle" fill="var(--execute)" font-family="IBM Plex Mono" font-size="10" opacity="0" id="execSub">< 50ms</text>
</g>
<!-- ========== 连接线:引擎到执行 ========== -->
<g id="engineToExec" opacity="0">
<path d="M 790 280 Q 920 260 1040 280" fill="none" stroke="var(--execute)" stroke-width="2"
stroke-dasharray="10 5" opacity="0.5" filter="url(#glowExec)">
<animate attributeName="stroke-dashoffset" from="0" to="-30" dur="0.6s" repeatCount="indefinite"/>
</path>
<!-- 箭头 -->
<polygon points="1045,280 1035,274 1035,286" fill="var(--execute)" opacity="0.7"/>
</g>
<!-- ========== 时间线对比层 ========== -->
<g id="timelineLayer" opacity="0" transform="translate(0, 0)">
<!-- 分割线 -->
<line x1="80" y1="490" x2="1320" y2="490" stroke="var(--dim)" stroke-width="0.5"/>
<!-- 标题 -->
<text x="80" y="520" fill="var(--text)" font-family="Rajdhani" font-size="14" font-weight="700" letter-spacing="2" opacity="0.7">时序对比 / TEMPORAL COMPARISON</text>
<!-- 传统交互时间线 -->
<g id="tradTimeline">
<text x="80" y="558" fill="var(--voice)" font-family="IBM Plex Mono" font-size="11" opacity="0.7">传统交互</text>
<!-- 时间轴 -->
<line x1="220" y1="554" x2="1300" y2="554" stroke="var(--dim)" stroke-width="1"/>
<!-- 阶段块 -->
<g id="tradBlocks" opacity="0">
<rect x="220" y="542" width="180" height="24" rx="3" fill="rgba(255,85,119,0.15)" stroke="var(--voice)" stroke-width="0.8"/>
<text x="310" y="558" text-anchor="middle" fill="var(--voice)" font-family="IBM Plex Mono" font-size="9">沉默等待</text>
<rect x="410" y="542" width="280" height="24" rx="3" fill="rgba(255,85,119,0.25)" stroke="var(--voice)" stroke-width="0.8"/>
<text x="550" y="558" text-anchor="middle" fill="var(--voice)" font-family="IBM Plex Mono" font-size="9">语音输入 + 识别</text>
<rect x="700" y="542" width="200" height="24" rx="3" fill="rgba(255,85,119,0.2)" stroke="var(--voice)" stroke-width="0.8"/>
<text x="800" y="558" text-anchor="middle" fill="var(--voice)" font-family="IBM Plex Mono" font-size="9">语义解析</text>
<rect x="910" y="542" width="120" height="24" rx="3" fill="rgba(255,85,119,0.15)" stroke="var(--voice)" stroke-width="0.8"/>
<text x="970" y="558" text-anchor="middle" fill="var(--voice)" font-family="IBM Plex Mono" font-size="9">执行</text>
<rect x="1040" y="542" width="100" height="24" rx="3" fill="rgba(255,85,119,0.1)" stroke="var(--voice)" stroke-width="0.8"/>
<text x="1090" y="558" text-anchor="middle" fill="var(--voice)" font-family="IBM Plex Mono" font-size="9">反馈</text>
</g>
<text x="1200" y="558" fill="var(--voice)" font-family="IBM Plex Mono" font-size="11" opacity="0" id="tradTime">~4.2s</text>
</g>
<!-- IFR 意图预判时间线 -->
<g id="ifrTimeline">
<text x="80" y="620" fill="var(--bio)" font-family="IBM Plex Mono" font-size="11" opacity="0.7">IFR 预判</text>
<!-- 时间轴 -->
<line x1="220" y1="616" x2="1300" y2="616" stroke="var(--dim)" stroke-width="1"/>
<!-- 阶段块 - 更短,且有重叠 -->
<g id="ifrBlocks" opacity="0">
<rect x="220" y="604" width="120" height="24" rx="3" fill="rgba(0,240,212,0.2)" stroke="var(--bio)" stroke-width="0.8"/>
<text x="280" y="620" text-anchor="middle" fill="var(--bio)" font-family="IBM Plex Mono" font-size="9">前导信号</text>
<rect x="310" y="604" width="180" height="24" rx="3" fill="rgba(51,136,255,0.2)" stroke="var(--neural)" stroke-width="0.8"/>
<text x="400" y="620" text-anchor="middle" fill="var(--neural)" font-family="IBM Plex Mono" font-size="9">意图预计算</text>
<rect x="470" y="604" width="80" height="24" rx="3" fill="rgba(245,200,66,0.2)" stroke="var(--intent)" stroke-width="0.8"/>
<text x="510" y="620" text-anchor="middle" fill="var(--intent)" font-family="IBM Plex Mono" font-size="9">确认</text>
<rect x="530" y="604" width="60" height="24" rx="3" fill="rgba(0,255,136,0.25)" stroke="var(--execute)" stroke-width="0.8"/>
<text x="560" y="620" text-anchor="middle" fill="var(--execute)" font-family="IBM Plex Mono" font-size="9">执行</text>
</g>
<text x="640" y="620" fill="var(--execute)" font-family="IBM Plex Mono" font-size="11" opacity="0" id="ifrTime">~0.45s</text>
</g>
<!-- 时间节省指示 -->
<g id="timeSaved" opacity="0">
<!-- 大括号标注节省的时间 -->
<line x1="600" y1="596" x2="600" y2="640" stroke="var(--execute)" stroke-width="1" stroke-dasharray="3 2" opacity="0.4"/>
<text x="620" y="636" fill="var(--execute)" font-family="Rajdhani" font-size="18" font-weight="700" opacity="0.9">节省 ~89%</text>
<text x="620" y="654" fill="var(--execute)" font-family="IBM Plex Mono" font-size="9" opacity="0.5">时间压缩比</text>
</g>
<!-- 原理标注 -->
<g id="principleNote" opacity="0">
<text x="80" y="700" fill="var(--intent)" font-family="Rajdhani" font-size="13" font-weight="500" opacity="0.6">IFR 核心原理:</text>
<text x="210" y="700" fill="var(--text)" font-family="IBM Plex Mono" font-size="11" opacity="0.45">利用发声前已存在的前导生理信号,将计算时间线前移至"意图形成期",消除了传统交互中的等待与识别延迟。</text>
<text x="80" y="724" fill="var(--intent)" font-family="Rajdhani" font-size="13" font-weight="500" opacity="0.6">资源巧用:</text>
<text x="170" y="724" fill="var(--text)" font-family="IBM Plex Mono" font-size="11" opacity="0.45">肌电信号是人体交互的"已有资源",传感器仅被动采集,无需用户额外操作,系统复杂度增幅极小。</text>
</g>
</g>
<!-- ========== 阶段文字标注 ========== -->
<g id="phaseText" opacity="0">
<text x="700" y="460" text-anchor="middle" fill="var(--text)" font-family="Rajdhani" font-size="20" font-weight="700" letter-spacing="2" id="phaseTitle" opacity="0"></text>
<text x="700" y="480" text-anchor="middle" fill="var(--text)" font-family="IBM Plex Mono" font-size="11" opacity="0.5" id="phaseDesc"></text>
</g>
</svg>
</div>
</div>
<!-- 控制面板 -->
<div class="controls">
<label>置信度阈值</label>
<input type="range" id="thresholdSlider" min="50" max="99" value="85" step="1"/>
<span class="threshold-val" id="thresholdDisplay">85%</span>
<div style="width:1px;height:20px;background:var(--dim);margin:0 4px;"></div>
<button class="ctrl-btn" id="restartBtn">重新播放</button>
</div>
<script>
(function(){
const SVG_NS = 'http://www.w3.org/2000/svg';
const svg = document.getElementById('mainSvg');
// 获取所有图层
const layers = {
human: document.getElementById('humanLayer'),
signal: document.getElementById('signalLayer'),
particle: document.getElementById('particleLayer'),
engine: document.getElementById('engineLayer'),
candidate: document.getElementById('candidateLayer'),
voice: document.getElementById('voiceLayer'),
execution: document.getElementById('executionLayer'),
engineToExec: document.getElementById('engineToExec'),
timeline: document.getElementById('timelineLayer'),
phaseText: document.getElementById('phaseText'),
};
// 候选动作元素
const candEls = {
1: { g: document.getElementById('cand1'), bar: document.getElementById('cand1Bar'), val: document.getElementById('cand1Val'), target: 92 },
2: { g: document.getElementById('cand2'), bar: document.getElementById('cand2Bar'), val: document.getElementById('cand2Val'), target: 87 },
3: { g: document.getElementById('cand3'), bar: document.getElementById('cand3Bar'), val: document.getElementById('cand3Val'), target: 68 },
4: { g: document.getElementById('cand4'), bar: document.getElementById('cand4Bar'), val: document.getElementById('cand4Val'), target: 52 },
};
// 阈值控制
let confidenceThreshold = 85;
const slider = document.getElementById('thresholdSlider');
const thresholdDisplay = document.getElementById('thresholdDisplay');
const thresholdLabel = document.getElementById('thresholdLabel');
slider.addEventListener('input', function(){
confidenceThreshold = parseInt(this.value);
thresholdDisplay.textContent = confidenceThreshold + '%';
thresholdLabel.textContent = '阈值 ' + confidenceThreshold + '%';
updateCandidateHighlights();
});
function updateCandidateHighlights(){
Object.keys(candEls).forEach(k => {
const c = candEls[k];
const above = c.target >= confidenceThreshold;
const rect = c.g.querySelector('rect');
if(above){
rect.setAttribute('stroke', k === '1' ? '#f5c842' : '#3388ff');
rect.setAttribute('stroke-width', '1.2');
c.g.style.opacity = '1';
} else {
rect.setAttribute('stroke', '#14203a');
rect.setAttribute('stroke-width', '0.6');
c.g.style.opacity = '0.4';
}
});
}
// 粒子系统
const particles = [];
const sigPath1 = document.getElementById('sigPath1');
const sigPath2 = document.getElementById('sigPath2');
function spawnParticle(pathEl, color, size, duration){
const totalLen = pathEl.getTotalLength();
const circle = document.createElementNS(SVG_NS, 'circle');
circle.setAttribute('r', size || 3);
circle.setAttribute('fill', color || '#00f0d4');
circle.setAttribute('opacity', '0');
circle.setAttribute('filter', 'url(#glowBio)');
layers.particle.appendChild(circle);
const p = {
el: circle,
pathLen: totalLen,
pathEl: pathEl,
startTime: performance.now(),
duration: duration || 1200,
alive: true
};
particles.push(p);
return p;
}
function updateParticles(now){
for(let i = particles.length - 1; i >= 0; i--){
const p = particles[i];
const elapsed = now - p.startTime;
const t = Math.min(elapsed / p.duration, 1);
const pt = p.pathEl.getPointAtLength(t * p.pathLen);
p.el.setAttribute('cx', pt.x);
p.el.setAttribute('cy', pt.y);
// 淡入淡出
const opacity = t < 0.1 ? t * 10 : t > 0.85 ? (1 - t) / 0.15 : 1;
p.el.setAttribute('opacity', Math.max(0, opacity * 0.9));
if(t >= 1){
p.el.remove();
particles.splice(i, 1);
}
}
}
// 持续产生粒子的定时器
let particleInterval = null;
function startParticleFlow(){
if(particleInterval) return;
particleInterval = setInterval(() => {
spawnParticle(sigPath1, '#00f0d4', 3, 1000 + Math.random() * 400);
spawnParticle(sigPath2, '#00f0d4', 2.5, 1200 + Math.random() * 500);
}, 200);
}
function stopParticleFlow(){
if(particleInterval){
clearInterval(particleInterval);
particleInterval = null;
}
}
// 辅助函数:设置元素透明度(带过渡)
function setOpacity(el, val, duration){
if(!el) return;
duration = duration || 600;
el.style.transition = 'opacity ' + duration + 'ms ease';
el.style.opacity = val;
}
// 辅助函数:动画数值
function animateValue(from, to, duration, callback, done){
const start = performance.now();
function tick(now){
const t = Math.min((now - start) / duration, 1);
const ease = t < 0.5 ? 2*t*t : -1+(4-2*t)*t; // easeInOutQuad
callback(from + (to - from) * ease);
if(t < 1) requestAnimationFrame(tick);
else if(done) done();
}
requestAnimationFrame(tick);
}
// 阶段指示器
const phaseDots = document.querySelectorAll('.phase-dot');
function setPhaseDot(idx, state){
if(!phaseDots[idx]) return;
phaseDots[idx].classList.remove('active','done');
if(state === 'active') phaseDots[idx].classList.add('active');
else if(state === 'done') phaseDots[idx].classList.add('done');
}
// ===== 动画阶段 =====
const PHASE_DURATIONS = [2000, 3000, 3500, 2000, 2500, 2000];
// 0:激活 1:前导信号 2:意图预计算 3:语音确认 4:即时执行 5:展示/保持
let animFrame = null;
let phaseTimeout = null;
let running = false;
function resetAll(){
// 停止所有动画
if(animFrame) cancelAnimationFrame(animFrame);
if(phaseTimeout) clearTimeout(phaseTimeout);
stopParticleFlow();
// 清除粒子
particles.forEach(p => p.el.remove());
particles.length = 0;
// 重置图层透明度
Object.values(layers).forEach(l => {
if(l) l.style.opacity = '0';
});
// 重置候选卡片
Object.values(candEls).forEach(c => {
c.g.style.opacity = '0';
c.bar.setAttribute('width', '0');
c.val.textContent = '0%';
});
// 重置时间线子元素
const tradBlocks = document.getElementById('tradBlocks');
const ifrBlocks = document.getElementById('ifrBlocks');
if(tradBlocks) tradBlocks.style.opacity = '0';
if(ifrBlocks) ifrBlocks.style.opacity = '0';
const tradTime = document.getElementById('tradTime');
const ifrTime = document.getElementById('ifrTime');
if(tradTime) tradTime.style.opacity = '0';
if(ifrTime) ifrTime.style.opacity = '0';
// 重置时间节省
const timeSaved = document.getElementById('timeSaved');
if(timeSaved) timeSaved.style.opacity = '0';
const principleNote = document.getElementById('principleNote');
if(principleNote) principleNote.style.opacity = '0';
// 重置执行层
const execTarget = document.getElementById('execTarget');
const execLabel = document.getElementById('execLabel');
const execSub = document.getElementById('execSub');
if(execTarget) execTarget.style.opacity = '0';
if(execLabel) execLabel.style.opacity = '0';
if(execSub) execSub.style.opacity = '0';
// 重置阈值线
const thresholdLine = document.getElementById('thresholdLine');
if(thresholdLine) thresholdLine.style.opacity = '0';
// 重置阶段指示器
phaseDots.forEach(d => d.classList.remove('active','done'));
// 重置阶段文字
const phaseTitle = document.getElementById('phaseTitle');
const phaseDesc = document.getElementById('phaseDesc');
if(phaseTitle) phaseTitle.style.opacity = '0';
if(phaseDesc) phaseDesc.textContent = '';
}
function startAnimation(){
resetAll();
running = true;
phase0_activate();
}
// 阶段0:激活 - 人形图和传感器出现
function phase0_activate(){
setPhaseDot(0, 'active');
setOpacity(layers.human, 1, 800);
setTimeout(() => {
// 传感器脉冲增强
setOpacity(layers.engine, 0.4, 1000);
}, 600);
setTimeout(() => {
setOpacity(layers.phaseText, 1, 400);
const pt = document.getElementById('phaseTitle');
const pd = document.getElementById('phaseDesc');
pt.style.opacity = '1';
pt.textContent = '微生理信号采集阵列';
pd.textContent = 'sEMG传感器 · 微雷达 · 1000Hz采样率';
pt.setAttribute('fill', '#00f0d4');
}, 800);
phaseTimeout = setTimeout(() => {
setPhaseDot(0, 'done');
phase1_presignal();
}, PHASE_DURATIONS[0]);
}
// 阶段1:前导信号 - 传感器捕获信号,粒子流向引擎
function phase1_presignal(){
setPhaseDot(1, 'active');
setOpacity(layers.signal, 1, 600);
startParticleFlow();
const pt = document.getElementById('phaseTitle');
const pd = document.getElementById('phaseDesc');
pt.textContent = '前导信号捕获';
pd.textContent = '喉部/面部微肌肉收缩 → 电信号产生(发声前200-500ms)';
pt.setAttribute('fill', '#00f0d4');
// 引擎开始接收
setTimeout(() => {
setOpacity(layers.engine, 1, 800);
}, 500);
phaseTimeout = setTimeout(() => {
setPhaseDot(1, 'done');
phase2_precompute();
}, PHASE_DURATIONS[1]);
}
// 阶段2:意图预计算 - 引擎处理,候选动作出现,置信度上升
function phase2_precompute(){
setPhaseDot(2, 'active');
const pt = document.getElementById('phaseTitle');
const pd = document.getElementById('phaseDesc');
pt.textContent = '意图预计算';
pd.textContent = '结合界面上下文,生成候选动作集 · 置信度持续攀升';
pt.setAttribute('fill', '#3388ff');
// 显示候选卡片和阈值线
setOpacity(layers.candidate, 1, 600);
setTimeout(() => {
const tl = document.getElementById('thresholdLine');
if(tl) setOpacity(tl, 1, 400);
}, 300);
// 依次显示候选卡片并动画置信度
const delay = 300;
Object.keys(candEls).forEach((k, i) => {
const c = candEls[k];
setTimeout(() => {
setOpacity(c.g, 1, 400);
// 动画置信度值
const targetConf = c.target;
animateValue(0, targetConf, 1200, (v) => {
c.val.textContent = Math.round(v) + '%';
c.bar.setAttribute('width', (v / 100 * 38) + '');
});
}, i * delay);
});
// 延迟后高亮超阈值项
setTimeout(() => {
updateCandidateHighlights();
}, 1800);
phaseTimeout = setTimeout(() => {
setPhaseDot(2, 'done');
phase3_confirm();
}, PHASE_DURATIONS[2]);
}
// 阶段3:语音确认 - 用户开口,系统瞬间匹配
function phase3_confirm(){
setPhaseDot(3, 'active');
const pt = document.getElementById('phaseTitle');
const pd = document.getElementById('phaseDesc');
pt.textContent = '语音微确认';
pd.textContent = '用户开口说第一个词 → 系统已在高置信度区间完成匹配';
pt.setAttribute('fill', '#ff5577');
setOpacity(layers.voice, 1, 400);
// 高亮匹配的候选
setTimeout(() => {
const c1 = candEls[1];
const rect = c1.g.querySelector('rect');
rect.setAttribute('stroke', '#00ff88');
rect.setAttribute('stroke-width', '2');
// 闪光效果
c1.g.style.filter = 'url(#glowExec)';
setTimeout(() => { c1.g.style.filter = ''; }, 600);
}, 500);
phaseTimeout = setTimeout(() => {
setPhaseDot(3, 'done');
phase4_execute();
}, PHASE_DURATIONS[3]);
}
// 阶段4:即时执行
function phase4_execute(){
setPhaseDot(4, 'active');
const pt = document.getElementById('phaseTitle');
const pd = document.getElementById('phaseDesc');
pt.textContent = '即时执行';
pd.textContent = '指令瞬间下发 → 设备响应 → 用户感知"零延迟"';
pt.setAttribute('fill', '#00ff88');
// 停止信号粒子
stopParticleFlow();
// 显示执行连线和目标
setOpacity(layers.engineToExec, 1, 300);
setTimeout(() => {
setOpacity(layers.execution, 1, 200);
// 重建闪光动画(克隆替换以重启动画)
const flash1 = document.getElementById('execFlash1');
const flash2 = document.getElementById('execFlash2');
if(flash1){
const clone1 = flash1.cloneNode(true);
clone1.removeAttribute('opacity');
flash1.parentNode.replaceChild(clone1, flash1);
}
if(flash2){
const clone2 = flash2.cloneNode(true);
clone2.removeAttribute('opacity');
flash1.parentNode.replaceChild(clone2, flash2);
}
// 显示目标图标
const execTarget = document.getElementById('execTarget');
const execLabel = document.getElementById('execLabel');
const execSub = document.getElementById('execSub');
setTimeout(() => {
if(execTarget){ setOpacity(execTarget, 1, 400); }
if(execLabel){ setOpacity(execLabel, 1, 400); }
if(execSub){ setOpacity(execSub, 1, 400); }
}, 300);
}, 400);
// 隐藏语音层
setTimeout(() => { setOpacity(layers.voice, 0, 300); }, 800);
phaseTimeout = setTimeout(() => {
setPhaseDot(4, 'done');
phase5_timeline();
}, PHASE_DURATIONS[4]);
}
// 阶段5:时间线对比展示
function phase5_timeline(){
setOpacity(layers.timeline, 1, 800);
// 显示传统时间线
setTimeout(() => {
const tradBlocks = document.getElementById('tradBlocks');
if(tradBlocks) setOpacity(tradBlocks, 1, 600);
setTimeout(() => {
const tradTime = document.getElementById('tradTime');
if(tradTime) setOpacity(tradTime, 1, 400);
}, 400);
}, 500);
// 显示IFR时间线
setTimeout(() => {
const ifrBlocks = document.getElementById('ifrBlocks');
if(ifrBlocks) setOpacity(ifrBlocks, 1, 600);
setTimeout(() => {
const ifrTime = document.getElementById('ifrTime');
if(ifrTime) setOpacity(ifrTime, 1, 400);
}, 400);
}, 1500);
// 显示时间节省
setTimeout(() => {
const timeSaved = document.getElementById('timeSaved');
if(timeSaved) setOpacity(timeSaved, 1, 600);
}, 2500);
// 显示原理标注
setTimeout(() => {
const principleNote = document.getElementById('principleNote');
if(principleNote) setOpacity(principleNote, 1, 800);
}, 3000);
// 保持后重新循环
phaseTimeout = setTimeout(() => {
running = false;
startAnimation();
}, PHASE_DURATIONS[5]);
}
// 动画帧循环(用于粒子更新)
function mainLoop(now){
updateParticles(now);
if(running || particles.length > 0){
animFrame = requestAnimationFrame(mainLoop);
}
}
// 重新播放按钮
document.getElementById('restartBtn').addEventListener('click', function(){
startAnimation();
animFrame = requestAnimationFrame(mainLoop);
});
// 页面加载后自动开始
window.addEventListener('DOMContentLoaded', function(){
// 短暂延迟确保渲染完成
setTimeout(() => {
startAnimation();
animFrame = requestAnimationFrame(mainLoop);
}, 300);
});
// iframe重载时也自动开始
window.addEventListener('load', function(){
if(!running){
startAnimation();
animFrame = requestAnimationFrame(mainLoop);
}
});
// 页面可见性变化时处理
document.addEventListener('visibilitychange', function(){
if(!document.hidden && !running){
startAnimation();
animFrame = requestAnimationFrame(mainLoop);
}
});
})();
</script>
</body>
</html>
这是一个完整的、自动播放的 SVG 原理动画,以下是实现要点说明:
设计思路
- 美学方向:深空暗色背景 + 生物信号青绿 + 神经处理蓝 + 意图金色 + 执行绿色的配色体系,营造"生物-数字融合"的技术美学感
- 字体选择:Rajdhani(显示/标题)+ IBM Plex Mono(数据/标注),形成科技感与数据感的层次对比
- IFR 聚焦:直接展示"发声前预判→即时执行"的理想状态,不制作前后对比拖沓
五阶段动画流程(自动循环播放)
- 激活 — 人形轮廓与传感器节点渐现,sEMG 与微雷达标注亮起
- 前导信号 — 粒子沿贝塞尔曲线从传感器流向引擎,波形动画模拟 1000Hz 采样信号
- 意图预计算 — 引擎核心旋转加速,四张候选动作卡片依次出现,置信度条动态攀升,阈值线标识超限项
- 语音微确认 — 声波动画出现,最高置信度项瞬间匹配高亮(绿色闪光)
- 即时执行 — 执行闪光爆发,灯泡图标亮起,底部时间线对比展示传统 ~4.2s vs IFR ~0.45s(节省 89%)
交互功能
- 置信度阈值滑块:拖动可实时改变 50%-99% 阈值,低于阈值的候选卡片自动降低视觉权重,帮助理解阈值对预判准确率的影响
- 重新播放按钮:随时重启动画循环
积分规则:第一轮对话扣减8分,后续每轮扣6分
等待动画代码生成...
