分享图
动画工坊
引擎就绪
<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>中频感应振动流化床谷物干燥系统 - IFR理想解演示</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@300;400;700&display=swap" rel="stylesheet">
    <style>
        :root {
            --bg-color: #050508;
            --grid-color: rgba(30, 41, 59, 0.5);
            --cyan-glow: #00f0ff;
            --magma-glow: #ff3300;
            --grain-color: #fbbf24;
            --machine-stroke: #334155;
            --text-muted: #64748b;
        }

        body, html {
            margin: 0;
            padding: 0;
            width: 100%;
            height: 100%;
            background-color: var(--bg-color);
            color: #fff;
            font-family: 'JetBrains Mono', monospace;
            overflow: hidden;
            display: flex;
            align-items: center;
            justify-content: center;
        }

        #animation-container {
            width: 100%;
            height: 100vh;
            max-width: 100vw;
            display: flex;
            align-items: center;
            justify-content: center;
            position: relative;
        }

        svg {
            width: 100%;
            height: 100%;
            max-height: 900px;
            filter: drop-shadow(0 0 20px rgba(0, 240, 255, 0.05));
        }

        .hud-text {
            font-size: 12px;
            fill: var(--text-muted);
            pointer-events: none;
            letter-spacing: 1px;
        }

        .hud-title {
            font-size: 14px;
            fill: var(--cyan-glow);
            font-weight: bold;
            letter-spacing: 2px;
        }

        .hud-value {
            fill: #fff;
            font-weight: bold;
        }

        /* 动画定义 */
        @keyframes pulse-cyan {
            0%, 100% { opacity: 0.3; filter: brightness(1); }
            50% { opacity: 0.8; filter: brightness(1.5); }
        }

        @keyframes vibrate {
            0%, 100% { transform: translateY(0); }
            25% { transform: translateY(-3px); }
            75% { transform: translateY(3px); }
        }

        @keyframes flow-wave {
            0% { stroke-dashoffset: 100; }
            100% { stroke-dashoffset: 0; }
        }

        @keyframes steam-rise {
            0% { transform: translateY(0) scale(1); opacity: 0; }
            20% { opacity: 0.6; }
            100% { transform: translateY(-80px) scale(2); opacity: 0; }
        }

        @keyframes drum-spin {
            from { transform: rotate(0deg); }
            to { transform: rotate(360deg); }
        }

        .coil-glow {
            animation: pulse-cyan 2s infinite ease-in-out;
        }

        .vibrating-bed {
            animation: vibrate 0.1s infinite linear;
        }

        .magnetic-wave {
            stroke-dasharray: 10 15;
            animation: flow-wave 1s infinite linear;
        }

        .drum-group {
            transform-origin: 950px 400px;
            animation: drum-spin 4s infinite linear;
        }

        /* UI Overlay */
        .controls {
            position: absolute;
            bottom: 2rem;
            left: 50%;
            transform: translateX(-50%);
            display: flex;
            gap: 2rem;
            background: rgba(15, 23, 42, 0.8);
            border: 1px solid var(--machine-stroke);
            padding: 1rem 2rem;
            border-radius: 8px;
            backdrop-filter: blur(10px);
            z-index: 10;
        }

        .slider-group {
            display: flex;
            flex-direction: column;
            gap: 0.5rem;
        }

        .slider-group label {
            font-size: 11px;
            color: var(--text-muted);
            display: flex;
            justify-content: space-between;
        }

        .slider-group label span {
            color: var(--cyan-glow);
            font-weight: bold;
        }

        input[type=range] {
            -webkit-appearance: none;
            width: 150px;
            background: var(--machine-stroke);
            height: 4px;
            border-radius: 2px;
            outline: none;
        }

        input[type=range]::-webkit-slider-thumb {
            -webkit-appearance: none;
            width: 12px;
            height: 12px;
            border-radius: 50%;
            background: var(--cyan-glow);
            cursor: pointer;
            box-shadow: 0 0 10px var(--cyan-glow);
        }
    </style>
</head>
<body>

    <div id="animation-container">
        <!-- SVG Canvas (1200x700 居中显示,主体居中,文字边缘) -->
        <svg id="scene" viewBox="0 0 1200 700" preserveAspectRatio="xMidYMid meet">
            <defs>
                <!-- 发光滤镜 -->
                <filter id="glow" x="-20%" y="-20%" width="140%" height="140%">
                    <feGaussianBlur stdDeviation="4" result="blur" />
                    <feComposite in="SourceGraphic" in2="blur" operator="over" />
                </filter>
                <filter id="glow-intense" x="-50%" y="-50%" width="200%" height="200%">
                    <feGaussianBlur stdDeviation="8" result="blur" />
                    <feComposite in="SourceGraphic" in2="blur" operator="over" />
                </filter>
                <filter id="heat-glow">
                    <feGaussianBlur stdDeviation="2" result="blur" />
                    <feComposite in="SourceGraphic" in2="blur" operator="over" />
                </filter>
                
                <!-- 背景网格图案 -->
                <pattern id="grid" width="40" height="40" patternUnits="userSpaceOnUse">
                    <path d="M 40 0 L 0 0 0 40" fill="none" stroke="rgba(30,41,59,0.3)" stroke-width="1"/>
                </pattern>
                
                <!-- 机器纹理 -->
                <linearGradient id="metal" x1="0%" y1="0%" x2="0%" y2="100%">
                    <stop offset="0%" stop-color="#1e293b" />
                    <stop offset="50%" stop-color="#0f172a" />
                    <stop offset="100%" stop-color="#020617" />
                </linearGradient>
            </defs>

            <!-- 1. 背景层 -->
            <rect width="1200" height="700" fill="url(#grid)" />
            
            <!-- HUD 边缘文字排版区 (非遮挡) -->
            <g id="hud-top-left" transform="translate(30, 40)">
                <text class="hud-title" x="0" y="0">SYS.IFR_TRIZ // 理想最终解</text>
                <text class="hud-text" x="0" y="20">型号: IND-FLUID-800</text>
                <text class="hud-text" x="0" y="40">状态: <tspan fill="#00f0ff">在线且稳定运转</tspan></text>
            </g>
            
            <g id="hud-bottom-left" transform="translate(30, 660)">
                <circle cx="5" cy="-5" r="4" fill="#fbbf24" />
                <text class="hud-text" x="15" y="0">谷物颗粒 (受热蒸发水分)</text>
                
                <circle cx="5" cy="-25" r="4" fill="#94a3b8" />
                <circle cx="5" cy="-25" r="4" fill="#ff3300" filter="url(#heat-glow)" opacity="0.8" />
                <text class="hud-text" x="15" y="-20">磁性载热颗粒 (涡流发热源)</text>
                
                <path d="M 0 -45 L 10 -45" stroke="#00f0ff" stroke-width="2" class="magnetic-wave" />
                <text class="hud-text" x="15" y="-40">中频交变磁场区域</text>
            </g>

            <g id="hud-top-right" transform="translate(1000, 40)">
                <text class="hud-text" x="0" y="0">实时监控参数_</text>
                <rect x="0" y="10" width="160" height="1" fill="#334155" />
                
                <text class="hud-text" x="0" y="30">中频频率: <tspan class="hud-value" id="val-freq">12.5 kHz</tspan></text>
                <text class="hud-text" x="0" y="50">振动频率: <tspan class="hud-value">15 Hz</tspan></text>
                <text class="hud-text" x="0" y="70">颗粒温度: <tspan class="hud-value" fill="#ff3300">45.0 °C</tspan></text>
                <text class="hud-text" x="0" y="90">混合比例: <tspan class="hud-value">1 : 2.5</tspan></text>
                
                <!-- 迷你波形图 -->
                <path d="M 0 120 Q 10 110, 20 120 T 40 120 T 60 120 T 80 120 T 100 120 T 120 120 T 140 120" 
                      fill="none" stroke="#00f0ff" stroke-width="1" class="magnetic-wave" />
            </g>

            <!-- 2. 主体设备层 (居中区域 x:150-1050) -->
            
            <!-- 回收管道 (底部) -->
            <path d="M 950 500 L 950 580 L 150 580 L 150 250" fill="none" stroke="#1e293b" stroke-width="30" stroke-linejoin="round" />
            <path d="M 950 500 L 950 580 L 150 580 L 150 250" fill="none" stroke="#00f0ff" stroke-width="1" stroke-linejoin="round" stroke-dasharray="5 10" class="magnetic-wave" opacity="0.3"/>
            <text class="hud-text" x="500" y="600" fill="#00f0ff" opacity="0.6">▲ 磁性颗粒循环回收路径</text>

            <!-- 进料斗 (左侧) -->
            <path d="M 100 100 L 250 100 L 200 250 L 150 250 Z" fill="url(#metal)" stroke="var(--machine-stroke)" stroke-width="2" />
            <text class="hud-text" x="110" y="90">谷物与磁球混合入口</text>
            
            <!-- 成品谷物出口 (右下) -->
            <path d="M 820 480 L 900 480 L 900 600 L 820 600 Z" fill="none" stroke="var(--machine-stroke)" stroke-width="2" stroke-dasharray="4 4"/>
            <text class="hud-text" x="830" y="615">干燥谷物收集区</text>

            <!-- 振动流化床主体 (中央) -->
            <g id="fluid-bed-system">
                <!-- 静态外壳/支架 -->
                <rect x="230" y="270" width="560" height="200" fill="none" stroke="#1e293b" stroke-width="4" rx="8" />
                
                <!-- 振动部分 -->
                <g class="vibrating-bed">
                    <!-- 床体底板 -->
                    <rect x="250" y="440" width="530" height="10" fill="#334155" rx="4" />
                    <!-- 支撑弹簧 -->
                    <path d="M 300 450 L 290 455 L 310 465 L 290 475 L 310 485 L 300 490 M 700 450 L 690 455 L 710 465 L 690 475 L 710 485 L 700 490" fill="none" stroke="#64748b" stroke-width="3" />
                </g>

                <!-- 中频感应线圈 (上/下阵列) -->
                <g id="induction-coils">
                    <!-- 上方线圈 -->
                    <rect x="280" y="220" width="450" height="30" fill="#0f172a" stroke="#00f0ff" stroke-width="1" rx="4" />
                    <g class="coil-glow" fill="#00f0ff">
                        <rect x="290" y="225" width="20" height="20" rx="2" />
                        <rect x="330" y="225" width="20" height="20" rx="2" />
                        <rect x="370" y="225" width="20" height="20" rx="2" />
                        <rect x="410" y="225" width="20" height="20" rx="2" />
                        <rect x="450" y="225" width="20" height="20" rx="2" />
                        <rect x="490" y="225" width="20" height="20" rx="2" />
                        <rect x="530" y="225" width="20" height="20" rx="2" />
                        <rect x="570" y="225" width="20" height="20" rx="2" />
                        <rect x="610" y="225" width="20" height="20" rx="2" />
                        <rect x="650" y="225" width="20" height="20" rx="2" />
                        <rect x="690" y="225" width="20" height="20" rx="2" />
                    </g>
                    <!-- 下方线圈 -->
                    <rect x="280" y="480" width="450" height="30" fill="#0f172a" stroke="#00f0ff" stroke-width="1" rx="4" />
                    <g class="coil-glow" fill="#00f0ff">
                        <rect x="290" y="485" width="20" height="20" rx="2" />
                        <rect x="330" y="485" width="20" height="20" rx="2" />
                        <rect x="370" y="485" width="20" height="20" rx="2" />
                        <rect x="410" y="485" width="20" height="20" rx="2" />
                        <rect x="450" y="485" width="20" height="20" rx="2" />
                        <rect x="490" y="485" width="20" height="20" rx="2" />
                        <rect x="530" y="485" width="20" height="20" rx="2" />
                        <rect x="570" y="485" width="20" height="20" rx="2" />
                        <rect x="610" y="485" width="20" height="20" rx="2" />
                        <rect x="650" y="485" width="20" height="20" rx="2" />
                        <rect x="690" y="485" width="20" height="20" rx="2" />
                    </g>
                    
                    <!-- 磁场指示线 -->
                    <g stroke="#00f0ff" stroke-width="1" opacity="0.2" class="magnetic-wave">
                        <path d="M 300 250 L 300 480 M 340 250 L 340 480 M 380 250 L 380 480 M 420 250 L 420 480 M 460 250 L 460 480 M 500 250 L 500 480 M 540 250 L 540 480 M 580 250 L 580 480 M 620 250 L 620 480 M 660 250 L 660 480 M 700 250 L 700 480" />
                    </g>
                </g>
            </g>
            
            <text class="hud-text" x="460" y="210" fill="#00f0ff">中频交变磁场 (产生涡流)</text>
            <text class="hud-text" x="460" y="530">振动流化床 (悬浮、翻动)</text>

            <!-- 磁选滚筒 (右侧) -->
            <g id="magnetic-separator" transform="translate(950, 400)">
                <circle cx="0" cy="0" r="90" fill="none" stroke="var(--machine-stroke)" stroke-width="2" stroke-dasharray="10 5" />
                <g class="drum-group">
                    <circle cx="0" cy="0" r="80" fill="url(#metal)" stroke="#334155" stroke-width="4" />
                    <!-- 滚筒内部磁极指示 -->
                    <path d="M -70 0 A 70 70 0 0 1 70 0" fill="none" stroke="#00f0ff" stroke-width="6" opacity="0.4" filter="url(#glow)"/>
                    <line x1="-80" y1="0" x2="80" y2="0" stroke="#334155" stroke-width="2" />
                    <line x1="0" y1="-80" x2="0" y2="80" stroke="#334155" stroke-width="2" />
                </g>
                <text class="hud-text" x="-35" y="-95" fill="#00f0ff">磁选分离滚筒</text>
            </g>

            <!-- 挡板 / 刮板 -->
            <path d="M 940 490 L 980 480" stroke="#fbbf24" stroke-width="3" stroke-linecap="round"/>

            <!-- 3. 粒子系统容器 (通过JS动态渲染) -->
            <g id="steam-layer"></g>
            <g id="particles-layer"></g>
            
        </svg>

        <!-- 交互控制面板 (悬浮于底部居中) -->
        <div class="controls">
            <div class="slider-group">
                <label>中频磁场强度 <span>(感应加热)</span></label>
                <input type="range" id="power-slider" min="0" max="100" value="80">
            </div>
            <div class="slider-group">
                <label>流化床振动幅度 <span>(物理翻动)</span></label>
                <input type="range" id="vibrate-slider" min="10" max="100" value="50">
            </div>
        </div>
    </div>

    <script>
        // 常量与配置
        const SVG_NS = "http://www.w3.org/2000/svg";
        const particlesLayer = document.getElementById('particles-layer');
        const steamLayer = document.getElementById('steam-layer');
        const particles = [];
        const steams = [];
        
        let powerLevel = 80;
        let vibrateLevel = 50;

        // UI 绑定
        document.getElementById('power-slider').addEventListener('input', (e) => {
            powerLevel = parseInt(e.target.value);
            document.getElementById('val-freq').textContent = (8 + (powerLevel / 100) * 7).toFixed(1) + ' kHz';
            const coils = document.querySelectorAll('.coil-glow, .magnetic-wave');
            coils.forEach(el => {
                el.style.opacity = (powerLevel / 100);
            });
        });

        document.getElementById('vibrate-slider').addEventListener('input', (e) => {
            vibrateLevel = parseInt(e.target.value);
            document.querySelector('.vibrating-bed').style.animationDuration = (0.2 - (vibrateLevel / 100) * 0.15) + 's';
        });

        class Particle {
            constructor(type, startX, startY) {
                this.type = type; // 'grain' (谷物) 或 'ball' (磁球)
                this.x = startX;
                this.y = startY;
                this.vx = (Math.random() - 0.5) * 2;
                this.vy = 2 + Math.random() * 2;
                this.temp = type === 'ball' ? 25 : 25; 
                this.moisture = type === 'grain' ? 100 : 0;
                this.radius = type === 'grain' ? 5 : 4;
                this.element = document.createElementNS(SVG_NS, type === 'grain' ? 'ellipse' : 'circle');
                this.glow = null;

                if (type === 'grain') {
                    this.element.setAttribute('rx', this.radius + 1);
                    this.element.setAttribute('ry', this.radius - 1);
                    this.element.setAttribute('fill', '#fbbf24');
                } else {
                    this.element.setAttribute('r', this.radius);
                    this.element.setAttribute('fill', '#94a3b8');
                    
                    // 磁球发光层
                    this.glow = document.createElementNS(SVG_NS, 'circle');
                    this.glow.setAttribute('r', this.radius * 1.5);
                    this.glow.setAttribute('fill', '#ff3300');
                    this.glow.setAttribute('filter', 'url(#heat-glow)');
                    this.glow.setAttribute('opacity', '0');
                    particlesLayer.appendChild(this.glow);
                }

                particlesLayer.appendChild(this.element);
                this.state = 'hopper'; // hopper -> bed -> separation -> end / recycle
                this.updateDOM();
            }

            updateDOM() {
                if (this.type === 'grain') {
                    this.element.setAttribute('cx', this.x);
                    this.element.setAttribute('cy', this.y);
                    
                    // 随温度/干燥度改变颜色 (偏深橙)
                    const dryness = 100 - this.moisture;
                    const r = 251, g = 191 - (dryness * 0.8), b = 36 - (dryness * 0.2);
                    this.element.setAttribute('fill', `rgb(${r}, ${g}, ${b})`);
                } else {
                    this.element.setAttribute('cx', this.x);
                    this.element.setAttribute('cy', this.y);
                    
                    if (this.glow) {
                        this.glow.setAttribute('cx', this.x);
                        this.glow.setAttribute('cy', this.y);
                        // 温度映射到透明度 25度->0, 80度->1
                        let glowOpacity = Math.max(0, (this.temp - 30) / 50);
                        this.glow.setAttribute('opacity', glowOpacity.toString());
                    }
                }
            }

            destroy() {
                if (this.element.parentNode) this.element.parentNode.removeChild(this.element);
                if (this.glow && this.glow.parentNode) this.glow.parentNode.removeChild(this.glow);
            }
        }

        class Steam {
            constructor(x, y) {
                this.x = x;
                this.y = y;
                this.age = 0;
                this.life = 60 + Math.random() * 40;
                this.element = document.createElementNS(SVG_NS, 'circle');
                this.element.setAttribute('r', 3 + Math.random() * 4);
                this.element.setAttribute('fill', '#ffffff');
                this.element.setAttribute('opacity', '0.4');
                this.element.setAttribute('filter', 'url(#glow)');
                steamLayer.appendChild(this.element);
            }
            update() {
                this.age++;
                this.y -= 1.5;
                this.x += (Math.random() - 0.5) * 2;
                this.element.setAttribute('cx', this.x);
                this.element.setAttribute('cy', this.y);
                this.element.setAttribute('opacity', 0.4 * (1 - this.age / this.life));
                return this.age >= this.life;
            }
            destroy() {
                if (this.element.parentNode) this.element.parentNode.removeChild(this.element);
            }
        }

        // 动画主循环
        let frame = 0;
        
        function spawnParticles() {
            // 在进料口生成,比例 谷物:磁球 = 2.5 : 1
            if (frame % 3 === 0) {
                particles.push(new Particle('grain', 160 + Math.random() * 40, 100));
                particles.push(new Particle('grain', 160 + Math.random() * 40, 100));
            }
            if (frame % 7 === 0) {
                particles.push(new Particle('ball', 160 + Math.random() * 40, 100));
            }
        }

        function updatePhysics() {
            for (let i = particles.length - 1; i >= 0; i--) {
                let p = particles[i];
                let isDead = false;

                switch (p.state) {
                    case 'hopper':
                        p.y += p.vy;
                        if (p.y > 250) {
                            p.state = 'bed';
                            p.vx = 2 + Math.random() * 2;
                        }
                        break;
                    
                    case 'bed':
                        // 物理模拟:重力与床底碰撞
                        p.vy += 0.2; // gravity
                        p.y += p.vy;
                        p.x += p.vx;

                        // 床底碰撞 (流化床振动给向上的力)
                        if (p.y > 430) {
                            p.y = 430;
                            // 振动幅度影响弹跳高度
                            p.vy = -(3 + (vibrateLevel / 100) * 4) * (0.8 + Math.random() * 0.4);
                        }
                        
                        // 推进力 (向右流动)
                        if (p.vx < 1.5) p.vx += 0.1;
                        if (p.vx > 3) p.vx -= 0.1;

                        // 核心创新逻辑:磁球发热,谷物吸热
                        if (p.type === 'ball') {
                            // 在感应区,吸收能量升温
                            if (p.x > 280 && p.x < 730) {
                                p.temp += (powerLevel / 100) * 1.5;
                                if (p.temp > 80) p.temp = 80; // 最高温限制,避免过热
                            }
                        } else if (p.type === 'grain') {
                            // 极简碰撞模拟:基于概率和群体密度的热传导
                            // 假定谷物在床内稳定升温,水分下降
                            if (p.x > 280 && p.x < 730) {
                                p.temp += 0.1 + (powerLevel / 100) * 0.2;
                                if (p.temp > 45) { // 达到蒸发温度
                                    p.moisture -= 0.5;
                                    if (Math.random() < 0.05) { // 产生蒸汽视觉
                                        steams.push(new Steam(p.x, p.y));
                                    }
                                }
                            }
                        }

                        // 离开流化床
                        if (p.x > 800) {
                            p.state = 'separation';
                            p.vx = 3;
                            p.vy = -2;
                        }
                        break;
                    
                    case 'separation':
                        // 到达磁选滚筒
                        p.y += p.vy;
                        p.x += p.vx;
                        p.vy += 0.2; // 重力
                        
                        const drumX = 950, drumY = 400, r = 80;
                        const dx = p.x - drumX;
                        const dy = p.y - drumY;
                        const dist = Math.sqrt(dx*dx + dy*dy);

                        if (p.type === 'ball') {
                            // 磁球被滚筒吸附
                            if (dist < r + 20 && p.x < drumX + 20) {
                                // 强制贴附在表面旋转
                                const angle = Math.atan2(dy, dx);
                                // 根据滚筒旋转计算新位置
                                p.x = drumX + Math.cos(angle + 0.05) * (r + p.radius);
                                p.y = drumY + Math.sin(angle + 0.05) * (r + p.radius);
                                p.vx = 0; p.vy = 0;
                                
                                // 底部脱离点 (通过刮板或磁场减弱区)
                                if (angle > Math.PI * 0.4 && angle < Math.PI * 0.6) {
                                    p.state = 'recycle';
                                    p.vx = 0;
                                    p.vy = 2;
                                }
                                
                                // 冷却
                                p.temp -= 0.5;
                                if (p.temp < 25) p.temp = 25;
                            }
                        } else {
                            // 谷物不受磁力,直接抛物线下落
                            if (p.y > 600) {
                                isDead = true; // 收集完成
                            }
                        }
                        break;
                        
                    case 'recycle':
                        // 进入底部回收管道
                        if (p.y < 550 && p.x > 900) {
                            p.y += 4; // 掉入管道
                        } else if (p.y >= 550 && p.x > 150) {
                            p.y = 550;
                            p.x -= 6; // 在管道中向左移动
                            p.temp -= 0.2; // 继续冷却
                        } else if (p.x <= 150) {
                            p.x = 150;
                            p.y -= 6; // 提升回初始位置
                            if (p.y < 250) {
                                p.state = 'bed'; // 重新进入系统,模拟循环
                                p.vx = 2; p.vy = 0;
                            }
                        }
                        break;
                }

                if (isDead) {
                    p.destroy();
                    particles.splice(i, 1);
                } else {
                    p.updateDOM();
                }
            }

            // 更新蒸汽
            for (let i = steams.length - 1; i >= 0; i--) {
                if (steams[i].update()) {
                    steams[i].destroy();
                    steams.splice(i, 1);
                }
            }
        }

        function animate() {
            frame++;
            spawnParticles();
            updatePhysics();
            requestAnimationFrame(animate);
        }

        // 页面加载完成后直接启动,不需点击
        window.addEventListener('DOMContentLoaded', () => {
            // 初始化一些谷物在床内,避免初期太空
            for(let i=0; i<40; i++) {
                let p = new Particle(i%3==0?'ball':'grain', 300 + Math.random()*400, 350 + Math.random()*80);
                p.state = 'bed';
                particles.push(p);
            }
            animate();
        });

    </script>
</body>
</html>
积分规则:每轮对话扣10分