分享图
A
动画渲染工坊
就绪

这是一个非常有意思的 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>

实现说明:

  1. IFR 核心可视化:动画直接展示"理想解已实现"的运作状态——点燃壁炉后,热能沿管道自动传导至 Neuro 端包装盒,隐形墨水在 45°C 阈值处自显现,无需任何额外提示系统。资源(热能)的流向用橙色粒子沿管道路径流动来表现,清晰呈现"已有资源被巧妙利用"的 IFR 思想。

  2. 视觉引导设计

    • 中央温度计实时显示温度,45°C 红色标记线作为关键阈值提示
    • 热粒子从壁炉端流向包装盒端,颜色从深橙渐变为亮黄
    • 线索显影时使用 blur→clear 过渡 + 辉光滤镜,制造"墨水被热能激活"的视觉感受
    • 烙印使用 brightness(3)→brightness(1) 过渡,模拟烧入效果
  3. 交互性

    • 「点燃壁炉」按钮触发完整的温度渐升动画(4秒缓动上升至 62°C)
    • 温度滑块可手动精确控制,体验阈值临界行为:低于 45°C 线索不可见,达到 45°C 瞬间触发显影
    • 30 秒倒计时器模拟温感油墨的持续显影时间窗口
    • 重置按钮恢复初始状态
  4. 氛围营造:两个房间使用冷暖对比色调(Neuro 冷蓝 vs Evil 暗红),壁炉火焰多层分频闪烁,浮尘粒子增加空间感,噪点纹理覆盖增强质感。

积分规则:第一轮对话扣减6分,后续每轮扣4分