分享图
动画工坊
引擎就绪
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>中频感应缓苏干燥系统 - IFR 原理可视化</title>
    <style>
        :root {
            --bg-color: #07090f;
            --grid-color: rgba(56, 189, 248, 0.05);
            --text-main: #94a3b8;
            --text-glow: #e2e8f0;
            --heat-color: #ff4d00;
            --cool-color: #00e5ff;
            --grain-color: #f59e0b;
            --machine-bg: #0f172a;
            --machine-border: #1e293b;
        }

        body {
            margin: 0;
            padding: 0;
            background-color: var(--bg-color);
            color: var(--text-main);
            font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
            overflow: hidden;
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            width: 100vw;
        }

        #visualization-container {
            position: relative;
            width: 100%;
            height: 100%;
            display: flex;
            justify-content: center;
            align-items: center;
            background-image: 
                linear-gradient(var(--grid-color) 1px, transparent 1px),
                linear-gradient(90deg, var(--grid-color) 1px, transparent 1px);
            background-size: 40px 40px;
            background-position: center center;
        }

        svg {
            width: 100%;
            height: 100%;
            max-width: 1800px;
            max-height: 1000px;
            filter: drop-shadow(0 0 30px rgba(0, 0, 0, 0.8));
        }

        /* Peripheral Minimalist UI Overlay */
        .overlay-ui {
            position: absolute;
            pointer-events: none;
            font-size: 13px;
            line-height: 1.5;
            letter-spacing: 0.5px;
            z-index: 10;
        }

        .top-left { top: 30px; left: 40px; }
        .bottom-right { bottom: 30px; right: 40px; text-align: right; }
        
        .title-block {
            border-left: 3px solid var(--cool-color);
            padding-left: 12px;
            margin-bottom: 15px;
        }

        .title {
            font-size: 16px;
            font-weight: 600;
            color: var(--text-glow);
            letter-spacing: 1.5px;
            text-transform: uppercase;
        }

        .subtitle {
            font-size: 11px;
            color: var(--cool-color);
            opacity: 0.8;
            font-family: monospace;
        }

        .legend-item {
            display: flex;
            align-items: center;
            margin-bottom: 6px;
            gap: 8px;
        }

        .legend-color {
            width: 10px;
            height: 10px;
            border-radius: 50%;
        }

        .tag {
            display: inline-block;
            padding: 2px 6px;
            background: rgba(15, 23, 42, 0.8);
            border: 1px solid rgba(255, 255, 255, 0.1);
            border-radius: 4px;
            font-family: monospace;
            color: var(--text-glow);
        }

        /* CSS Animations */
        @keyframes spin {
            from { transform: rotate(0deg); }
            to { transform: rotate(360deg); }
        }

        @keyframes dashFlow {
            to { stroke-dashoffset: -40; }
        }

        @keyframes pulseGlow {
            0% { filter: drop-shadow(0 0 8px var(--heat-color)); opacity: 0.8; }
            50% { filter: drop-shadow(0 0 16px var(--heat-color)); opacity: 1; }
            100% { filter: drop-shadow(0 0 8px var(--heat-color)); opacity: 0.8; }
        }

        .fan-blade {
            transform-origin: 50% 50%;
            animation: spin 0.8s linear infinite;
        }

        .air-stream-hot {
            stroke-dasharray: 10 10;
            animation: dashFlow 1s linear infinite;
            opacity: 0.6;
        }

        .air-stream-cool {
            stroke-dasharray: 10 10;
            animation: dashFlow 1.5s linear infinite;
            opacity: 0.5;
        }

        .induction-coil {
            animation: pulseGlow 2s ease-in-out infinite;
        }
    </style>
</head>
<body>

    <div id="visualization-container">
        <!-- Minimal UI Overlays -->
        <div class="overlay-ui top-left">
            <div class="title-block">
                <div class="title">TRIZ 最终理想解 (IFR)</div>
                <div class="subtitle">中频感应低热缓苏干燥系统</div>
            </div>
            <div style="color: #64748b; max-width: 300px;">
                矛盾消除:极少增加系统复杂度,利用分离时空(交替干燥/缓苏)与间接热风,实现高速排湿且谷温不超限。
            </div>
        </div>

        <div class="overlay-ui bottom-right">
            <div class="legend-item"><span class="legend-color" style="background: var(--heat-color); box-shadow: 0 0 8px var(--heat-color);"></span> 50-55℃ 穿透热风 (带走表层水)</div>
            <div class="legend-item"><span class="legend-color" style="background: var(--cool-color); box-shadow: 0 0 8px var(--cool-color);"></span> 纯通风缓苏区 (内部水分外移)</div>
            <div class="legend-item"><span class="legend-color" style="background: var(--grain-color);"></span> 谷物 (芯温 ≤ 42℃)</div>
            <div style="margin-top: 10px; font-family: monospace; opacity: 0.7;">SYS.STATUS // ACTIVE.AUTO_RUN</div>
        </div>

        <!-- Main SVG Visualization -->
        <svg viewBox="0 0 1600 900" preserveAspectRatio="xMidYMid meet" xmlns="http://www.w3.org/2000/svg">
            <defs>
                <!-- Gradients -->
                <linearGradient id="heatGrad" x1="0" y1="1" x2="0" y2="0">
                    <stop offset="0%" stop-color="#ff4d00" stop-opacity="0.9" />
                    <stop offset="100%" stop-color="#ff4d00" stop-opacity="0" />
                </linearGradient>
                <linearGradient id="coolGrad" x1="0" y1="1" x2="0" y2="0">
                    <stop offset="0%" stop-color="#00e5ff" stop-opacity="0.6" />
                    <stop offset="100%" stop-color="#00e5ff" stop-opacity="0" />
                </linearGradient>
                <linearGradient id="machineBody" x1="0" y1="0" x2="0" y2="1">
                    <stop offset="0%" stop-color="#1e293b" />
                    <stop offset="100%" stop-color="#0f172a" />
                </linearGradient>
                
                <!-- Filters -->
                <filter id="glowHeat" x="-20%" y="-20%" width="140%" height="140%">
                    <feGaussianBlur stdDeviation="6" result="blur" />
                    <feComposite in="SourceGraphic" in2="blur" operator="over" />
                </filter>
                <filter id="glowCool" x="-20%" y="-20%" width="140%" height="140%">
                    <feGaussianBlur stdDeviation="4" result="blur" />
                    <feComposite in="SourceGraphic" in2="blur" operator="over" />
                </filter>

                <!-- Patterns -->
                <pattern id="meshBelt" width="20" height="10" patternUnits="userSpaceOnUse">
                    <path d="M 0,5 L 20,5 M 10,0 L 10,10" stroke="#475569" stroke-width="1" opacity="0.5" />
                </pattern>
                
                <!-- Components -->
                <g id="fan">
                    <circle cx="0" cy="0" r="25" fill="#0f172a" stroke="#334155" stroke-width="2" />
                    <g class="fan-blade">
                        <path d="M 0,-5 C 10,-20 20,-20 0,-23 C -20,-20 -10,-20 0,-5 Z" fill="#64748b" />
                        <path d="M 5,0 C 20,10 20,20 23,0 C 20,-20 20,-10 5,0 Z" fill="#64748b" transform="rotate(120)" />
                        <path d="M 5,0 C 20,10 20,20 23,0 C 20,-20 20,-10 5,0 Z" fill="#64748b" transform="rotate(240)" />
                        <circle cx="0" cy="0" r="4" fill="#cbd5e1" />
                    </g>
                </g>

                <g id="heatExchanger">
                    <!-- Finned tube base -->
                    <rect x="-80" y="-15" width="160" height="30" fill="#334155" rx="5" />
                    <!-- Fins -->
                    <path d="M-70,-20 v40 M-60,-20 v40 M-50,-20 v40 M-40,-20 v40 M-30,-20 v40 M-20,-20 v40 M-10,-20 v40 M0,-20 v40 M10,-20 v40 M20,-20 v40 M30,-20 v40 M40,-20 v40 M50,-20 v40 M60,-20 v40 M70,-20 v40" stroke="#94a3b8" stroke-width="2" opacity="0.6"/>
                    <!-- Induction Coil -->
                    <path d="M -85,-10 Q -75,-25 -65,-10 T -45,-10 T -25,-10 T -5,-10 T 15,-10 T 35,-10 T 55,-10 T 75,-10 T 85,0" 
                          fill="none" stroke="#ff4d00" stroke-width="4" class="induction-coil" />
                    <path d="M -85,10 Q -75,25 -65,10 T -45,10 T -25,10 T -5,10 T 15,10 T 35,10 T 55,10 T 75,10 T 85,0" 
                          fill="none" stroke="#ff4d00" stroke-width="4" class="induction-coil" />
                </g>
            </defs>

            <!-- Background structure elements -->
            <!-- Zone Dividers -->
            <path d="M 700 300 L 700 700 M 900 300 L 900 700" stroke="#334155" stroke-width="2" stroke-dasharray="5 5" />
            <text x="500" y="250" fill="#ff4d00" font-size="14" font-family="monospace" text-anchor="middle" filter="url(#glowHeat)">ZONE 1: 干燥段 (20min) / 加热排湿</text>
            <text x="800" y="250" fill="#00e5ff" font-size="14" font-family="monospace" text-anchor="middle" filter="url(#glowCool)">ZONE 2: 缓苏段 (10min) / 水分外移</text>
            <text x="1100" y="250" fill="#ff4d00" font-size="14" font-family="monospace" text-anchor="middle" filter="url(#glowHeat)">ZONE 3: 干燥段 (20min) / 二次排湿</text>

            <!-- Machine Casing -->
            <path d="M 250 280 L 1350 280 L 1350 780 L 250 780 Z" fill="none" stroke="#1e293b" stroke-width="4" rx="10" />
            
            <!-- Conveyor Belt -->
            <rect x="230" y="520" width="1140" height="15" fill="url(#meshBelt)" stroke="#475569" stroke-width="2" rx="4" />
            <!-- Roller Wheels -->
            <circle cx="240" cy="527.5" r="12" fill="#1e293b" stroke="#64748b" stroke-width="2" />
            <circle cx="1360" cy="527.5" r="12" fill="#1e293b" stroke="#64748b" stroke-width="2" />

            <!-- Fans & Heat Exchangers Assembly -->
            <!-- Zone 1: Drying -->
            <use href="#heatExchanger" x="500" y="650" />
            <use href="#fan" x="420" y="730" />
            <use href="#fan" x="500" y="730" />
            <use href="#fan" x="580" y="730" />
            
            <!-- Zone 2: Tempering (No heat exchanger, only air) -->
            <use href="#fan" x="800" y="730" />
            
            <!-- Zone 3: Drying -->
            <use href="#heatExchanger" x="1100" y="650" />
            <use href="#fan" x="1020" y="730" />
            <use href="#fan" x="1100" y="730" />
            <use href="#fan" x="1180" y="730" />

            <!-- Static Air Streams (Animated via CSS dashes) -->
            <!-- Zone 1 -->
            <path d="M 420 620 L 420 320 M 460 620 L 460 320 M 500 620 L 500 320 M 540 620 L 540 320 M 580 620 L 580 320" 
                  stroke="url(#heatGrad)" stroke-width="6" fill="none" class="air-stream-hot" />
            <!-- Zone 2 -->
            <path d="M 760 700 L 760 320 M 800 700 L 800 320 M 840 700 L 840 320" 
                  stroke="url(#coolGrad)" stroke-width="4" fill="none" class="air-stream-cool" />
            <!-- Zone 3 -->
            <path d="M 1020 620 L 1020 320 M 1060 620 L 1060 320 M 1100 620 L 1100 320 M 1140 620 L 1140 320 M 1180 620 L 1180 320" 
                  stroke="url(#heatGrad)" stroke-width="6" fill="none" class="air-stream-hot" />

            <!-- Exhaust Valves (Top) -->
            <path d="M 480 280 L 480 250 L 520 250 L 520 280 Z" fill="#0f172a" stroke="#475569" stroke-width="2"/>
            <path d="M 485 245 L 495 235 M 500 245 L 510 235 M 515 245 L 525 235" stroke="#ff4d00" stroke-width="2" class="air-stream-hot" stroke-dasharray="4 4" />
            
            <path d="M 1080 280 L 1080 250 L 1120 250 L 1120 280 Z" fill="#0f172a" stroke="#475569" stroke-width="2"/>
            <path d="M 1085 245 L 1095 235 M 1100 245 L 1110 235 M 1115 245 L 1125 235" stroke="#ff4d00" stroke-width="2" class="air-stream-hot" stroke-dasharray="4 4" />

            <!-- Dynamic Containers for JS injected elements -->
            <g id="particles-vapor"></g>
            <g id="particles-grain"></g>

            <!-- MACRO VIEW: The IFR Core Visualizer -->
            <g transform="translate(800, 140)">
                <circle cx="0" cy="0" r="55" fill="#0a0a0f" stroke="#334155" stroke-width="2" />
                <circle cx="0" cy="0" r="60" fill="none" stroke="#475569" stroke-width="1" stroke-dasharray="4 4" />
                <text x="0" y="-75" fill="#e2e8f0" font-size="12" font-family="monospace" text-anchor="middle" font-weight="bold">微观原理分析视窗</text>
                
                <!-- Macro Grain -->
                <circle id="macro-grain-surface" cx="0" cy="0" r="40" fill="none" stroke="#00e5ff" stroke-width="4" opacity="0.8" />
                <circle id="macro-grain-core" cx="0" cy="0" r="38" fill="#f59e0b" opacity="0.8" />
                <circle cx="0" cy="0" r="15" fill="#00e5ff" opacity="0.4" filter="url(#glowCool)" />
                
                <!-- Moisture Migration Arrows (Visible in Tempering) -->
                <g id="macro-migration" opacity="0">
                    <path d="M 0 -20 L 0 -35 M -4 -30 L 0 -35 L 4 -30" stroke="#00e5ff" stroke-width="2" fill="none" />
                    <path d="M 0 20 L 0 35 M -4 30 L 0 35 L 4 30" stroke="#00e5ff" stroke-width="2" fill="none" />
                    <path d="M 20 0 L 35 0 M 30 -4 L 35 0 L 30 4" stroke="#00e5ff" stroke-width="2" fill="none" />
                    <path d="M -20 0 L -35 0 M -30 -4 L -35 0 L -30 4" stroke="#00e5ff" stroke-width="2" fill="none" />
                </g>

                <!-- Status Text inside Macro View -->
                <text id="macro-temp" x="0" y="5" fill="#0f172a" font-size="12" font-family="monospace" text-anchor="middle" font-weight="bold">T: 38℃</text>
                <text id="macro-state" x="0" y="65" fill="#00e5ff" font-size="11" font-family="monospace" text-anchor="middle">水向表面扩散</text>
            </g>

            <!-- Macro View Connectors -->
            <path id="macro-tracker-line" d="M 800 200 L 800 500" stroke="#475569" stroke-width="1" stroke-dasharray="4 4" fill="none" />
            <circle id="macro-tracker-dot" cx="800" cy="500" r="4" fill="none" stroke="#00e5ff" stroke-width="2" />
        </svg>
    </div>

    <script>
        document.addEventListener("DOMContentLoaded", () => {
            const grainContainer = document.getElementById('particles-grain');
            const vaporContainer = document.getElementById('particles-vapor');
            
            // Macro view elements
            const macroSurface = document.getElementById('macro-grain-surface');
            const macroMigration = document.getElementById('macro-migration');
            const macroTemp = document.getElementById('macro-temp');
            const macroState = document.getElementById('macro-state');
            const trackerLine = document.getElementById('macro-tracker-line');
            const trackerDot = document.getElementById('macro-tracker-dot');

            const ZONE_START = 260;
            const ZONE_END = 1340;
            const Y_BASE = 510;
            const NUM_GRAINS = 120;
            
            class Vapor {
                constructor(x, y) {
                    this.x = x + (Math.random() * 20 - 10);
                    this.y = y;
                    this.life = 1.0;
                    this.speed = Math.random() * 1.5 + 1;
                    this.el = document.createElementNS("http://www.w3.org/2000/svg", "circle");
                    this.el.setAttribute("r", Math.random() * 2 + 1);
                    this.el.setAttribute("fill", "#00e5ff");
                    vaporContainer.appendChild(this.el);
                }
                update() {
                    this.y -= this.speed;
                    this.life -= 0.015;
                    this.el.setAttribute("cx", this.x);
                    this.el.setAttribute("cy", this.y);
                    this.el.setAttribute("opacity", Math.max(0, this.life));
                    return this.life > 0;
                }
                remove() {
                    this.el.remove();
                }
            }

            class Grain {
                constructor(x, offsetLayer) {
                    this.x = x;
                    this.y = Y_BASE - offsetLayer * 12 + (Math.random() * 4 - 2);
                    this.internalMoisture = 100;
                    this.surfaceMoisture = 60;
                    this.temp = 25; // ambient start
                    
                    this.g = document.createElementNS("http://www.w3.org/2000/svg", "g");
                    
                    // Core
                    this.core = document.createElementNS("http://www.w3.org/2000/svg", "circle");
                    this.core.setAttribute("r", 5);
                    this.core.setAttribute("fill", "#f59e0b");
                    
                    // Surface moisture ring
                    this.surface = document.createElementNS("http://www.w3.org/2000/svg", "circle");
                    this.surface.setAttribute("r", 6.5);
                    this.surface.setAttribute("fill", "none");
                    this.surface.setAttribute("stroke", "#00e5ff");
                    this.surface.setAttribute("stroke-width", "1.5");
                    
                    this.g.appendChild(this.core);
                    this.g.appendChild(this.surface);
                    grainContainer.appendChild(this.g);
                }

                update() {
                    // Conveyor speed
                    this.x += 0.4;
                    if (this.x > ZONE_END) {
                        this.x = ZONE_START;
                        this.internalMoisture = 100;
                        this.surfaceMoisture = 60;
                        this.temp = 25;
                    }

                    // Physics based on Zones
                    let zone = 0; // 0: enter/exit, 1: dry1, 2: temper, 3: dry2
                    
                    if (this.x > 300 && this.x < 700) zone = 1;
                    else if (this.x >= 700 && this.x <= 900) zone = 2;
                    else if (this.x > 900 && this.x < 1300) zone = 3;

                    if (zone === 1 || zone === 3) {
                        // Drying: Surface moisture evaporates, temp rises slightly but capped
                        if (this.surfaceMoisture > 5) {
                            this.surfaceMoisture -= 0.15;
                            // Spawn vapor randomly
                            if (Math.random() < 0.05) vapors.push(new Vapor(this.x, this.y));
                        }
                        if (this.temp < 41) this.temp += 0.05;
                    } else if (zone === 2) {
                        // Tempering: Internal moves to surface, temp drops
                        if (this.internalMoisture > 20 && this.surfaceMoisture < 80) {
                            this.internalMoisture -= 0.1;
                            this.surfaceMoisture += 0.08; // slight loss
                        }
                        if (this.temp > 32) this.temp -= 0.05;
                    } else {
                        // Ambient cooling
                        if (this.temp > 25) this.temp -= 0.1;
                    }

                    // Update Visuals
                    this.g.setAttribute("transform", `translate(${this.x}, ${this.y})`);
                    this.surface.setAttribute("opacity", Math.max(0, this.surfaceMoisture / 100));
                    
                    // Temp color shift (Core turns more orange as it heats, but never red due to low temp limit)
                    const red = 245;
                    const green = Math.max(120, 158 - (this.temp - 25) * 2); // 158 is amber green
                    this.core.setAttribute("fill", `rgb(${red}, ${green}, 11)`);
                    
                    return { x: this.x, y: this.y, zone, temp: this.temp, surfM: this.surfaceMoisture };
                }
            }

            const grains = [];
            const vapors = [];
            
            // Initialize grains distributed along the belt
            for (let i = 0; i < NUM_GRAINS; i++) {
                const startX = ZONE_START + (Math.random() * (ZONE_END - ZONE_START));
                const layer = i % 3; // 3 layers of grain
                grains.push(new Grain(startX, layer));
            }

            // Select a tracked grain for the Macro View
            const trackedGrainIndex = Math.floor(NUM_GRAINS / 2);

            function animate() {
                // Update grains
                let trackedStats = null;
                
                grains.forEach((grain, index) => {
                    const stats = grain.update();
                    if (index === trackedGrainIndex) {
                        trackedStats = stats;
                    }
                });

                // Update vapors
                for (let i = vapors.length - 1; i >= 0; i--) {
                    if (!vapors[i].update()) {
                        vapors[i].remove();
                        vapors.splice(i, 1);
                    }
                }

                // Update Macro View UI
                if (trackedStats) {
                    trackerDot.setAttribute("cx", trackedStats.x);
                    trackerDot.setAttribute("cy", trackedStats.y);
                    
                    // Bezier curve connector
                    trackerLine.setAttribute("d", `M 800 195 Q 800 350 ${trackedStats.x} ${trackedStats.y}`);

                    macroTemp.textContent = `T: ${trackedStats.temp.toFixed(1)}℃`;
                    macroSurface.setAttribute("stroke-width", Math.max(1, (trackedStats.surfM / 100) * 8));

                    if (trackedStats.zone === 1 || trackedStats.zone === 3) {
                        macroState.textContent = "快速气流带走表层水";
                        macroState.setAttribute("fill", "#ff4d00");
                        macroMigration.setAttribute("opacity", "0");
                        macroTemp.setAttribute("fill", "#ff4d00");
                    } else if (trackedStats.zone === 2) {
                        macroState.textContent = "内部水分向表面扩散";
                        macroState.setAttribute("fill", "#00e5ff");
                        macroMigration.setAttribute("opacity", "1");
                        macroTemp.setAttribute("fill", "#00e5ff");
                        
                        // Rotate migration arrows for active feel
                        const rot = (Date.now() / 20) % 360;
                        macroMigration.setAttribute("transform", `rotate(${rot})`);
                    } else {
                        macroState.textContent = "常温输送区";
                        macroState.setAttribute("fill", "#94a3b8");
                        macroMigration.setAttribute("opacity", "0");
                        macroTemp.setAttribute("fill", "#94a3b8");
                    }
                }

                requestAnimationFrame(animate);
            }

            // Auto-start animation
            requestAnimationFrame(animate);
        });
    </script>
</body>
</html>
积分规则:每轮对话扣10分