这是一个非常有趣的设计挑战——将"傲娇害羞退缩导致信息链断裂"的社交问题,通过"魔法共鸣"的场效应灯光同步来解决,并且要用 IFR(最终理想解)的视角来呈现。让我深入设计这个动画。
<!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=Cinzel:wght@400;700;900&family=Noto+Sans+SC:wght@300;400;700&display=swap" rel="stylesheet">
<style>
:root {
--bg: #06060c;
--fg: #e8dfd4;
--muted: #6a6575;
--accent: #c9a84c;
--card: #0e0e18;
--border: #22222e;
--red: #ff2d55;
--blue: #00b4ff;
--green: #00ff88;
--room-evil: #0c0a14;
--room-neuro: #0a0c16;
}
*{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;
}
body::before{
content:'';position:fixed;inset:0;
background:
radial-gradient(ellipse at 20% 25%,rgba(50,20,70,0.25) 0%,transparent 55%),
radial-gradient(ellipse at 80% 75%,rgba(15,30,70,0.25) 0%,transparent 55%),
radial-gradient(ellipse at 50% 50%,rgba(201,168,76,0.03) 0%,transparent 40%);
pointer-events:none;z-index:0;
}
.container{position:relative;z-index:1;width:100%;max-width:1260px;padding:20px 16px}
header{text-align:center;margin-bottom:18px}
header h1{
font-family:'Cinzel',serif;font-weight:900;
font-size:clamp(22px,3.8vw,40px);letter-spacing:.08em;
color:var(--accent);
text-shadow:0 0 40px rgba(201,168,76,0.25),0 2px 8px rgba(0,0,0,0.6);
}
header p{font-size:13px;color:var(--muted);margin-top:5px;font-weight:300;letter-spacing:.12em}
.animation-wrapper{
width:100%;
background:linear-gradient(145deg,#0c0a16 0%,#08080e 45%,#0a0c18 100%);
border:1px solid var(--border);border-radius:14px;
overflow:hidden;
box-shadow:0 0 80px rgba(0,0,0,0.6),inset 0 0 80px rgba(0,0,0,0.4);
}
.animation-wrapper svg{width:100%;height:auto;display:block}
/* 控制面板 */
.control-panel{
display:flex;flex-wrap:wrap;align-items:center;justify-content:center;
gap:14px;margin-top:20px;padding:18px 20px;
background:var(--card);border:1px solid var(--border);border-radius:12px;
}
.symbol-btn{
display:flex;flex-direction:column;align-items:center;gap:3px;
padding:12px 22px;border:2px solid var(--border);border-radius:10px;
background:transparent;color:var(--fg);
font-family:'Noto Sans SC',sans-serif;font-size:13px;
cursor:pointer;transition:all .3s ease;min-width:108px;
position:relative;overflow:hidden;
}
.symbol-btn::before{
content:'';position:absolute;inset:0;opacity:0;transition:opacity .3s;
border-radius:8px;
}
.symbol-btn.moon::before{background:radial-gradient(circle,rgba(255,45,85,0.15),transparent 70%)}
.symbol-btn.sun::before{background:radial-gradient(circle,rgba(0,180,255,0.15),transparent 70%)}
.symbol-btn.star::before{background:radial-gradient(circle,rgba(0,255,136,0.15),transparent 70%)}
.symbol-btn:hover::before{opacity:1}
.symbol-btn:hover{transform:translateY(-2px);box-shadow:0 6px 24px rgba(0,0,0,0.4)}
.symbol-btn.moon{border-color:rgba(255,45,85,0.25)}
.symbol-btn.moon:hover,.symbol-btn.moon.active{border-color:var(--red);box-shadow:0 0 24px rgba(255,45,85,0.25)}
.symbol-btn.sun{border-color:rgba(0,180,255,0.25)}
.symbol-btn.sun:hover,.symbol-btn.sun.active{border-color:var(--blue);box-shadow:0 0 24px rgba(0,180,255,0.25)}
.symbol-btn.star{border-color:rgba(0,255,136,0.25)}
.symbol-btn.star:hover,.symbol-btn.star.active{border-color:var(--green);box-shadow:0 0 24px rgba(0,255,136,0.25)}
.symbol-btn.active::before{opacity:1}
.symbol-btn.active{transform:translateY(-1px)}
.symbol-icon{font-size:26px;line-height:1}
.symbol-name{font-weight:700;font-size:13px}
.symbol-color-tag{font-size:10px;opacity:.5;letter-spacing:.06em}
.moon .symbol-icon{color:var(--red)}.sun .symbol-icon{color:var(--blue)}.star .symbol-icon{color:var(--green)}
.reset-btn{
padding:10px 22px;background:transparent;border:1px solid var(--border);
border-radius:8px;color:var(--muted);font-family:'Noto Sans SC',sans-serif;
font-size:12px;cursor:pointer;transition:all .3s;letter-spacing:.05em;
}
.reset-btn:hover{border-color:var(--accent);color:var(--accent)}
.status-bar{
flex-basis:100%;text-align:center;padding-top:10px;
border-top:1px solid var(--border);margin-top:6px;
}
.status-text{
font-size:12px;color:var(--muted);transition:color .4s;
font-weight:300;letter-spacing:.08em;
}
.status-text.active{color:var(--fg)}
/* IFR说明 */
.info-panel{
margin-top:20px;padding:18px 22px;
background:var(--card);border:1px solid var(--border);border-radius:12px;
font-size:12.5px;line-height:1.9;color:var(--muted);
}
.info-panel h3{
font-family:'Cinzel',serif;color:var(--accent);font-size:14px;
margin-bottom:8px;letter-spacing:.06em;
}
.info-panel .hl{color:var(--fg);font-weight:700}
.ifr-core{
margin-top:12px;padding:12px 16px;
background:rgba(201,168,76,0.05);border-left:3px solid var(--accent);
border-radius:0 6px 6px 0;
}
.ifr-core .hl{color:var(--accent)}
/* SVG内动画 */
@keyframes rotate-cw{from{transform:rotate(0deg)}to{transform:rotate(360deg)}}
@keyframes rotate-ccw{from{transform:rotate(0deg)}to{transform:rotate(-360deg)}}
@keyframes flicker{0%,100%{opacity:.82}25%{opacity:1}50%{opacity:.72}75%{opacity:.93}}
@keyframes pulse-soft{0%,100%{opacity:.08}50%{opacity:.18}}
@keyframes dash-flow{from{stroke-dashoffset:0}to{stroke-dashoffset:-30}}
@keyframes float-y{0%,100%{transform:translateY(0)}50%{transform:translateY(-6px)}}
.rune-ring{animation:rotate-cw 45s linear infinite;transform-origin:0 0}
.geo-ring{animation:rotate-ccw 28s linear infinite;transform-origin:0 0}
.inner-deco{animation:rotate-cw 18s linear infinite;transform-origin:0 0}
.candle-glow{animation:flicker 2.2s ease-in-out infinite}
.ambient-pulse{animation:pulse-soft 5s ease-in-out infinite}
.dash-anim{animation:dash-flow 1.5s linear infinite}
.float-anim{animation:float-y 4s ease-in-out infinite}
@media(prefers-reduced-motion:reduce){
*,*::before,*::after{animation-duration:.01ms!important;transition-duration:.15s!important}
}
@media(max-width:640px){
.container{padding:10px 8px}
.symbol-btn{padding:8px 14px;min-width:85px}
.symbol-icon{font-size:20px}
.info-panel{font-size:11.5px;padding:14px 16px}
}
</style>
</head>
<body>
<div class="container">
<header>
<h1>ARCANE RESONANCE</h1>
<p>IFR 理想解原理动画 — 灯光同步确认机制</p>
</header>
<div class="animation-wrapper">
<svg id="main-svg" viewBox="0 0 1400 720" xmlns="http://www.w3.org/2000/svg">
<defs>
<!-- 发光滤镜 -->
<filter id="glow-sm" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur in="SourceGraphic" stdDeviation="4" result="b"/>
<feMerge><feMergeNode in="b"/><feMergeNode in="SourceGraphic"/></feMerge>
</filter>
<filter id="glow-md" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur in="SourceGraphic" stdDeviation="10" result="b"/>
<feMerge><feMergeNode in="b"/><feMergeNode in="SourceGraphic"/></feMerge>
</filter>
<filter id="glow-lg" x="-80%" y="-80%" width="260%" height="260%">
<feGaussianBlur in="SourceGraphic" stdDeviation="22" result="b"/>
<feMerge><feMergeNode in="b"/><feMergeNode in="SourceGraphic"/></feMerge>
</filter>
<filter id="glow-xl" x="-100%" y="-100%" width="300%" height="300%">
<feGaussianBlur in="SourceGraphic" stdDeviation="40" result="b"/>
<feMerge><feMergeNode in="b"/><feMergeNode in="SourceGraphic"/></feMerge>
</filter>
<!-- 房间渐变 -->
<radialGradient id="evil-ambient" cx="50%" cy="25%" r="65%">
<stop offset="0%" stop-color="#1a1028" stop-opacity="0.6"/>
<stop offset="100%" stop-color="#0c0a14" stop-opacity="0"/>
</radialGradient>
<radialGradient id="neuro-ambient" cx="50%" cy="65%" r="60%">
<stop offset="0%" stop-color="#101828" stop-opacity="0.6"/>
<stop offset="100%" stop-color="#0a0c16" stop-opacity="0"/>
</radialGradient>
<!-- 法阵核心渐变(动态替换) -->
<radialGradient id="core-grad" cx="50%" cy="50%" r="50%">
<stop offset="0%" stop-color="#2a3a6a" stop-opacity="0.6"/>
<stop offset="60%" stop-color="#1a2a4a" stop-opacity="0.2"/>
<stop offset="100%" stop-color="#0a1020" stop-opacity="0"/>
</radialGradient>
<!-- 吊灯光渐变 -->
<radialGradient id="candle-grad" cx="50%" cy="50%" r="50%">
<stop offset="0%" stop-color="#c9a84c" stop-opacity="0.9"/>
<stop offset="100%" stop-color="#c9a84c" stop-opacity="0"/>
</radialGradient>
<!-- 连接路径 -->
<path id="wifi-path" d="M480,210 C600,80 800,80 920,380" fill="none"/>
<!-- 符文路径 -->
<path id="rune-circle" d="M-125,0 a125,125 0 1,1 250,0 a125,125 0 1,1 -250,0" fill="none"/>
</defs>
<!-- ===== 全局背景 ===== -->
<rect width="1400" height="720" fill="#08080e"/>
<!-- ===== 左侧:Evil密室 ===== -->
<g id="evil-chamber">
<!-- 房间框架 -->
<rect x="30" y="20" width="600" height="680" rx="6" fill="#0c0a14" stroke="#1e1a2a" stroke-width="1.5"/>
<rect x="30" y="20" width="600" height="680" rx="6" fill="url(#evil-ambient)"/>
<!-- 哥特拱顶 -->
<path d="M30,20 Q330,-30 630,20" fill="none" stroke="#2a2440" stroke-width="1"/>
<!-- 石墙纹理线 -->
<line x1="30" y1="180" x2="630" y2="180" stroke="#151220" stroke-width="0.5"/>
<line x1="30" y1="400" x2="630" y2="400" stroke="#151220" stroke-width="0.5"/>
<line x1="30" y1="560" x2="630" y2="560" stroke="#151220" stroke-width="0.5"/>
<line x1="200" y1="20" x2="200" y2="400" stroke="#151220" stroke-width="0.3"/>
<line x1="460" y1="20" x2="460" y2="400" stroke="#151220" stroke-width="0.3"/>
<!-- 房间标签 -->
<text x="330" y="60" text-anchor="middle" font-family="Cinzel,serif" font-size="13" fill="#4a4060" letter-spacing="0.15em">EVIL CHAMBER</text>
<!-- ===== 哥特吊灯 ===== -->
<g id="chandelier" transform="translate(330,0)">
<!-- 天花板底座 -->
<ellipse cx="0" cy="82" rx="18" ry="6" fill="#3a3040" stroke="#5a4a5a" stroke-width="1"/>
<!-- 链条 -->
<g stroke="#5a4a5a" stroke-width="1.5" fill="none">
<line x1="0" y1="20" x2="0" y2="68"/>
<ellipse cx="0" cy="72" rx="4" ry="5"/>
<ellipse cx="0" cy="82" rx="3" ry="4"/>
</g>
<!-- 顶部冠饰 -->
<path d="M-14,88 Q-7,84 0,88 Q7,84 14,88" fill="#3a3040" stroke="#6a5a6a" stroke-width="0.8"/>
<!-- 中心柱 -->
<rect x="-6" y="88" width="12" height="18" rx="3" fill="#3a3040" stroke="#5a4a5a" stroke-width="0.8"/>
<!-- 装饰环 -->
<ellipse cx="0" cy="94" rx="10" ry="3" fill="none" stroke="#6a5a6a" stroke-width="0.6"/>
<!-- 5条臂 -->
<g stroke="#5a4a5a" fill="none" stroke-width="2.5" stroke-linecap="round">
<path d="M0,98 C-50,100 -75,108 -95,122"/>
<path d="M0,98 C-30,102 -48,112 -58,128"/>
<path d="M0,98 L0,135"/>
<path d="M0,98 C30,102 48,112 58,128"/>
<path d="M0,98 C50,100 75,108 95,122"/>
</g>
<!-- 臂末端卷曲装饰 -->
<g fill="#5a4a5a">
<circle cx="-95" cy="122" r="3"/><circle cx="-58" cy="128" r="3"/>
<circle cx="0" cy="135" r="3"/>
<circle cx="58" cy="128" r="3"/><circle cx="95" cy="122" r="3"/>
</g>
<!-- 蜡烛座 + 灯光 -->
<g id="candle-lights">
<!-- 每根蜡烛:底座矩形 + 火焰圆 + 光晕圆 -->
<g class="candle-unit" data-idx="0" transform="translate(-95,122)">
<rect x="-3" y="-6" width="6" height="8" rx="1" fill="#4a3a3a"/>
<circle class="flame-core" cx="0" cy="-10" r="5" fill="#c9a84c" filter="url(#glow-sm)"/>
<circle class="flame-glow candle-glow" cx="0" cy="-10" r="22" fill="#c9a84c" opacity="0.12" filter="url(#glow-md)"/>
</g>
<g class="candle-unit" data-idx="1" transform="translate(-58,128)">
<rect x="-3" y="-6" width="6" height="8" rx="1" fill="#4a3a3a"/>
<circle class="flame-core" cx="0" cy="-10" r="5" fill="#c9a84c" filter="url(#glow-sm)"/>
<circle class="flame-glow candle-glow" cx="0" cy="-10" r="22" fill="#c9a84c" opacity="0.12" filter="url(#glow-md)"/>
</g>
<g class="candle-unit" data-idx="2" transform="translate(0,135)">
<rect x="-3" y="-6" width="6" height="8" rx="1" fill="#4a3a3a"/>
<circle class="flame-core" cx="0" cy="-10" r="5" fill="#c9a84c" filter="url(#glow-sm)"/>
<circle class="flame-glow candle-glow" cx="0" cy="-10" r="22" fill="#c9a84c" opacity="0.12" filter="url(#glow-md)"/>
</g>
<g class="candle-unit" data-idx="3" transform="translate(58,128)">
<rect x="-3" y="-6" width="6" height="8" rx="1" fill="#4a3a3a"/>
<circle class="flame-core" cx="0" cy="-10" r="5" fill="#c9a84c" filter="url(#glow-sm)"/>
<circle class="flame-glow candle-glow" cx="0" cy="-10" r="22" fill="#c9a84c" opacity="0.12" filter="url(#glow-md)"/>
</g>
<g class="candle-unit" data-idx="4" transform="translate(95,122)">
<rect x="-3" y="-6" width="6" height="8" rx="1" fill="#4a3a3a"/>
<circle class="flame-core" cx="0" cy="-10" r="5" fill="#c9a84c" filter="url(#glow-sm)"/>
<circle class="flame-glow candle-glow" cx="0" cy="-10" r="22" fill="#c9a84c" opacity="0.12" filter="url(#glow-md)"/>
</g>
</g>
<!-- 水晶垂饰 -->
<g fill="#7a7aaa" opacity="0.4">
<path d="M-95,130 l-2,10 2,3 2,-3z"/>
<path d="M-58,136 l-1.5,8 1.5,2.5 1.5,-2.5z"/>
<path d="M0,143 l-2,12 2,3 2,-3z"/>
<path d="M58,136 l-1.5,8 1.5,2.5 1.5,-2.5z"/>
<path d="M95,130 l-2,10 2,3 2,-3z"/>
</g>
<!-- 底部装饰坠 -->
<path d="M0,155 l-3,14 3,4 3,-4z" fill="#8a7aaa" opacity="0.35"/>
</g>
<!-- 环境光晕(吊灯周围) -->
<circle cx="330" cy="130" r="80" fill="#c9a84c" opacity="0.04" filter="url(#glow-lg)" class="ambient-pulse" id="evil-ambient-glow"/>
<!-- Evil人影(剪影) -->
<g id="evil-figure" transform="translate(310,530)">
<!-- 身体 -->
<ellipse cx="0" cy="0" rx="18" ry="8" fill="#120e1a"/>
<rect x="-14" y="-40" width="28" height="42" rx="8" fill="#120e1a"/>
<!-- 头 -->
<circle cx="0" cy="-52" r="11" fill="#120e1a"/>
<!-- 双马尾 -->
<path d="M-8,-56 C-20,-50 -28,-35 -26,-15" stroke="#120e1a" fill="none" stroke-width="5" stroke-linecap="round"/>
<path d="M8,-56 C20,-50 28,-35 26,-15" stroke="#120e1a" fill="none" stroke-width="5" stroke-linecap="round"/>
<!-- 交叉手臂(傲娇姿势) -->
<path d="M-14,-25 L-6,-18 L6,-25" stroke="#120e1a" fill="none" stroke-width="4" stroke-linecap="round"/>
<path d="M14,-25 L6,-18 L-6,-25" stroke="#120e1a" fill="none" stroke-width="4" stroke-linecap="round"/>
</g>
<!-- 语音波纹起点 -->
<circle id="voice-origin" cx="310" cy="490" r="3" fill="none" opacity="0"/>
<!-- 语音气泡容器 -->
<g id="speech-bubble" opacity="0">
<rect x="200" y="430" width="220" height="38" rx="8" fill="#1a1528" stroke="#3a3050" stroke-width="1"/>
<polygon points="295,468 310,485 325,468" fill="#1a1528" stroke="#3a3050" stroke-width="1"/>
<polygon points="297,468 310,482 323,468" fill="#1a1528"/>
<text id="speech-text" x="310" y="454" text-anchor="middle" font-family="'Noto Sans SC',sans-serif" font-size="12" fill="#c9a84c"></text>
</g>
</g>
<!-- ===== 中间连接区域 ===== -->
<g id="connection-zone">
<!-- 分隔墙 -->
<rect x="640" y="20" width="120" height="680" rx="0" fill="#0a0a12" stroke="#1a1a28" stroke-width="0.5"/>
<text x="700" y="48" text-anchor="middle" font-family="Cinzel,serif" font-size="10" fill="#2a2840" letter-spacing="0.2em">WIFI BIND</text>
<!-- 连接路径(底层虚线) -->
<use href="#wifi-path" stroke="#1a2040" stroke-width="1.5" stroke-dasharray="6 8" class="dash-anim" fill="none"/>
<!-- WiFi信号图标 -->
<g transform="translate(700,180)" id="wifi-icon">
<circle cx="0" cy="10" r="4" fill="#2a3a5a"/>
<path d="M-10,4 A14,14 0 0,1 10,4" fill="none" stroke="#2a3a5a" stroke-width="2" stroke-linecap="round"/>
<path d="M-20,-2 A24,24 0 0,1 20,-2" fill="none" stroke="#2a3a5a" stroke-width="2" stroke-linecap="round"/>
<path d="M-30,-8 A34,34 0 0,1 30,-8" fill="none" stroke="#1a2a4a" stroke-width="1.5" stroke-linecap="round"/>
</g>
<!-- 传输脉冲容器 -->
<g id="pulse-layer"></g>
<!-- 箭头指示 -->
<polygon points="890,375 900,385 890,395" fill="#1a2a4a" opacity="0.5"/>
</g>
<!-- ===== 右侧:Neuro密室 ===== -->
<g id="neuro-chamber">
<!-- 房间框架 -->
<rect x="770" y="20" width="600" height="680" rx="6" fill="#0a0c16" stroke="#1a1e30" stroke-width="1.5"/>
<rect x="770" y="20" width="600" height="680" rx="6" fill="url(#neuro-ambient)"/>
<!-- 拱顶 -->
<path d="M770,20 Q1070,-20 1370,20" fill="none" stroke="#1a2040" stroke-width="1"/>
<!-- 墙纹 -->
<line x1="770" y1="200" x2="1370" y2="200" stroke="#0e1020" stroke-width="0.5"/>
<line x1="770" y1="420" x2="1370" y2="420" stroke="#0e1020" stroke-width="0.5"/>
<!-- 房间标签 -->
<text x="1070" y="55" text-anchor="middle" font-family="Cinzel,serif" font-size="13" fill="#3a4060" letter-spacing="0.15em">NEURO CHAMBER</text>
<!-- ===== 法阵 ===== -->
<g id="magic-circle" transform="translate(1070,430)">
<!-- 地面光晕 -->
<circle r="180" fill="#0a1020" opacity="0.3" id="ground-glow"/>
<!-- 外环 -->
<circle r="155" fill="none" stroke="#1a2a4a" stroke-width="2.5"/>
<circle r="152" fill="none" stroke="#1a2a4a" stroke-width="0.5" stroke-dasharray="2 6"/>
<!-- 外环刻度 -->
<g stroke="#2a3a5a" stroke-width="1" opacity="0.5">
<line x1="0" y1="-155" x2="0" y2="-148"/>
<line x1="77.5" y1="-134" x2="74" y2="-128"/>
<line x1="134" y1="-77.5" x2="128" y2="-74"/>
<line x1="155" y1="0" x2="148" y2="0"/>
<line x1="134" y1="77.5" x2="128" y2="74"/>
<line x1="77.5" y1="134" x2="74" y2="128"/>
<line x1="0" y1="155" x2="0" y2="148"/>
<line x1="-77.5" y1="134" x2="-74" y2="128"/>
<line x1="-134" y1="77.5" x2="-128" y2="74"/>
<line x1="-155" y1="0" x2="-148" y2="0"/>
<line x1="-134" y1="-77.5" x2="-128" y2="-74"/>
<line x1="-77.5" y1="-134" x2="-74" y2="-128"/>
</g>
<!-- 符文环(旋转) -->
<g class="rune-ring" transform="translate(0,0)">
<circle r="125" fill="none" stroke="#1e2e50" stroke-width="1.2"/>
<text font-size="11" fill="#3a5a8a" opacity="0.55" letter-spacing="6">
<textPath href="#rune-circle" startOffset="0%">ᚠ ᚢ ᚦ ᚨ ᚱ ᚲ ᚷ ᚹ ᚺ ᚾ ᛁ ᛃ ᛈ ᛇ ᛉ ᛊ ᛏ ᛒ ᛖ ᛗ ᛚ ᛞ ᛟ</textPath>
</text>
</g>
<!-- 几何环(旋转) -->
<g class="geo-ring" transform="translate(0,0)">
<circle r="95" fill="none" stroke="#1e2e50" stroke-width="1"/>
<!-- 六芒星 -->
<polygon points="0,-88 76.2,44 -76.2,44" fill="none" stroke="#2a4a6a" stroke-width="1" opacity="0.5"/>
<polygon points="0,88 76.2,-44 -76.2,-44" fill="none" stroke="#2a4a6a" stroke-width="1" opacity="0.5"/>
</g>
<!-- 内装饰环(旋转) -->
<g class="inner-deco" transform="translate(0,0)">
<circle r="58" fill="none" stroke="#1e2e50" stroke-width="1"/>
<!-- 小菱形装饰 -->
<g fill="#2a4a6a" opacity="0.4">
<rect x="-4" y="-62" width="8" height="8" rx="1" transform="rotate(45,0,-58)"/>
<rect x="-4" y="-62" width="8" height="8" rx="1" transform="rotate(45,0,-58) rotate(60,0,0)"/>
<rect x="-4" y="-62" width="8" height="8" rx="1" transform="rotate(45,0,-58) rotate(120,0,0)"/>
<rect x="-4" y="-62" width="8" height="8" rx="1" transform="rotate(45,0,-58) rotate(180,0,0)"/>
<rect x="-4" y="-62" width="8" height="8" rx="1" transform="rotate(45,0,-58) rotate(240,0,0)"/>
<rect x="-4" y="-62" width="8" height="8" rx="1" transform="rotate(45,0,-58) rotate(300,0,0)"/>
</g>
</g>
<!-- 内环 -->
<circle r="35" fill="none" stroke="#2a3a5a" stroke-width="1.5"/>
<!-- 核心 -->
<circle id="circle-core" r="18" fill="url(#core-grad)"/>
<circle id="core-inner" r="8" fill="#1a2a4a" opacity="0.5"/>
<!-- 核心光晕 -->
<circle id="core-glow" r="50" fill="#1a2a4a" opacity="0.06" filter="url(#glow-lg)" class="ambient-pulse"/>
</g>
<!-- 环境光晕(法阵周围) -->
<circle cx="1070" cy="430" r="120" fill="#1a2a4a" opacity="0.03" filter="url(#glow-xl)" class="ambient-pulse" id="neuro-ambient-glow"/>
<!-- Neuro人影 -->
<g id="neuro-figure" transform="translate(1070,570)">
<ellipse cx="0" cy="0" rx="16" ry="7" fill="#0e1020"/>
<rect x="-12" y="-38" width="24" height="40" rx="7" fill="#0e1020"/>
<circle cx="0" cy="-50" r="10" fill="#0e1020"/>
<!-- 头发装饰 -->
<path d="M-10,-55 C-6,-62 6,-62 10,-55" stroke="#0e1020" fill="none" stroke-width="3" stroke-linecap="round"/>
<!-- 一只手托下巴 -->
<path d="M12,-35 L18,-42 L16,-50" stroke="#0e1020" fill="none" stroke-width="3.5" stroke-linecap="round"/>
</g>
<!-- 注意力指示器(激活时出现) -->
<g id="attention-indicator" opacity="0" transform="translate(1070,360)">
<line x1="0" y1="0" x2="0" y2="-30" stroke="#c9a84c" stroke-width="1.5" stroke-dasharray="3 3" opacity="0.6"/>
<polygon points="-5,-30 0,-38 5,-30" fill="#c9a84c" opacity="0.6"/>
<text x="12" y="-25" font-family="'Noto Sans SC',sans-serif" font-size="10" fill="#c9a84c" opacity="0.7">!</text>
</g>
</g>
<!-- ===== 闪光覆盖层 ===== -->
<rect id="evil-flash" x="30" y="20" width="600" height="680" rx="6" fill="#c9a84c" opacity="0" pointer-events="none"/>
<rect id="neuro-flash" x="770" y="20" width="600" height="680" rx="6" fill="#1a2a4a" opacity="0" pointer-events="none"/>
<!-- ===== 共鸣标注 ===== -->
<g id="resonance-label" opacity="0" transform="translate(700,660)">
<rect x="-120" y="-18" width="240" height="36" rx="6" fill="#12121a" stroke="#c9a84c" stroke-width="1" opacity="0.9"/>
<text x="0" y="4" text-anchor="middle" font-family="'Noto Sans SC',sans-serif" font-size="13" fill="#c9a84c" font-weight="700" letter-spacing="0.08em">信号即确认 — 无需言语回应</text>
</g>
<!-- ===== IFR关键标注(激活时显示) ===== -->
<g id="ifr-annotation" opacity="0" transform="translate(700,120)">
<rect x="-140" y="-14" width="280" height="28" rx="5" fill="#0e0e18" stroke="rgba(201,168,76,0.4)" stroke-width="1"/>
<text x="0" y="4" text-anchor="middle" font-family="'Noto Sans SC',sans-serif" font-size="11" fill="rgba(201,168,76,0.8)" letter-spacing="0.06em">灯光 = 信号发送 + 通信确认(双重功能合一)</text>
</g>
<!-- ===== 动态元素层 ===== -->
<g id="dynamic-layer"></g>
</svg>
</div>
<!-- 控制面板 -->
<div class="control-panel">
<button class="symbol-btn moon" data-symbol="moon" data-color="#ff2d55" aria-label="月亮符号-红色">
<span class="symbol-icon">☽</span>
<span class="symbol-name">月亮</span>
<span class="symbol-color-tag">RED</span>
</button>
<button class="symbol-btn sun" data-symbol="sun" data-color="#00b4ff" aria-label="太阳符号-蓝色">
<span class="symbol-icon">☀</span>
<span class="symbol-name">太阳</span>
<span class="symbol-color-tag">BLUE</span>
</button>
<button class="symbol-btn star" data-symbol="star" data-color="#00ff88" aria-label="星星符号-绿色">
<span class="symbol-icon">✦</span>
<span class="symbol-name">星星</span>
<span class="symbol-color-tag">GREEN</span>
</button>
<button class="reset-btn" id="reset-btn" aria-label="重置动画">重置</button>
<div class="status-bar">
<p class="status-text" id="status-text">等待信号输入 ...</p>
</div>
</div>
<!-- IFR说明 -->
<div class="info-panel">
<h3>IDEAL FINAL RESULT</h3>
<p><span class="hl">核心矛盾:</span>Evil需要确认信息已被接收,但羞于等待言语回应;Neuro需要强刺激才能注意力集中。</p>
<p><span class="hl">理想解:</span>利用密室中<span class="hl">已有的灯光资源</span>,将"通信确认"工程动作替换为"魔法共鸣"场效应。灯光变色同时承担<span class="hl">信号发送</span>与<span class="hl">确认收到</span>双重功能——Evil看到灯亮即知对方收到(保护自尊),Neuro被强视觉刺激吸引(不走神)。</p>
<div class="ifr-core">
<span class="hl">IFR终极理想:</span>系统自行确认通信,无需额外的人为回应环节。灯光变化本身就是确认——矛盾在资源复用中消融,系统复杂度极低(仅WiFi绑定+语音关键词提取)。
</div>
</div>
</div>
<script>
(function(){
/* ===== 常量与配置 ===== */
const SVG_NS = 'http://www.w3.org/2000/svg';
const svg = document.getElementById('main-svg');
const dynamicLayer = document.getElementById('dynamic-layer');
const statusText = document.getElementById('status-text');
/* 符号配置 */
const SYMBOLS = {
moon: { color: '#ff2d55', name: '月亮', keyword: '我看到月亮符号了' },
sun: { color: '#00b4ff', name: '太阳', keyword: '我看到太阳符号了' },
star: { color: '#00ff88', name: '星星', keyword: '我看到星星符号了' }
};
let isAnimating = false;
let currentState = 'idle';
let activeSymbol = null;
/* ===== DOM引用 ===== */
const candleUnits = document.querySelectorAll('.candle-unit');
const flameCores = document.querySelectorAll('.flame-core');
const flameGlows = document.querySelectorAll('.flame-glow');
const circleCore = document.getElementById('circle-core');
const coreInner = document.getElementById('core-inner');
const coreGlow = document.getElementById('core-glow');
const evilFlash = document.getElementById('evil-flash');
const neuroFlash = document.getElementById('neuro-flash');
const evilAmbientGlow = document.getElementById('evil-ambient-glow');
const neuroAmbientGlow = document.getElementById('neuro-ambient-glow');
const speechBubble = document.getElementById('speech-bubble');
const speechTextEl = document.getElementById('speech-text');
const attentionIndicator = document.getElementById('attention-indicator');
const resonanceLabel = document.getElementById('resonance-label');
const ifrAnnotation = document.getElementById('ifr-annotation');
const wifiIcon = document.getElementById('wifi-icon');
const wifiPath = document.getElementById('wifi-path');
/* ===== 工具函数 ===== */
function delay(ms) { return new Promise(r => setTimeout(r, ms)); }
function setStatus(text, active = false) {
statusText.textContent = text;
statusText.classList.toggle('active', active);
}
function createSVGElement(tag, attrs) {
const el = document.createElementNS(SVG_NS, tag);
for (const [k, v] of Object.entries(attrs)) {
el.setAttribute(k, v);
}
return el;
}
function lerp(a, b, t) { return a + (b - a) * t; }
/* ===== 动画:语音波纹 ===== */
async function animateVoiceWaves(cx, cy, color) {
const waveCount = 4;
const promises = [];
for (let i = 0; i < waveCount; i++) {
promises.push(new Promise(resolve => {
setTimeout(() => {
const wave = createSVGElement('circle', {
cx, cy, r: 12,
fill: 'none', stroke: color || '#c9a84c',
'stroke-width': 2, opacity: 0.8,
filter: 'url(#glow-sm)'
});
dynamicLayer.appendChild(wave);
const start = performance.now();
const duration = 900;
function step(now) {
const p = Math.min((now - start) / duration, 1);
const r = 12 + p * 55;
const opacity = 0.8 * (1 - p);
const sw = 2 * (1 - p * 0.6);
wave.setAttribute('r', r);
wave.setAttribute('opacity', opacity);
wave.setAttribute('stroke-width', sw);
if (p < 1) requestAnimationFrame(step);
else { wave.remove(); resolve(); }
}
requestAnimationFrame(step);
}, i * 180);
}));
}
await Promise.all(promises);
}
/* ===== 动画:传输脉冲 ===== */
async function animateTransmissionPulse(color) {
const pathLength = wifiPath.getTotalLength();
const pulseCount = 3;
const promises = [];
for (let i = 0; i < pulseCount; i++) {
promises.push(new Promise(resolve => {
setTimeout(() => {
/* 主脉冲 */
const pulse = createSVGElement('circle', {
r: 6, fill: color, opacity: 0.95,
filter: 'url(#glow-md)'
});
/* 拖尾 */
const trail = createSVGElement('circle', {
r: 10, fill: color, opacity: 0.3,
filter: 'url(#glow-lg)'
});
dynamicLayer.appendChild(trail);
dynamicLayer.appendChild(pulse);
const start = performance.now();
const duration = 700;
function step(now) {
const p = Math.min((now - start) / duration, 1);
const point = wifiPath.getPointAtLength(p * pathLength);
const trailPoint = wifiPath.getPointAtLength(Math.max(0, p - 0.06) * pathLength);
pulse.setAttribute('cx', point.x);
pulse.setAttribute('cy', point.y);
trail.setAttribute('cx', trailPoint.x);
trail.setAttribute('cy', trailPoint.y);
/* 淡出 */
if (p > 0.7) {
const fadeP = (p - 0.7) / 0.3;
pulse.setAttribute('opacity', 0.95 * (1 - fadeP));
trail.setAttribute('opacity', 0.3 * (1 - fadeP));
}
if (p < 1) requestAnimationFrame(step);
else { pulse.remove(); trail.remove(); resolve(); }
}
requestAnimationFrame(step);
}, i * 200);
}));
}
await Promise.all(promises);
}
/* ===== 动画:粒子爆发 ===== */
async function animateParticleBurst(cx, cy, color, count = 16) {
const promises = [];
for (let i = 0; i < count; i++) {
promises.push(new Promise(resolve => {
const angle = (Math.PI * 2 / count) * i + Math.random() * 0.3;
const speed = 40 + Math.random() * 50;
const dx = Math.cos(angle) * speed;
const dy = Math.sin(angle) * speed;
const size = 2 + Math.random() * 3;
const p = createSVGElement('circle', {
cx, cy, r: size,
fill: color, opacity: 0.9,
filter: 'url(#glow-sm)'
});
dynamicLayer.appendChild(p);
const start = performance.now();
const duration = 600 + Math.random() * 400;
function step(now) {
const t = Math.min((now - start) / duration, 1);
const ease = 1 - Math.pow(1 - t, 3); // easeOutCubic
p.setAttribute('cx', cx + dx * ease);
p.setAttribute('cy', cy + dy * ease);
p.setAttribute('opacity', 0.9 * (1 - t));
p.setAttribute('r', size * (1 - t * 0.5));
if (t < 1) requestAnimationFrame(step);
else { p.remove(); resolve(); }
}
requestAnimationFrame(step);
}));
}
await Promise.all(promises);
}
/* ===== 动画:灯光颜色过渡 ===== */
function setChandelierColor(color, duration = 500) {
candleUnits.forEach((unit, i) => {
const core = unit.querySelector('.flame-core');
const glow = unit.querySelector('.flame-glow');
setTimeout(() => {
core.style.transition = `fill ${duration}ms ease`;
glow.style.transition = `fill ${duration}ms ease, opacity ${duration}ms ease`;
core.setAttribute('fill', color);
glow.setAttribute('fill', color);
glow.setAttribute('opacity', '0.25');
}, i * 60);
});
}
function setMagicCircleColor(color, duration = 500) {
circleCore.style.transition = `fill ${duration}ms ease`;
coreInner.style.transition = `fill ${duration}ms ease`;
coreGlow.style.transition = `fill ${duration}ms ease, opacity ${duration}ms ease`;
/* 更新渐变 */
const grad = document.getElementById('core-grad');
const stops = grad.querySelectorAll('stop');
stops[0].style.transition = `stop-color ${duration}ms ease`;
stops[1].style.transition = `stop-color ${duration}ms ease`;
stops[0].setAttribute('stop-color', color);
stops[0].setAttribute('stop-opacity', '0.8');
stops[1].setAttribute('stop-color', color);
stops[1].setAttribute('stop-opacity', '0.3');
coreInner.setAttribute('fill', color);
coreInner.setAttribute('opacity', '0.7');
coreGlow.setAttribute('fill', color);
coreGlow.setAttribute('opacity', '0.15');
}
/* ===== 动画:闪光效果 ===== */
async function flashEffect(element, color, maxOpacity = 0.25) {
element.setAttribute('fill', color);
element.setAttribute('opacity', maxOpacity);
const start = performance.now();
const duration = 600;
function step(now) {
const p = Math.min((now - start) / duration, 1);
const opacity = maxOpacity * (1 - p);
element.setAttribute('opacity', opacity);
if (p < 1) requestAnimationFrame(step);
}
requestAnimationFrame(step);
await delay(duration);
}
/* ===== 动画:环境光变色 ===== */
function setAmbientGlow(element, color, opacity = 0.08) {
element.style.transition = 'fill 600ms ease, opacity 600ms ease';
element.setAttribute('fill', color);
element.setAttribute('opacity', opacity);
}
/* ===== 动画:WiFi图标激活 ===== */
async function animateWiFiIcon(color) {
const paths = wifiIcon.querySelectorAll('path');
const circle = wifiIcon.querySelector('circle');
paths.forEach((p, i) => {
p.style.transition = `stroke ${300}ms ease`;
p.setAttribute('stroke', color);
});
circle.style.transition = `fill ${300}ms ease`;
circle.setAttribute('fill', color);
}
/* ===== 重置所有视觉状态 ===== */
function resetVisuals() {
/* 吊灯颜色 */
const defaultFlame = '#c9a84c';
flameCores.forEach(c => {
c.style.transition = 'fill 400ms ease';
c.setAttribute('fill', defaultFlame);
});
flameGlows.forEach(g => {
g.style.transition = 'fill 400ms ease, opacity 400ms ease';
g.setAttribute('fill', defaultFlame);
g.setAttribute('opacity', '0.12');
});
/* 法阵颜色 */
const grad = document.getElementById('core-grad');
const stops = grad.querySelectorAll('stop');
stops[0].setAttribute('stop-color', '#2a3a6a');
stops[0].setAttribute('stop-opacity', '0.6');
stops[1].setAttribute('stop-color', '#1a2a4a');
stops[1].setAttribute('stop-opacity', '0.2');
coreInner.setAttribute('fill', '#1a2a4a');
coreInner.setAttribute('opacity', '0.5');
coreGlow.setAttribute('fill', '#1a2a4a');
coreGlow.setAttribute('opacity', '0.06');
/* 闪光 */
evilFlash.setAttribute('opacity', '0');
neuroFlash.setAttribute('opacity', '0');
/* 环境光 */
evilAmbientGlow.setAttribute('fill', '#c9a84c');
evilAmbientGlow.setAttribute('opacity', '0.04');
neuroAmbientGlow.setAttribute('fill', '#1a2a4a');
neuroAmbientGlow.setAttribute('opacity', '0.03');
/* 语音气泡 */
speechBubble.setAttribute('opacity', '0');
/* 注意力指示 */
attentionIndicator.setAttribute('opacity', '0');
/* 共鸣标注 */
resonanceLabel.setAttribute('opacity', '0');
/* IFR标注 */
ifrAnnotation.setAttribute('opacity', '0');
/* WiFi图标 */
const wifiPaths = wifiIcon.querySelectorAll('path');
const wifiCircle = wifiIcon.querySelector('circle');
wifiPaths[0].setAttribute('stroke', '#2a3a5a');
wifiPaths[1].setAttribute('stroke', '#2a3a5a');
wifiPaths[2].setAttribute('stroke', '#1a2a4a');
wifiCircle.setAttribute('fill', '#2a3a5a');
/* 清理动态层 */
while (dynamicLayer.firstChild) dynamicLayer.firstChild.remove();
/* 按钮状态 */
document.querySelectorAll('.symbol-btn').forEach(b => b.classList.remove('active'));
activeSymbol = null;
currentState = 'idle';
}
/* ===== 主动画序列 ===== */
async function runSequence(symbolKey) {
if (isAnimating) return;
isAnimating = true;
const config = SYMBOLS[symbolKey];
const color = config.color;
/* 重置到干净状态 */
resetVisuals();
await delay(150);
activeSymbol = symbolKey;
document.querySelector(`.symbol-btn.${symbolKey}`).classList.add('active');
/* === 阶段1:Evil发声 === */
currentState = 'speaking';
setStatus(`Evil: "${config.keyword}"`, true);
/* 显示语音气泡 */
speechTextEl.textContent = config.keyword;
speechBubble.setAttribute('opacity', '1');
/* 语音波纹 */
await animateVoiceWaves(310, 490, '#c9a84c');
await delay(200);
/* === 阶段2:语音识别处理 === */
currentState = 'processing';
setStatus('语音识别: 提取关键词 → ' + config.name, true);
/* 识别闪烁 - 吊灯微闪 */
evilFlash.setAttribute('fill', color);
evilFlash.setAttribute('opacity', '0.08');
await delay(200);
evilFlash.setAttribute('opacity', '0');
await delay(300);
/* === 阶段3:吊灯变色 === */
currentState = 'chandelier';
setStatus('Evil密室吊灯 → ' + config.name + '色', true);
setChandelierColor(color, 600);
setAmbientGlow(evilAmbientGlow, color, 0.1);
/* 吊灯闪光 */
await flashEffect(evilFlash, color, 0.15);
await delay(300);
/* === 阶段4:WiFi传输 === */
currentState = 'transmitting';
setStatus('WiFi信号同步传输中 ...', true);
animateWiFiIcon(color);
await animateTransmissionPulse(color);
await delay(200);
/* === 阶段5:法阵激活 === */
currentState = 'circle';
setStatus('Neuro密室法阵共鸣激活!', true);
setMagicCircleColor(color, 500);
setAmbientGlow(neuroAmbientGlow, color, 0.08);
/* 法阵闪光 + 粒子 */
flashEffect(neuroFlash, color, 0.2);
animateParticleBurst(1070, 430, color, 20);
await delay(400);
/* Neuro注意力指示 */
attentionIndicator.setAttribute('opacity', '1');
await delay(500);
/* === 阶段6:共鸣确认 === */
currentState = 'resonance';
setStatus('魔法共鸣 — 信号即确认,通信链路完整', true);
/* 共鸣标注浮现 */
resonanceLabel.setAttribute('opacity', '1');
await delay(300);
/* IFR核心标注 */
ifrAnnotation.setAttribute('opacity', '1');
/* 最终同步闪光 */
await flashEffect(evilFlash, color, 0.12);
await flashEffect(neuroFlash, color, 0.12);
/* 隐藏语音气泡 */
speechBubble.setAttribute('opacity', '0');
currentState = 'confirmed';
isAnimating = false;
}
/* ===== 事件绑定 ===== */
document.querySelectorAll('.symbol-btn').forEach(btn => {
btn.addEventListener('click', () => {
const symbol = btn.dataset.symbol;
if (symbol) runSequence(symbol);
});
});
document.getElementById('reset-btn').addEventListener('click', () => {
if (isAnimating) return;
resetVisuals();
setStatus('等待信号输入 ...');
});
/* ===== 键盘快捷键 ===== */
document.addEventListener('keydown', (e) => {
if (e.key === '1') runSequence('moon');
else if (e.key === '2') runSequence('sun');
else if (e.key === '3') runSequence('star');
else if (e.key === 'r' || e.key === 'R') {
if (!isAnimating) { resetVisuals(); setStatus('等待信号输入 ...'); }
}
});
/* ===== 空闲粒子效果 ===== */
function spawnAmbientParticle() {
if (currentState !== 'idle') return;
const isLeft = Math.random() > 0.5;
const x = isLeft ? 80 + Math.random() * 500 : 820 + Math.random() * 500;
const y = 100 + Math.random() * 500;
const color = isLeft ? '#c9a84c' : '#3a5a8a';
const p = createSVGElement('circle', {
cx: x, cy: y, r: 1 + Math.random() * 1.5,
fill: color, opacity: 0
});
dynamicLayer.appendChild(p);
const start = performance.now();
const duration = 2000 + Math.random() * 3000;
const dy = -(20 + Math.random() * 30);
const dx = (Math.random() - 0.5) * 20;
function step(now) {
const t = Math.min((now - start) / duration, 1);
const ease = t;
p.setAttribute('cx', x + dx * ease);
p.setAttribute('cy', y + dy * ease);
/* 淡入淡出 */
const opacity = t < 0.3 ? t / 0.3 : t > 0.7 ? (1 - t) / 0.3 : 1;
p.setAttribute('opacity', opacity * 0.35);
if (t < 1) requestAnimationFrame(step);
else p.remove();
}
requestAnimationFrame(step);
}
/* 定期生成环境粒子 */
setInterval(spawnAmbientParticle, 400);
/* 初始状态 */
setStatus('等待信号输入 ... 点击符号按钮或按 1/2/3 触发');
})();
</script>
</body>
</html>
实现说明
视觉设计方向:深邃的暗黑哥特+神秘学美学,两个密室拥有截然不同的氛围——Evil侧是冷紫调的哥特石室配金色烛光吊灯,Neuro侧是深蓝调的星辰法阵室。中间以WiFi绑定区域连接,形成完整的信息通道。
核心动画序列(点击月亮/太阳/星星触发):
- Evil发声 — 语音气泡出现 + 扩散波纹从人影发出
- 语音识别 — 吊灯微闪,关键词被提取
- 吊灯变色 — 5支蜡烛依次过渡为目标色(红/蓝/绿),配合闪光效果
- WiFi传输 — 3个光脉冲沿弧线路径从左室飞向右室,带拖尾
- 法阵激活 — 核心渐变变色 + 粒子爆发 + 房间闪光 + Neuro注意力指示器
- 共鸣确认 — 双室同步微闪,浮现"信号即确认—无需言语回应"标注和IFR双重功能标注
IFR原理体现:
- 灯光变色同时承担信号发送与通信确认双重功能,无需额外言语回应环节
- 利用了两个密室中已有的灯光资源(吊灯+法阵),仅增加WiFi绑定和语音提取,系统复杂度极低
- 动画最终状态明确标注"灯光 = 信号发送 + 通信确认(双重功能合一)"
交互方式:点击三个符号按钮或按键盘 1/2/3 触发不同颜色的共鸣序列,按 R 或点击重置回归待机状态。空闲时有漂浮的环境粒子营造氛围。
积分规则:第一轮对话扣减6分,后续每轮扣4分
等待动画代码生成...
