这是一个非常有意思的 TRIZ/游戏设计问题。我将围绕 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 href="https://fonts.googleapis.com/css2?family=Cinzel:wght@700;900&family=Noto+Serif+SC:wght@300;600;900&display=swap" rel="stylesheet">
<script src="https://cdn.tailwindcss.com"></script>
<style>
:root {
--bg-void: #06080e;
--neuro-wall: #0c1624;
--neuro-accent: #1a3a5f;
--evil-wall: #180808;
--evil-accent: #5a1a1a;
--gold: #c9a84c;
--gold-dim: #6a5a2c;
--flame-core: #fff4b0;
--flame-mid: #ff8c2b;
--flame-outer: #cc3300;
--clue-blue: #00d4ff;
--clue-red: #ff2244;
--pipe-metal: #3a3e48;
--heat-orange: #ff7733;
}
* { margin:0; padding:0; box-sizing:border-box; }
body {
background: var(--bg-void);
font-family: 'Noto Serif SC', serif;
color: #e0d8cc;
min-height: 100vh;
display: flex; flex-direction: column; align-items: center;
overflow-x: hidden;
padding: 20px 10px 40px;
}
/* 噪点纹理覆盖 */
body::before {
content: ''; position: fixed; inset: 0; z-index: 9999; pointer-events: none;
background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.03'/%3E%3C/svg%3E");
opacity: 0.5;
}
.page-title {
font-family: 'Cinzel', serif;
font-size: clamp(20px, 3.5vw, 36px);
font-weight: 900;
letter-spacing: 0.12em;
color: var(--gold);
text-align: center;
margin-bottom: 4px;
text-shadow: 0 0 30px rgba(201,168,76,0.3);
}
.page-sub {
font-size: clamp(11px, 1.5vw, 14px);
color: #7a7268;
text-align: center;
margin-bottom: 18px;
letter-spacing: 0.08em;
}
.svg-wrap {
width: 100%; max-width: 1100px;
border: 1px solid rgba(201,168,76,0.15);
border-radius: 12px;
overflow: hidden;
background: #080a10;
box-shadow: 0 0 60px rgba(0,0,0,0.6), inset 0 0 80px rgba(0,0,0,0.4);
}
.svg-wrap svg { display: block; width: 100%; height: auto; }
/* 火焰动画 */
@keyframes flicker1 {
0%,100% { transform: scaleY(1) scaleX(1) translateY(0); opacity:.88; }
20% { transform: scaleY(1.12) scaleX(.94) translateY(-3px); opacity:1; }
40% { transform: scaleY(.92) scaleX(1.06) translateY(1px); opacity:.82; }
60% { transform: scaleY(1.08) scaleX(.97) translateY(-2px); opacity:.95; }
80% { transform: scaleY(.95) scaleX(1.03) translateY(1px); opacity:.85; }
}
@keyframes flicker2 {
0%,100% { transform: scaleY(1) scaleX(1); opacity:.85; }
30% { transform: scaleY(1.15) scaleX(.92); opacity:1; }
60% { transform: scaleY(.88) scaleX(1.08); opacity:.78; }
}
@keyframes flicker3 {
0%,100% { transform: scaleY(1) scaleX(1); opacity:.9; }
25% { transform: scaleY(.9) scaleX(1.1); opacity:1; }
50% { transform: scaleY(1.1) scaleX(.9); opacity:.8; }
75% { transform: scaleY(.95) scaleX(1.05); opacity:.95; }
}
.flame-outer { animation: flicker1 0.8s ease-in-out infinite; transform-origin: bottom center; }
.flame-mid { animation: flicker2 0.6s ease-in-out infinite; transform-origin: bottom center; }
.flame-inner { animation: flicker3 0.4s ease-in-out infinite; transform-origin: bottom center; }
.flame-core { animation: flicker1 0.3s ease-in-out infinite; transform-origin: bottom center; }
/* 壁炉光照脉动 */
@keyframes fireGlowPulse {
0%,100% { opacity: 0.25; }
50% { opacity: 0.45; }
}
#fireGlow { animation: fireGlowPulse 2s ease-in-out infinite; }
/* 热粒子动画 - 沿管道路径 */
@keyframes particleFlow {
0% { offset-distance: 0%; opacity: 0; }
10% { opacity: 0.9; }
90% { opacity: 0.9; }
100% { offset-distance: 100%; opacity: 0; }
}
.heat-particle {
offset-path: path('M920,510 C850,540 700,560 600,555 C500,550 380,530 310,465');
animation: particleFlow 3s linear infinite;
}
.heat-particle:nth-child(2) { animation-delay: -0.4s; }
.heat-particle:nth-child(3) { animation-delay: -0.8s; }
.heat-particle:nth-child(4) { animation-delay: -1.2s; }
.heat-particle:nth-child(5) { animation-delay: -1.6s; }
.heat-particle:nth-child(6) { animation-delay: -2.0s; }
.heat-particle:nth-child(7) { animation-delay: -2.4s; }
.heat-particle:nth-child(8) { animation-delay: -0.2s; r: 3.5; }
.heat-particle:nth-child(9) { animation-delay: -1.0s; r: 2; }
.heat-particle:nth-child(10) { animation-delay: -1.8s; r: 3; }
.heat-particle:nth-child(11) { animation-delay: -0.6s; r: 2.5; }
.heat-particle:nth-child(12) { animation-delay: -1.4s; r: 3.2; }
/* 线索文字显现动画 */
@keyframes clueReveal {
0% { opacity: 0; filter: blur(8px); }
60% { opacity: 0.7; filter: blur(2px); }
100% { opacity: 1; filter: blur(0px); }
}
.clue-revealing { animation: clueReveal 1.5s ease-out forwards; }
/* 烙印烧入动画 */
@keyframes brandBurn {
0% { opacity: 0; filter: blur(6px) brightness(3); }
30% { opacity: 0.6; filter: blur(3px) brightness(2); }
100% { opacity: 1; filter: blur(0px) brightness(1); }
}
.brand-burning { animation: brandBurn 1.8s ease-out forwards; }
/* 温度指示器发光 */
@keyframes heatGlow {
0%,100% { box-shadow: 0 0 8px rgba(255,119,51,0.4); }
50% { box-shadow: 0 0 16px rgba(255,119,51,0.7); }
}
/* 控制面板 */
.controls-panel {
width: 100%; max-width: 1100px;
margin-top: 20px;
display: flex; flex-wrap: wrap; gap: 16px;
align-items: center; justify-content: center;
}
.ctrl-btn {
font-family: 'Noto Serif SC', serif;
font-weight: 600;
font-size: 14px;
padding: 10px 28px;
border: 1px solid rgba(201,168,76,0.4);
border-radius: 6px;
background: linear-gradient(135deg, #1a1510, #0d0a06);
color: var(--gold);
cursor: pointer;
transition: all 0.3s;
letter-spacing: 0.06em;
}
.ctrl-btn:hover:not(:disabled) {
background: linear-gradient(135deg, #2a2015, #1a1008);
border-color: var(--gold);
box-shadow: 0 0 20px rgba(201,168,76,0.2);
}
.ctrl-btn:disabled {
opacity: 0.35; cursor: not-allowed;
}
.ctrl-btn.active {
background: linear-gradient(135deg, #3a2010, #2a1008);
border-color: var(--flame-mid);
color: var(--flame-mid);
box-shadow: 0 0 20px rgba(255,140,43,0.25);
}
.temp-slider-wrap {
display: flex; align-items: center; gap: 10px;
background: rgba(255,255,255,0.03);
padding: 8px 18px; border-radius: 8px;
border: 1px solid rgba(255,255,255,0.06);
}
.temp-slider-wrap label {
font-size: 13px; color: #8a8070; white-space: nowrap;
}
.temp-slider {
-webkit-appearance: none; appearance: none;
width: 180px; height: 6px;
background: linear-gradient(90deg, #1a3050, #ff4400);
border-radius: 3px; outline: none;
}
.temp-slider::-webkit-slider-thumb {
-webkit-appearance: none; appearance: none;
width: 18px; height: 18px;
border-radius: 50%;
background: var(--gold);
border: 2px solid #0a0a0a;
cursor: pointer;
box-shadow: 0 0 10px rgba(201,168,76,0.4);
}
.temp-value {
font-family: 'Cinzel', serif;
font-weight: 700;
font-size: 18px;
min-width: 60px;
text-align: right;
transition: color 0.3s;
}
.temp-value.hot { color: #ff6622; text-shadow: 0 0 12px rgba(255,100,30,0.5); }
.temp-value.cold { color: #4a7aaa; }
.status-bar {
width: 100%; max-width: 1100px;
margin-top: 14px;
text-align: center;
font-size: 13px;
color: #6a6258;
min-height: 22px;
transition: color 0.3s;
}
.status-bar.highlight { color: var(--clue-blue); text-shadow: 0 0 10px rgba(0,212,255,0.3); }
/* IFR说明面板 */
.ifr-panel {
width: 100%; max-width: 1100px;
margin-top: 24px;
padding: 20px 28px;
background: rgba(201,168,76,0.04);
border: 1px solid rgba(201,168,76,0.12);
border-radius: 10px;
}
.ifr-panel h3 {
font-family: 'Cinzel', serif;
font-size: 15px; font-weight: 700;
color: var(--gold); margin-bottom: 10px;
letter-spacing: 0.08em;
}
.ifr-panel p {
font-size: 13px; line-height: 1.8;
color: #8a8070;
}
.ifr-panel .ifr-key {
color: var(--clue-blue);
font-weight: 600;
}
/* 浮尘动画 */
@keyframes dustFloat {
0% { transform: translateY(0) translateX(0); opacity: 0.15; }
50% { transform: translateY(-15px) translateX(5px); opacity: 0.3; }
100% { transform: translateY(0) translateX(0); opacity: 0.15; }
}
/* 响应式 */
@media (max-width: 640px) {
.controls-panel { flex-direction: column; }
.temp-slider { width: 140px; }
}
</style>
</head>
<body>
<div class="page-title">THERMOCHROMIC REVELATION</div>
<div class="page-sub">温感显影 · IFR 理想解原理动画</div>
<div class="svg-wrap">
<svg id="scene" viewBox="0 0 1200 700" xmlns="http://www.w3.org/2000/svg">
<defs>
<!-- 渐变定义 -->
<linearGradient id="neuroWallGrad" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#0e1a2e"/>
<stop offset="100%" stop-color="#0a1220"/>
</linearGradient>
<linearGradient id="evilWallGrad" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#1c0a0a"/>
<stop offset="100%" stop-color="#120606"/>
</linearGradient>
<linearGradient id="deskGrad" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#3a2a18"/>
<stop offset="100%" stop-color="#2a1a0e"/>
</linearGradient>
<linearGradient id="cookieBoxGrad" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#2a2015"/>
<stop offset="100%" stop-color="#1a1008"/>
</linearGradient>
<linearGradient id="stoneGrad" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#4a3a2a"/>
<stop offset="100%" stop-color="#3a2a1a"/>
</linearGradient>
<linearGradient id="pipeGrad" x1="0" y1="0" x2="1" y2="0">
<stop offset="0%" stop-color="#2a2e38"/>
<stop offset="50%" stop-color="#3a3e48"/>
<stop offset="100%" stop-color="#2a2e38"/>
</linearGradient>
<radialGradient id="fireGlowGrad" cx="0.5" cy="0.8" r="0.7">
<stop offset="0%" stop-color="rgba(255,100,20,0.35)"/>
<stop offset="100%" stop-color="rgba(255,100,20,0)"/>
</radialGradient>
<radialGradient id="cookieHeatGlowGrad" cx="0.5" cy="1" r="0.6">
<stop offset="0%" stop-color="rgba(255,119,51,0.4)"/>
<stop offset="100%" stop-color="rgba(255,119,51,0)"/>
</radialGradient>
<linearGradient id="tempGaugeGrad" x1="0" y1="1" x2="0" y2="0">
<stop offset="0%" stop-color="#1a3050"/>
<stop offset="40%" stop-color="#4a7a30"/>
<stop offset="70%" stop-color="#cc8800"/>
<stop offset="100%" stop-color="#ff3300"/>
</linearGradient>
<!-- 滤镜 -->
<filter id="glowBlue" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur stdDeviation="4" result="blur"/>
<feFlood flood-color="#00d4ff" flood-opacity="0.6"/>
<feComposite in2="blur" operator="in"/>
<feMerge><feMergeNode/><feMergeNode in="SourceGraphic"/></feMerge>
</filter>
<filter id="glowRed" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur stdDeviation="4" result="blur"/>
<feFlood flood-color="#ff2244" flood-opacity="0.6"/>
<feComposite in2="blur" operator="in"/>
<feMerge><feMergeNode/><feMergeNode in="SourceGraphic"/></feMerge>
</filter>
<filter id="glowOrange" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur stdDeviation="6" result="blur"/>
<feFlood flood-color="#ff8833" flood-opacity="0.5"/>
<feComposite in2="blur" operator="in"/>
<feMerge><feMergeNode/><feMergeNode in="SourceGraphic"/></feMerge>
</filter>
<filter id="softShadow" x="-20%" y="-20%" width="140%" height="140%">
<feGaussianBlur stdDeviation="5"/>
</filter>
<!-- 管道路径(用于粒子动画) -->
<path id="pipePath" d="M920,510 C860,545 720,565 620,560 C520,555 400,535 310,470" fill="none"/>
<!-- 壁纸图案 -->
<pattern id="wallpaperNeuro" width="40" height="40" patternUnits="userSpaceOnUse">
<rect width="40" height="40" fill="none"/>
<circle cx="20" cy="20" r="1" fill="rgba(26,58,95,0.3)"/>
<path d="M0,20 Q10,15 20,20 Q30,25 40,20" fill="none" stroke="rgba(26,58,95,0.1)" stroke-width="0.5"/>
</pattern>
<pattern id="wallpaperEvil" width="30" height="30" patternUnits="userSpaceOnUse">
<rect width="30" height="30" fill="none"/>
<path d="M0,0 L30,30 M30,0 L0,30" stroke="rgba(90,26,26,0.08)" stroke-width="0.5"/>
</pattern>
</defs>
<!-- ====== 背景 ====== -->
<rect width="1200" height="700" fill="#060810"/>
<!-- ====== Neuro的房间 ====== -->
<g id="neuroRoom">
<!-- 房间墙体 -->
<rect x="30" y="55" width="535" height="490" rx="4" fill="url(#neuroWallGrad)"/>
<rect x="30" y="55" width="535" height="490" rx="4" fill="url(#wallpaperNeuro)"/>
<!-- 地板 -->
<rect x="30" y="460" width="535" height="85" fill="#0a0e18"/>
<line x1="30" y1="460" x2="565" y2="460" stroke="#1a2535" stroke-width="1"/>
<!-- 房间标签 -->
<text x="298" y="45" text-anchor="middle" font-family="Cinzel, serif" font-size="16" font-weight="700" fill="#4a7aaa" letter-spacing="0.15em">NEURO'S ROOM</text>
<!-- 窗户(冷光暗示) -->
<rect x="60" y="80" width="80" height="100" rx="3" fill="#0a1525" stroke="#1a3050" stroke-width="2"/>
<line x1="100" y1="80" x2="100" y2="180" stroke="#1a3050" stroke-width="1"/>
<line x1="60" y1="130" x2="140" y2="130" stroke="#1a3050" stroke-width="1"/>
<rect x="60" y="80" width="80" height="100" rx="3" fill="rgba(30,80,140,0.06)"/>
<!-- 桌子 -->
<rect x="140" y="390" width="300" height="14" rx="2" fill="url(#deskGrad)" stroke="#4a3820" stroke-width="1"/>
<rect x="160" y="404" width="12" height="56" rx="1" fill="#2a1a0e"/>
<rect x="410" y="404" width="12" height="56" rx="1" fill="#2a1a0e"/>
<!-- 桌上小物件 -->
<rect x="380" y="378" width="35" height="12" rx="2" fill="#1a1520" stroke="#2a2535" stroke-width="0.5"/>
<line x1="155" y1="388" x2="180" y2="388" stroke="#4a3820" stroke-width="1.5" stroke-linecap="round"/>
<!-- ===== 曲奇包装盒 ===== -->
<g id="cookieBox" transform="translate(220, 230)">
<!-- 阴影 -->
<ellipse cx="80" cy="162" rx="70" ry="8" fill="rgba(0,0,0,0.3)" filter="url(#softShadow)"/>
<!-- 盒体 -->
<rect x="0" y="0" width="160" height="155" rx="6" fill="url(#cookieBoxGrad)" stroke="#8b6914" stroke-width="2"/>
<!-- 内边框装饰 -->
<rect x="10" y="10" width="140" height="135" rx="3" fill="none" stroke="rgba(201,168,76,0.3)" stroke-width="1"/>
<!-- Art Nouveau装饰纹样 -->
<path d="M25,30 Q50,15 80,30 Q110,45 135,30" fill="none" stroke="rgba(201,168,76,0.4)" stroke-width="1.2"/>
<path d="M25,42 Q50,27 80,42 Q110,57 135,42" fill="none" stroke="rgba(201,168,76,0.25)" stroke-width="0.8"/>
<circle cx="80" cy="36" r="5" fill="none" stroke="rgba(201,168,76,0.5)" stroke-width="1"/>
<circle cx="80" cy="36" r="2" fill="rgba(201,168,76,0.3)"/>
<!-- 侧面装饰 -->
<path d="M20,65 Q35,58 50,65 Q65,72 80,65 Q95,58 110,65 Q125,72 140,65" fill="none" stroke="rgba(201,168,76,0.2)" stroke-width="0.8"/>
<!-- COOKIE文字 -->
<text x="80" y="90" text-anchor="middle" font-family="Cinzel, serif" font-size="18" font-weight="900" fill="rgba(201,168,76,0.6)" letter-spacing="0.2em">COOKIE</text>
<!-- 小曲奇图标 -->
<circle cx="55" cy="112" r="8" fill="rgba(201,168,76,0.15)" stroke="rgba(201,168,76,0.3)" stroke-width="0.8"/>
<circle cx="53" cy="110" r="1.5" fill="rgba(201,168,76,0.2)"/>
<circle cx="58" cy="113" r="1" fill="rgba(201,168,76,0.2)"/>
<circle cx="105" cy="112" r="8" fill="rgba(201,168,76,0.15)" stroke="rgba(201,168,76,0.3)" stroke-width="0.8"/>
<circle cx="103" cy="110" r="1.5" fill="rgba(201,168,76,0.2)"/>
<circle cx="108" cy="113" r="1" fill="rgba(201,168,76,0.2)"/>
<!-- ★ 隐藏线索区域(初始不可见)★ -->
<g id="hiddenClue" opacity="0">
<!-- 线索背景光晕 -->
<rect x="20" y="96" width="120" height="40" rx="4" fill="rgba(0,212,255,0.06)"/>
<!-- 太阳符号 -->
<g transform="translate(42, 116)">
<circle r="6" fill="none" stroke="#00d4ff" stroke-width="1.5"/>
<line x1="0" y1="-10" x2="0" y2="-8" stroke="#00d4ff" stroke-width="1.2" stroke-linecap="round"/>
<line x1="0" y1="8" x2="0" y2="10" stroke="#00d4ff" stroke-width="1.2" stroke-linecap="round"/>
<line x1="-10" y1="0" x2="-8" y2="0" stroke="#00d4ff" stroke-width="1.2" stroke-linecap="round"/>
<line x1="8" y1="0" x2="10" y2="0" stroke="#00d4ff" stroke-width="1.2" stroke-linecap="round"/>
<line x1="-7" y1="-7" x2="-5.5" y2="-5.5" stroke="#00d4ff" stroke-width="1" stroke-linecap="round"/>
<line x1="5.5" y1="-5.5" x2="7" y2="-7" stroke="#00d4ff" stroke-width="1" stroke-linecap="round"/>
<line x1="-7" y1="7" x2="-5.5" y2="5.5" stroke="#00d4ff" stroke-width="1" stroke-linecap="round"/>
<line x1="5.5" y1="5.5" x2="7" y2="7" stroke="#00d4ff" stroke-width="1" stroke-linecap="round"/>
</g>
<text x="62" y="122" font-family="Noto Serif SC, serif" font-size="20" font-weight="900" fill="#00d4ff" filter="url(#glowBlue)">= 蓝</text>
</g>
<!-- 底座加热指示条 -->
<rect id="heatStrip" x="25" y="145" width="110" height="5" rx="2" fill="#1a1510"/>
<rect id="heatStripActive" x="25" y="145" width="0" height="5" rx="2" fill="#ff7733" opacity="0"/>
</g>
<!-- 加热光晕(初始隐藏) -->
<rect id="cookieHeatGlow" x="220" y="350" width="160" height="80" rx="8" fill="url(#cookieHeatGlowGrad)" opacity="0"/>
<!-- 浮尘 -->
<circle cx="120" cy="200" r="1" fill="rgba(100,140,180,0.2)">
<animate attributeName="cy" values="200;185;200" dur="6s" repeatCount="indefinite"/>
<animate attributeName="opacity" values="0.15;0.3;0.15" dur="6s" repeatCount="indefinite"/>
</circle>
<circle cx="400" cy="150" r="0.8" fill="rgba(100,140,180,0.2)">
<animate attributeName="cy" values="150;138;150" dur="8s" repeatCount="indefinite"/>
<animate attributeName="opacity" values="0.1;0.25;0.1" dur="8s" repeatCount="indefinite"/>
</circle>
<circle cx="350" cy="300" r="1.2" fill="rgba(100,140,180,0.15)">
<animate attributeName="cy" values="300;288;300" dur="7s" repeatCount="indefinite"/>
</circle>
</g>
<!-- ====== 连接管道 ====== -->
<g id="connectionPipe">
<!-- 管道主体 -->
<path d="M920,510 C860,545 720,565 620,560 C520,555 400,535 310,470" fill="none" stroke="url(#pipeGrad)" stroke-width="14" stroke-linecap="round"/>
<path d="M920,510 C860,545 720,565 620,560 C520,555 400,535 310,470" fill="none" stroke="rgba(60,65,75,0.4)" stroke-width="8" stroke-linecap="round"/>
<!-- 管道高光 -->
<path d="M918,507 C858,542 718,562 618,557 C518,552 398,532 308,467" fill="none" stroke="rgba(100,105,120,0.15)" stroke-width="2" stroke-linecap="round"/>
<!-- 传感器标记 -->
<circle cx="620" cy="560" r="8" fill="#1a1e28" stroke="#3a4050" stroke-width="1.5"/>
<text x="620" y="564" text-anchor="middle" font-size="7" fill="#5a6070" font-family="Cinzel, serif">S</text>
<!-- 管道标签 -->
<text x="615" y="590" text-anchor="middle" font-size="10" fill="#3a4050" font-family="Cinzel, serif" letter-spacing="0.1em">THERMAL CONDUIT</text>
<!-- ★ 热粒子(初始隐藏)★ -->
<g id="heatParticles" opacity="0">
<circle class="heat-particle" r="3" fill="#ff8833"/>
<circle class="heat-particle" r="2.5" fill="#ff9944"/>
<circle class="heat-particle" r="3" fill="#ffaa55"/>
<circle class="heat-particle" r="2" fill="#ff7733"/>
<circle class="heat-particle" r="3.5" fill="#ff8833"/>
<circle class="heat-particle" r="2.5" fill="#ffbb66"/>
<circle class="heat-particle" r="2" fill="#ff9944"/>
<circle class="heat-particle" r="3" fill="#ff8833"/>
<circle class="heat-particle" r="2.5" fill="#ffaa55"/>
<circle class="heat-particle" r="3.2" fill="#ff7733"/>
<circle class="heat-particle" r="2.8" fill="#ff9944"/>
<circle class="heat-particle" r="2" fill="#ffbb66"/>
</g>
</g>
<!-- ====== Evil的房间 ====== -->
<g id="evilRoom">
<!-- 房间墙体 -->
<rect x="635" y="55" width="535" height="490" rx="4" fill="url(#evilWallGrad)"/>
<rect x="635" y="55" width="535" height="490" rx="4" fill="url(#wallpaperEvil)"/>
<!-- 地板 -->
<rect x="635" y="460" width="535" height="85" fill="#0e0606"/>
<line x1="635" y1="460" x2="1170" y2="460" stroke="#2a1010" stroke-width="1"/>
<!-- 房间标签 -->
<text x="903" y="45" text-anchor="middle" font-family="Cinzel, serif" font-size="16" font-weight="700" fill="#8a3a3a" letter-spacing="0.15em">EVIL'S ROOM</text>
<!-- 壁炉光照(初始隐藏) -->
<rect id="fireGlow" x="760" y="250" width="260" height="220" rx="8" fill="url(#fireGlowGrad)" opacity="0"/>
<!-- ===== 壁炉 ===== -->
<g id="fireplace" transform="translate(790, 160)">
<!-- 壁炉外框 -->
<rect x="-15" y="0" width="230" height="310" rx="4" fill="url(#stoneGrad)" stroke="#5a4a3a" stroke-width="2"/>
<!-- 壁炉石纹 -->
<line x1="-15" y1="60" x2="215" y2="60" stroke="#3a2a1a" stroke-width="0.5"/>
<line x1="-15" y1="120" x2="215" y2="120" stroke="#3a2a1a" stroke-width="0.5"/>
<line x1="50" y1="0" x2="50" y2="60" stroke="#3a2a1a" stroke-width="0.5"/>
<line x1="150" y1="0" x2="150" y2="60" stroke="#3a2a1a" stroke-width="0.5"/>
<!-- 壁炉台面 -->
<rect x="-25" y="-8" width="250" height="14" rx="3" fill="#5a4a38" stroke="#4a3a28" stroke-width="1"/>
<!-- ★ 烙印区域(初始隐藏)★ -->
<g id="brandMark" opacity="0" transform="translate(55, 20)">
<rect x="-5" y="-5" width="120" height="40" rx="4" fill="rgba(255,34,68,0.05)"/>
<!-- 月亮符号 -->
<g transform="translate(18, 15)">
<path d="M0,-8 A8,8 0 1,1 0,8 A5,5 0 1,0 0,-8" fill="#ff2244" opacity="0.9"/>
</g>
<text x="42" y="22" font-family="Noto Serif SC, serif" font-size="22" font-weight="900" fill="#ff2244" filter="url(#glowRed)">= 红</text>
</g>
<!-- 火柴盒 -->
<g id="matchbox" transform="translate(70, -6)">
<rect x="0" y="-14" width="55" height="14" rx="2" fill="#6a3520" stroke="#8a4530" stroke-width="0.8"/>
<rect x="3" y="-12" width="20" height="10" rx="1" fill="#4a2515"/>
<text x="35" y="-4" text-anchor="middle" font-size="6" fill="#c9a84c" font-family="Cinzel, serif">MATCH</text>
<!-- 火柴棒提示 -->
<line x1="58" y1="-7" x2="72" y2="-7" stroke="#c9a84c" stroke-width="1.5" stroke-linecap="round"/>
<ellipse cx="73" cy="-7" rx="2.5" ry="3" fill="#8b2020"/>
</g>
<!-- 壁炉拱门 -->
<path d="M15,80 L15,260 Q100,290 185,260 L185,80" fill="#0a0404" stroke="#4a3a2a" stroke-width="2"/>
<!-- 拱门内壁砖纹 -->
<path d="M20,100 L20,260 Q100,285 180,260 L180,100" fill="none" stroke="rgba(60,40,20,0.3)" stroke-width="0.5"/>
<!-- 壁炉底座/灰床 -->
<ellipse cx="100" cy="270" rx="70" ry="8" fill="#1a0e08"/>
</g>
<!-- ★ 火焰组(初始隐藏)★ -->
<g id="flames" opacity="0" transform="translate(890, 410)">
<!-- 外层火焰 -->
<path class="flame-outer" d="M0,-55 C18,-38 22,-12 18,8 C14,22 6,28 0,28 C-6,28 -14,22 -18,8 C-22,-12 -18,-38 0,-55" fill="#cc3300" opacity="0.7"/>
<!-- 中层火焰 -->
<path class="flame-mid" d="M0,-42 C13,-28 16,-8 13,6 C10,16 5,20 0,20 C-5,20 -10,16 -13,6 C-16,-8 -13,-28 0,-42" fill="#ff8c2b" opacity="0.8"/>
<!-- 内层火焰 -->
<path class="flame-inner" d="M0,-28 C8,-18 10,-4 8,4 C6,10 3,13 0,13 C-3,13 -6,10 -8,4 C-10,-4 -8,-18 0,-28" fill="#ffcc44" opacity="0.85"/>
<!-- 核心 -->
<path class="flame-core" d="M0,-14 C4,-8 5,-1 4,2 C3,5 1,6 0,6 C-1,6 -3,5 -4,2 C-5,-1 -4,-8 0,-14" fill="#fff4b0" opacity="0.95"/>
<!-- 火星 -->
<circle cx="-8" cy="-60" r="1.5" fill="#ffaa33" opacity="0.6">
<animate attributeName="cy" values="-60;-80;-60" dur="1.5s" repeatCount="indefinite"/>
<animate attributeName="opacity" values="0.6;0;0.6" dur="1.5s" repeatCount="indefinite"/>
</circle>
<circle cx="5" cy="-50" r="1" fill="#ff8833" opacity="0.5">
<animate attributeName="cy" values="-50;-72;-50" dur="2s" repeatCount="indefinite"/>
<animate attributeName="opacity" values="0.5;0;0.5" dur="2s" repeatCount="indefinite"/>
</circle>
<circle cx="12" cy="-45" r="0.8" fill="#ffcc44" opacity="0.4">
<animate attributeName="cy" values="-45;-65;-45" dur="1.8s" repeatCount="indefinite"/>
<animate attributeName="opacity" values="0.4;0;0.4" dur="1.8s" repeatCount="indefinite"/>
</circle>
</g>
<!-- 浮尘 -->
<circle cx="750" cy="200" r="1" fill="rgba(140,80,60,0.15)">
<animate attributeName="cy" values="200;185;200" dur="7s" repeatCount="indefinite"/>
<animate attributeName="opacity" values="0.1;0.25;0.1" dur="7s" repeatCount="indefinite"/>
</circle>
<circle cx="1050" cy="180" r="0.8" fill="rgba(140,80,60,0.15)">
<animate attributeName="cy" values="180;168;180" dur="9s" repeatCount="indefinite"/>
</circle>
</g>
<!-- ====== 温度计 ====== -->
<g id="tempGauge" transform="translate(585, 80)">
<rect x="-10" y="0" width="20" height="380" rx="10" fill="#0a0e18" stroke="#1a2535" stroke-width="1.5"/>
<rect x="-6" y="4" width="12" height="372" rx="6" fill="#0e1420"/>
<!-- 温度填充 -->
<rect id="tempFill" x="-6" y="376" width="12" height="0" rx="6" fill="url(#tempGaugeGrad)"/>
<!-- 45°C标记线 -->
<line x1="-16" y1="152" x2="-10" y2="152" stroke="#ff6622" stroke-width="2"/>
<text x="-22" y="156" text-anchor="end" font-size="9" fill="#ff6622" font-family="Cinzel, serif" font-weight="700">45°C</text>
<!-- 刻度 -->
<line x1="-14" y1="376" x2="-10" y2="376" stroke="#3a4555" stroke-width="1"/>
<text x="-18" y="380" text-anchor="end" font-size="7" fill="#3a4555">0°</text>
<line x1="-14" y1="283" x2="-10" y2="283" stroke="#3a4555" stroke-width="1"/>
<text x="-18" y="287" text-anchor="end" font-size="7" fill="#3a4555">25°</text>
<line x1="-14" y1="190" x2="-10" y2="190" stroke="#3a4555" stroke-width="1"/>
<text x="-18" y="194" text-anchor="end" font-size="7" fill="#3a4555">50°</text>
<line x1="-14" y1="97" x2="-10" y2="97" stroke="#3a4555" stroke-width="1"/>
<text x="-18" y="101" text-anchor="end" font-size="7" fill="#3a4555">75°</text>
</g>
<!-- ====== 状态信息 ====== -->
<g id="statusText" transform="translate(600, 660)">
<text id="statusLine" x="0" y="0" text-anchor="middle" font-size="13" fill="#4a4540" font-family="Noto Serif SC, serif">等待 Evil 点燃壁炉...</text>
</g>
<!-- ====== 计时器 ====== -->
<g id="timerDisplay" transform="translate(600, 635)" opacity="0">
<text id="timerText" x="0" y="0" text-anchor="middle" font-family="Cinzel, serif" font-size="14" font-weight="700" fill="#ff8833" letter-spacing="0.1em">30s</text>
</g>
<!-- ====== 动作箭头提示 ====== -->
<g id="actionArrow" opacity="0">
<path d="M870,340 L850,360 M870,340 L890,360" stroke="#ff8833" stroke-width="2" stroke-linecap="round" fill="none"/>
<animate attributeName="opacity" values="0;0.8;0" dur="2s" repeatCount="indefinite"/>
</g>
</svg>
</div>
<!-- 控制面板 -->
<div class="controls-panel">
<button id="btnLight" class="ctrl-btn" onclick="lightFire()">点燃壁炉</button>
<div class="temp-slider-wrap">
<label>环境温度</label>
<input type="range" id="tempSlider" class="temp-slider" min="20" max="80" value="22" step="1" oninput="onTempSlide(this.value)"/>
<span id="tempDisplay" class="temp-value cold">22°C</span>
</div>
<button id="btnReset" class="ctrl-btn" onclick="resetScene()" disabled>重置</button>
</div>
<div id="statusBar" class="status-bar">拖动温度滑块至 45°C 以上,或点击「点燃壁炉」触发显影</div>
<!-- IFR说明 -->
<div class="ifr-panel">
<h3>IFR · 最终理想解分析</h3>
<p>
<span class="ifr-key">矛盾</span>:线索需要隐藏(神秘感) vs 玩家需要发现(避免卡关)。<br>
<span class="ifr-key">理想解</span>:线索自身在恰当时机"主动显现",无需额外提示系统——温感油墨利用壁炉热能这一<span class="ifr-key">已存在资源</span>作为激活条件,零额外功耗。<br>
<span class="ifr-key">资源巧用</span>:Evil 点火这一必须操作本身就是触发器,热能经管道传导至 Neuro 端包装盒底座,激活感温装置 → 隐形墨水显影。系统复杂度极低,却强制创造了双方交互契机。<br>
<span class="ifr-key">关键阈值</span>:45°C 显影 · 持续 30 秒 · 不点火则永远不可见
</p>
</div>
<script>
// ===== 状态管理 =====
const state = {
isLit: false,
temperature: 22,
clueRevealed: false,
timerActive: false,
timerSeconds: 30,
animatingTemp: false,
tempAnimId: null,
timerInterval: null,
particleAnimId: null
};
// ===== DOM 引用 =====
const $ = id => document.getElementById(id);
const flames = $('flames');
const fireGlow = $('fireGlow');
const hiddenClue = $('hiddenClue');
const brandMark = $('brandMark');
const heatParticles = $('heatParticles');
const heatStrip = $('heatStrip');
const heatStripActive = $('heatStripActive');
const cookieHeatGlow = $('cookieHeatGlow');
const tempFill = $('tempFill');
const tempDisplay = $('tempDisplay');
const tempSlider = $('tempSlider');
const statusLine = $('statusLine');
const timerDisplay = $('timerDisplay');
const timerText = $('timerText');
const statusBar = $('statusBar');
const btnLight = $('btnLight');
const btnReset = $('btnReset');
const actionArrow = $('actionArrow');
const matchbox = $('matchbox');
// ===== 温度→视觉映射 =====
function tempToFillHeight(temp) {
// 0°C = y:376 (bottom), 80°C = y:4 (top)
const ratio = Math.max(0, Math.min(1, temp / 80));
return ratio * 372;
}
function updateTempGauge(temp) {
const h = tempToFillHeight(temp);
tempFill.setAttribute('height', h);
tempFill.setAttribute('y', 376 - h);
tempDisplay.textContent = Math.round(temp) + '°C';
tempSlider.value = Math.round(temp);
if (temp >= 45) {
tempDisplay.className = 'temp-value hot';
} else {
tempDisplay.className = 'temp-value cold';
}
}
// ===== 核心:温度变化处理 =====
function onTemperatureChange(temp) {
state.temperature = temp;
updateTempGauge(temp);
// 壁炉状态
if (temp > 25) {
const flameOpacity = Math.min(1, (temp - 25) / 20);
flames.setAttribute('opacity', flameOpacity);
fireGlow.setAttribute('opacity', flameOpacity * 0.35);
if (!state.isLit && temp > 30) {
state.isLit = true;
btnLight.classList.add('active');
btnLight.disabled = true;
matchbox.style.opacity = '0.3';
statusLine.textContent = '壁炉已点燃,温度上升中...';
}
} else {
flames.setAttribute('opacity', 0);
fireGlow.setAttribute('opacity', 0);
}
// 管道热传导
if (temp > 35) {
const pipeOpacity = Math.min(1, (temp - 35) / 15);
heatParticles.setAttribute('opacity', pipeOpacity * 0.9);
} else {
heatParticles.setAttribute('opacity', 0);
}
// 包装盒底座加热
if (temp > 38) {
const heatOpacity = Math.min(1, (temp - 38) / 10);
heatStripActive.setAttribute('opacity', heatOpacity);
heatStripActive.setAttribute('width', 110 * heatOpacity);
cookieHeatGlow.setAttribute('opacity', heatOpacity * 0.5);
} else {
heatStripActive.setAttribute('opacity', 0);
heatStripActive.setAttribute('width', 0);
cookieHeatGlow.setAttribute('opacity', 0);
}
// ★ 关键阈值:45°C 线索显影 ★
if (temp >= 45 && !state.clueRevealed) {
revealClues();
}
// 温度低于45时线索消失(如果在计时器结束后)
if (temp < 45 && state.clueRevealed && !state.timerActive) {
hideClues();
}
}
// ===== 线索显影 =====
function revealClues() {
state.clueRevealed = true;
// Neuro端:包装盒显现"☀=蓝"
hiddenClue.setAttribute('opacity', 1);
hiddenClue.classList.add('clue-revealing');
// Evil端:壁炉上方烙印"☾=红"
brandMark.setAttribute('opacity', 1);
brandMark.classList.add('brand-burning');
// 状态更新
statusLine.textContent = '线索已显影!Neuro 看到「☀=蓝」,Evil 看到「☾=红」';
statusBar.className = 'status-bar highlight';
statusBar.textContent = '✦ 双方线索已动态显现 — 拼图完成 ✦';
// 启动30秒计时
startTimer();
}
// ===== 隐藏线索 =====
function hideClues() {
state.clueRevealed = false;
hiddenClue.setAttribute('opacity', 0);
hiddenClue.classList.remove('clue-revealing');
brandMark.setAttribute('opacity', 0);
brandMark.classList.remove('brand-burning');
statusLine.textContent = '温度不足,线索已消退...';
statusBar.className = 'status-bar';
statusBar.textContent = '温度低于 45°C,线索消失';
}
// ===== 30秒计时器 =====
function startTimer() {
state.timerActive = true;
state.timerSeconds = 30;
timerDisplay.setAttribute('opacity', 1);
timerText.textContent = '30s';
timerText.setAttribute('fill', '#ff8833');
state.timerInterval = setInterval(() => {
state.timerSeconds--;
timerText.textContent = state.timerSeconds + 's';
if (state.timerSeconds <= 10) {
timerText.setAttribute('fill', '#ff3344');
}
if (state.timerSeconds <= 0) {
clearInterval(state.timerInterval);
state.timerActive = false;
timerDisplay.setAttribute('opacity', 0.4);
timerText.textContent = '0s';
timerText.setAttribute('fill', '#4a4540');
statusLine.textContent = '显影时间结束,线索消退...';
// 线索渐隐
setTimeout(() => {
if (state.temperature >= 45) {
// 温度还在,重新开始计时(实际上温感油墨持续显影)
statusLine.textContent = '温度维持中,线索持续显现';
timerDisplay.setAttribute('opacity', 1);
startTimer();
} else {
hideClues();
}
}, 1500);
}
}, 1000);
}
// ===== 点燃壁炉按钮 =====
function lightFire() {
if (state.isLit) return;
btnLight.disabled = true;
btnReset.disabled = false;
actionArrow.setAttribute('opacity', 0);
// 温度渐升动画
state.animatingTemp = true;
const targetTemp = 62;
const startTemp = state.temperature;
const startTime = performance.now();
const duration = 4000;
function animateTemp(now) {
const elapsed = now - startTime;
const progress = Math.min(1, elapsed / duration);
// 缓动函数:先快后慢
const eased = 1 - Math.pow(1 - progress, 2.5);
const currentTemp = startTemp + (targetTemp - startTemp) * eased;
onTemperatureChange(currentTemp);
if (progress < 1) {
state.tempAnimId = requestAnimationFrame(animateTemp);
} else {
state.animatingTemp = false;
}
}
state.tempAnimId = requestAnimationFrame(animateTemp);
}
// ===== 温度滑块 =====
function onTempSlide(val) {
if (state.animatingTemp) {
cancelAnimationFrame(state.tempAnimId);
state.animatingTemp = false;
}
const temp = parseFloat(val);
btnReset.disabled = false;
if (temp > 25 && !state.isLit) {
state.isLit = true;
btnLight.classList.add('active');
btnLight.disabled = true;
matchbox.style.opacity = '0.3';
}
onTemperatureChange(temp);
if (temp < 45 && state.clueRevealed && state.timerActive) {
clearInterval(state.timerInterval);
state.timerActive = false;
timerDisplay.setAttribute('opacity', 0.4);
hideClues();
statusBar.className = 'status-bar';
statusBar.textContent = '温度低于 45°C,线索消失';
}
if (temp >= 25) {
statusLine.textContent = temp >= 45 ? '线索已显影!双方确认拼图' : '壁炉燃烧中,温度上升...';
} else {
statusLine.textContent = '壁炉未点燃';
}
}
// ===== 重置 =====
function resetScene() {
// 清除所有动画和计时
if (state.tempAnimId) cancelAnimationFrame(state.tempAnimId);
if (state.timerInterval) clearInterval(state.timerInterval);
state.isLit = false;
state.temperature = 22;
state.clueRevealed = false;
state.timerActive = false;
state.timerSeconds = 30;
state.animatingTemp = false;
// 重置视觉
updateTempGauge(22);
flames.setAttribute('opacity', 0);
fireGlow.setAttribute('opacity', 0);
heatParticles.setAttribute('opacity', 0);
heatStripActive.setAttribute('opacity', 0);
heatStripActive.setAttribute('width', 0);
cookieHeatGlow.setAttribute('opacity', 0);
hiddenClue.setAttribute('opacity', 0);
hiddenClue.classList.remove('clue-revealing');
brandMark.setAttribute('opacity', 0);
brandMark.classList.remove('brand-burning');
timerDisplay.setAttribute('opacity', 0);
timerText.textContent = '30s';
timerText.setAttribute('fill', '#ff8833');
matchbox.style.opacity = '1';
// 重置按钮
btnLight.disabled = false;
btnLight.classList.remove('active');
btnReset.disabled = true;
// 重置状态文字
statusLine.textContent = '等待 Evil 点燃壁炉...';
statusBar.className = 'status-bar';
statusBar.textContent = '拖动温度滑块至 45°C 以上,或点击「点燃壁炉」触发显影';
}
// ===== 初始化 =====
updateTempGauge(22);
// 初始显示动作提示箭头
setTimeout(() => {
if (!state.isLit) {
actionArrow.setAttribute('opacity', 0.6);
}
}, 1500);
</script>
</body>
</html>
实现说明:
IFR 核心可视化:动画直接展示"理想解已实现"的运作状态——点燃壁炉后,热能沿管道自动传导至 Neuro 端包装盒,隐形墨水在 45°C 阈值处自显现,无需任何额外提示系统。资源(热能)的流向用橙色粒子沿管道路径流动来表现,清晰呈现"已有资源被巧妙利用"的 IFR 思想。
视觉引导设计:
- 中央温度计实时显示温度,45°C 红色标记线作为关键阈值提示
- 热粒子从壁炉端流向包装盒端,颜色从深橙渐变为亮黄
- 线索显影时使用
blur→clear过渡 + 辉光滤镜,制造"墨水被热能激活"的视觉感受 - 烙印使用
brightness(3)→brightness(1)过渡,模拟烧入效果
交互性:
- 「点燃壁炉」按钮触发完整的温度渐升动画(4秒缓动上升至 62°C)
- 温度滑块可手动精确控制,体验阈值临界行为:低于 45°C 线索不可见,达到 45°C 瞬间触发显影
- 30 秒倒计时器模拟温感油墨的持续显影时间窗口
- 重置按钮恢复初始状态
氛围营造:两个房间使用冷暖对比色调(Neuro 冷蓝 vs Evil 暗红),壁炉火焰多层分频闪烁,浮尘粒子增加空间感,噪点纹理覆盖增强质感。
积分规则:第一轮对话扣减6分,后续每轮扣4分
等待动画代码生成...
