分享图
动画工坊
引擎就绪
<!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>
    <style>
        :root {
            --bg-color: #07090f;
            --grid-color: rgba(30, 58, 138, 0.2);
            --text-color: #94a3b8;
            --accent-cyan: #06b6d4;
            --accent-orange: #f59e0b;
            --metal-dark: #1e293b;
            --metal-light: #475569;
            --poly-lining: #84cc16;
        }

        body, html {
            margin: 0;
            padding: 0;
            width: 100%;
            height: 100%;
            background-color: var(--bg-color);
            background-image: 
                linear-gradient(var(--grid-color) 1px, transparent 1px),
                linear-gradient(90deg, var(--grid-color) 1px, transparent 1px);
            background-size: 30px 30px;
            font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
            overflow: hidden;
            display: flex;
            justify-content: center;
            align-items: center;
        }

        .animation-container {
            position: relative;
            width: 100vw;
            height: 100vh;
            max-width: 1200px;
            max-height: 900px;
            display: flex;
            justify-content: center;
            align-items: center;
        }

        svg {
            width: 100%;
            height: 100%;
            display: block;
        }

        /* 极简信息面板 - 严格限制尺寸和位置 */
        .hud-panel {
            position: absolute;
            background: rgba(7, 9, 15, 0.85);
            border: 1px solid rgba(6, 182, 212, 0.3);
            border-radius: 6px;
            padding: 12px 16px;
            pointer-events: none;
            box-shadow: 0 4px 20px rgba(0,0,0,0.5);
            backdrop-filter: blur(4px);
        }

        .hud-top-left {
            top: 30px;
            left: 30px;
        }

        .hud-bottom-right {
            bottom: 30px;
            right: 30px;
        }

        .hud-title {
            color: #f8fafc;
            font-size: 14px;
            font-weight: bold;
            margin-bottom: 8px;
            text-transform: uppercase;
            letter-spacing: 1px;
            border-bottom: 1px solid rgba(6, 182, 212, 0.3);
            padding-bottom: 4px;
        }

        .hud-list {
            margin: 0;
            padding: 0;
            list-style: none;
            font-size: 11px;
            color: var(--text-color);
            line-height: 1.6;
        }

        .hud-list li {
            display: flex;
            align-items: center;
            gap: 8px;
        }

        .legend-dot {
            width: 6px;
            height: 6px;
            border-radius: 50%;
            display: inline-block;
        }
        
        .dot-sand { background: var(--accent-orange); box-shadow: 0 0 5px var(--accent-orange); }
        .dot-liquid { background: var(--accent-cyan); box-shadow: 0 0 5px var(--accent-cyan); }

        .param-value {
            color: var(--accent-cyan);
            font-weight: bold;
        }

        /* SVG 内置动画辅助 */
        .flow-arrow {
            animation: dashMove 2s linear infinite;
        }

        @keyframes dashMove {
            from { stroke-dashoffset: 20; }
            to { stroke-dashoffset: 0; }
        }
    </style>
</head>
<body>

<div class="animation-container">
    <!-- 主体动画 SVG -->
    <svg viewBox="0 0 1000 800" preserveAspectRatio="xMidYMid meet" id="main-svg">
        <defs>
            <!-- 材质渐变定义 -->
            <linearGradient id="metal-pipe" x1="0%" y1="0%" x2="100%" y2="0%">
                <stop offset="0%" stop-color="#1e293b" />
                <stop offset="30%" stop-color="#475569" />
                <stop offset="70%" stop-color="#334155" />
                <stop offset="100%" stop-color="#0f172a" />
            </linearGradient>
            
            <linearGradient id="metal-cone-back" x1="0%" y1="0%" x2="100%" y2="0%">
                <stop offset="0%" stop-color="#0b0f19" />
                <stop offset="50%" stop-color="#1a2235" />
                <stop offset="100%" stop-color="#05070a" />
            </linearGradient>

            <linearGradient id="liquid-flow" x1="0%" y1="0%" x2="0%" y2="100%">
                <stop offset="0%" stop-color="rgba(6, 182, 212, 0.8)" />
                <stop offset="100%" stop-color="rgba(6, 182, 212, 0.1)" />
            </linearGradient>

            <!-- 发光滤镜 -->
            <filter id="glow-cyan" x="-20%" y="-20%" width="140%" height="140%">
                <feGaussianBlur stdDeviation="4" result="blur" />
                <feComposite in="SourceGraphic" in2="blur" operator="over" />
            </filter>
            
            <filter id="glow-orange" x="-20%" y="-20%" width="140%" height="140%">
                <feGaussianBlur stdDeviation="3" result="blur" />
                <feComposite in="SourceGraphic" in2="blur" operator="over" />
            </filter>
        </defs>

        <!-- 背景网格及标注线 -->
        <g stroke="rgba(148, 163, 184, 0.1)" stroke-width="1" fill="none">
            <line x1="500" y1="50" x2="500" y2="750" stroke-dasharray="5 5" />
            <line x1="200" y1="400" x2="800" y2="400" stroke-dasharray="5 5" />
        </g>

        <!-- 静态结构:后半部分 (背板) -->
        <g id="structure-back">
            <!-- 缓冲罐底部 -->
            <path d="M300,100 L700,100 L700,200 L300,200 Z" fill="url(#metal-pipe)" />
            <text x="310" y="120" fill="#64748b" font-size="10">缓冲罐底部</text>
            
            <!-- 进液管 -->
            <path d="M150,220 L350,220 L350,280 L150,280 Z" fill="url(#metal-pipe)" />
            
            <!-- 旋流锥体(内壁背侧) -->
            <path d="M300,200 L700,200 L580,550 L420,550 Z" fill="url(#metal-cone-back)" stroke="#1e293b" stroke-width="2"/>
            <!-- 聚氨酯衬里(背侧边缘) -->
            <path d="M305,205 L695,205 L576,548 L424,548 Z" fill="none" stroke="var(--poly-lining)" stroke-width="3" opacity="0.3"/>
            
            <!-- 沉砂收集包 -->
            <path d="M420,550 L580,550 L580,680 L420,680 Z" fill="url(#metal-pipe)" />
            <!-- 排砂阀体 -->
            <path d="M450,700 L550,700 L520,740 L480,740 Z" fill="#334155" stroke="#1e293b"/>
            <rect x="495" y="680" width="10" height="20" fill="#64748b" />
            <circle cx="500" cy="720" r="15" fill="#f43f5e" /> <!-- 手阀手轮 -->
            
            <!-- 溢流管(清液出口) 延伸至旋流器内部 -->
            <path d="M460,50 L540,50 L540,320 L460,320 Z" fill="#0f172a" stroke="#334155" stroke-width="2"/>
            <path d="M540,100 L850,100 L850,160 L540,160 Z" fill="url(#metal-pipe)" />
        </g>

        <!-- 粒子动画层容器 (由JS填充) -->
        <g id="particle-layer"></g>

        <!-- 静态结构:前半部分透视框 (剖面切割线) -->
        <g id="structure-front" pointer-events="none">
            <!-- 旋流器外壳剖面线 -->
            <path d="M300,200 L700,200 L580,550 L420,550 Z" fill="none" stroke="#475569" stroke-width="4" />
            <!-- 聚氨酯衬里剖面线 -->
            <path d="M308,205 L692,205 L572,545 L428,545 Z" fill="none" stroke="var(--poly-lining)" stroke-width="4" opacity="0.8"/>
            
            <!-- 溢流管前端虚线(表示在内部) -->
            <path d="M460,200 L460,320 M540,200 L540,320" fill="none" stroke="#64748b" stroke-width="2" stroke-dasharray="6 4" />
            <path d="M460,320 L540,320" fill="none" stroke="#64748b" stroke-width="2" stroke-dasharray="6 4" />

            <!-- 结构指示标签 (极简字号) -->
            <g font-size="10" fill="#94a3b8">
                <line x1="280" y1="250" x2="350" y2="250" stroke="#94a3b8" stroke-width="1" />
                <text x="210" y="245">含砂浆液入口</text>
                
                <line x1="720" y1="130" x2="800" y2="130" stroke="#94a3b8" stroke-width="1" />
                <text x="810" y="133">去放液管线(清液)</text>

                <line x1="610" y1="350" x2="680" y2="350" stroke="var(--poly-lining)" stroke-width="1" />
                <text x="690" y="353" fill="var(--poly-lining)">聚氨酯耐磨衬里</text>

                <line x1="600" y1="620" x2="680" y2="620" stroke="#94a3b8" stroke-width="1" />
                <text x="690" y="623">沉砂收集包</text>
            </g>
        </g>
    </svg>

    <!-- 左上角:原理与参数 -->
    <div class="hud-panel hud-top-left">
        <div class="hud-title">IFR设计: 旋流沉砂无源分离</div>
        <ul class="hud-list">
            <li><strong>核心矛盾破除:</strong>利用流体自身动能实现离心分离,无需动力部件。</li>
            <li><strong>旋流压力:</strong><span class="param-value">0.1 - 0.3 MPa</span></li>
            <li><strong>分离粒度:</strong><span class="param-value">0.1 ~ 2 mm</span></li>
            <li style="margin-top: 8px; color: #64748b;">(注: 粗砂沉降,中心清液上溢,保护下游阀门)</li>
        </ul>
    </div>

    <!-- 右下角:图例 -->
    <div class="hud-panel hud-bottom-right">
        <ul class="hud-list">
            <li><span class="legend-dot dot-sand"></span> 粗砂粒 (比重大,沿壁下沉)</li>
            <li><span class="legend-dot dot-liquid"></span> 澄清水 (比重小,中心溢流)</li>
        </ul>
    </div>
</div>

<script>
    /**
     * 高保真流体动力学粒子动画系统
     * 利用 SVG <circle> 和 requestAnimationFrame 模拟三维旋流效果
     */
    document.addEventListener("DOMContentLoaded", () => {
        const svgNS = "http://www.w3.org/2000/svg";
        const particleLayer = document.getElementById("particle-layer");
        
        // 动画参数配置
        const config = {
            sandCount: 250,      // 砂粒数量
            liquidCount: 300,    // 清液粒子数量
            centerX: 500,        // 旋流中心X
            coneTopY: 200,       // 锥体顶部Y
            coneBottomY: 550,    // 锥体底部Y
            coneTopR: 190,       // 锥体顶部半径 (内壁)
            coneBottomR: 70,     // 锥体底部半径
            pocketBottomY: 670,  // 收集包底部Y
            overflowBottomY: 320,// 溢流管底部Y
            overflowR: 35,       // 溢流管半径
            inletY: 250,         // 入口Y基准
            inletX: 150          // 入口X基准
        };

        const particles = [];

        class Particle {
            constructor(type) {
                this.type = type; // 'sand' | 'liquid'
                this.element = document.createElementNS(svgNS, "circle");
                
                if (this.type === 'sand') {
                    this.radius = Math.random() * 2 + 1.5;
                    this.element.setAttribute("fill", "var(--accent-orange)");
                    this.element.setAttribute("filter", "url(#glow-orange)");
                } else {
                    this.radius = Math.random() * 1.5 + 1;
                    this.element.setAttribute("fill", "var(--accent-cyan)");
                    this.element.setAttribute("opacity", Math.random() * 0.5 + 0.3);
                }
                
                this.element.setAttribute("r", this.radius);
                particleLayer.appendChild(this.element);
                
                this.reset();
                // 初始时随机分布在生命周期中,避免一起出现
                this.progress = Math.random();
                this.updatePosition();
            }

            reset() {
                this.state = 'inlet'; // inlet -> cyclone -> (pocket | overflow)
                this.progress = 0;
                
                // 入口参数
                this.x = config.inletX + Math.random() * 100;
                this.y = config.inletY - 20 + Math.random() * 40;
                this.speed = Math.random() * 0.005 + 0.005;
                
                // 旋流参数
                this.angle = Math.random() * Math.PI * 2;
                this.angularVelocity = Math.random() * 0.1 + 0.15; // 角速度
                
                // 在收集包中的沉积位置
                if (this.type === 'sand') {
                    this.settleX = config.centerX + (Math.random() - 0.5) * (config.coneBottomR * 2 - 10);
                    this.settleY = config.pocketBottomY - Math.random() * 30;
                }
            }

            updatePosition() {
                this.progress += this.speed;

                if (this.state === 'inlet') {
                    // 入口阶段:向右直线移动
                    this.x += (config.centerX - config.inletX) * this.speed * 2;
                    if (this.x > config.centerX - config.coneTopR + 20) {
                        this.state = 'cyclone';
                        this.progress = 0;
                        // 转换到极坐标
                        this.angle = Math.PI; // 从左侧进入
                        this.currentY = this.y;
                    }
                    this.element.setAttribute("cx", this.x);
                    this.element.setAttribute("cy", this.y);
                    this.element.setAttribute("opacity", 1);
                } 
                else if (this.state === 'cyclone') {
                    this.angle += this.angularVelocity;
                    
                    if (this.type === 'sand') {
                        // 砂粒:沿外壁向下螺旋
                        this.currentY += 2; // 下沉速度
                        
                        // 计算当前高度下的圆锥半径
                        let ratio = (this.currentY - config.coneTopY) / (config.coneBottomY - config.coneTopY);
                        ratio = Math.max(0, Math.min(1, ratio));
                        let currentR = config.coneTopR - ratio * (config.coneTopR - config.coneBottomR) - this.radius;

                        if (this.currentY >= config.coneBottomY) {
                            this.state = 'pocket';
                            this.progress = 0;
                        }

                        // 3D透视投影 (Y轴压缩)
                        let px = config.centerX + currentR * Math.cos(this.angle);
                        let py = this.currentY + currentR * 0.15 * Math.sin(this.angle);
                        
                        // 简单的深度排序(背面变暗变小)
                        if (Math.sin(this.angle) < 0) {
                            this.element.setAttribute("opacity", 0.3);
                        } else {
                            this.element.setAttribute("opacity", 1);
                        }

                        this.element.setAttribute("cx", px);
                        this.element.setAttribute("cy", py);

                    } else {
                        // 清液:先外旋下沉,再内旋上升
                        // 假设下沉到一定深度后被中心负压抽离
                        let depthThreshold = config.coneTopY + 200 + Math.random() * 100;
                        
                        if (this.currentY < depthThreshold && !this.rising) {
                            this.currentY += 1.5;
                            let ratio = (this.currentY - config.coneTopY) / (config.coneBottomY - config.coneTopY);
                            let currentR = (config.coneTopR - ratio * (config.coneTopR - config.coneBottomR)) * 0.8; // 在主涡流内层
                            
                            let px = config.centerX + currentR * Math.cos(this.angle);
                            let py = this.currentY + currentR * 0.15 * Math.sin(this.angle);
                            this.element.setAttribute("cx", px);
                            this.element.setAttribute("cy", py);
                        } else {
                            this.rising = true;
                            this.currentY -= 3; // 上升速度更快
                            
                            // 逐渐收缩到中心溢流管半径
                            let currentR = config.overflowR * 0.8;
                            
                            let px = config.centerX + currentR * Math.cos(this.angle * 2); // 内部转速更快
                            let py = this.currentY + currentR * 0.15 * Math.sin(this.angle * 2);

                            this.element.setAttribute("cx", px);
                            this.element.setAttribute("cy", py);

                            if (this.currentY < 130) {
                                this.state = 'overflow';
                                this.x = px;
                                this.y = py;
                            }
                        }
                    }
                }
                else if (this.state === 'pocket') {
                    // 砂粒落入收集包
                    if (this.currentY < this.settleY) {
                        this.currentY += 2;
                        this.element.setAttribute("cy", this.currentY);
                        // 稍微随机水平抖动模拟沉积
                        this.element.setAttribute("cx", config.centerX + (Math.random()-0.5)*5 + (this.settleX - config.centerX)*0.5);
                    } else {
                        // 停留一段时间后重置(模拟定期排砂排走)
                        this.progress += 0.005;
                        if (this.progress > 1) {
                            this.reset();
                        }
                    }
                }
                else if (this.state === 'overflow') {
                    // 清液向右流出
                    this.x += 4;
                    this.element.setAttribute("cx", this.x);
                    this.element.setAttribute("cy", this.y);
                    
                    if (this.x > 850) {
                        this.reset();
                        this.rising = false;
                    }
                }
            }
        }

        // 初始化粒子池
        for (let i = 0; i < config.sandCount; i++) {
            particles.push(new Particle('sand'));
        }
        for (let i = 0; i < config.liquidCount; i++) {
            particles.push(new Particle('liquid'));
        }

        // 核心渲染循环 - 自动播放无需交互
        function animate() {
            for (let i = 0; i < particles.length; i++) {
                particles[i].updatePosition();
            }
            requestAnimationFrame(animate);
        }

        // 启动动画
        animate();
    });
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>旋流沉砂分离结构 - TRIZ 最终理想解</title>
    <style>
        :root {
            --bg-color: #050814;
            --grid-color: rgba(0, 255, 255, 0.05);
            --cyan-glow: #00f0ff;
            --orange-glow: #ff5500;
            --green-pu: #39ff14;
            --panel-bg: rgba(5, 8, 20, 0.7);
            --border-tech: 1px solid rgba(0, 240, 255, 0.3);
            --text-main: #e0f7fa;
            --font-tech: 'Consolas', 'Courier New', monospace;
        }

        * {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
        }

        body {
            background-color: var(--bg-color);
            background-image: 
                linear-gradient(var(--grid-color) 1px, transparent 1px),
                linear-gradient(90deg, var(--grid-color) 1px, transparent 1px);
            background-size: 30px 30px;
            color: var(--text-main);
            font-family: var(--font-tech);
            height: 100vh;
            display: flex;
            justify-content: center;
            align-items: center;
            overflow: hidden;
        }

        /* 扫描线特效 */
        body::after {
            content: "";
            position: absolute;
            top: 0; left: 0; width: 100%; height: 100%;
            background: linear-gradient(rgba(18, 16, 16, 0) 50%, rgba(0, 0, 0, 0.25) 50%), linear-gradient(90deg, rgba(255, 0, 0, 0.06), rgba(0, 255, 0, 0.02), rgba(0, 0, 255, 0.06));
            background-size: 100% 4px, 3px 100%;
            pointer-events: none;
            z-index: 100;
        }

        .dashboard {
            position: relative;
            width: 100vw;
            height: 100vh;
            display: flex;
            justify-content: space-between;
        }

        .svg-container {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            z-index: 1;
            display: flex;
            justify-content: center;
            align-items: center;
        }

        svg {
            width: 100%;
            height: 100%;
            max-width: 1400px;
        }

        .hud-panel {
            position: relative;
            z-index: 10;
            width: 350px;
            margin: 30px;
            padding: 25px;
            background: var(--panel-bg);
            border: var(--border-tech);
            backdrop-filter: blur(8px);
            display: flex;
            flex-direction: column;
            gap: 20px;
            box-shadow: 0 0 20px rgba(0, 240, 255, 0.1) inset;
        }

        .hud-panel::before {
            content: '';
            position: absolute;
            top: -2px; left: -2px; width: 20px; height: 20px;
            border-top: 2px solid var(--cyan-glow);
            border-left: 2px solid var(--cyan-glow);
        }
        .hud-panel::after {
            content: '';
            position: absolute;
            bottom: -2px; right: -2px; width: 20px; height: 20px;
            border-bottom: 2px solid var(--cyan-glow);
            border-right: 2px solid var(--cyan-glow);
        }

        h1 {
            font-size: 1.4rem;
            color: var(--cyan-glow);
            text-shadow: 0 0 10px rgba(0, 240, 255, 0.5);
            text-transform: uppercase;
            letter-spacing: 2px;
            border-bottom: 1px solid var(--cyan-glow);
            padding-bottom: 10px;
        }

        h2 {
            font-size: 1rem;
            color: var(--orange-glow);
            margin-bottom: 5px;
        }

        .triz-box {
            background: rgba(255, 85, 0, 0.1);
            border-left: 3px solid var(--orange-glow);
            padding: 15px;
            font-size: 0.9rem;
            line-height: 1.6;
        }

        .highlight {
            color: var(--green-pu);
            font-weight: bold;
        }

        .controls {
            margin-top: auto;
            background: rgba(0, 240, 255, 0.05);
            padding: 15px;
            border: 1px solid rgba(0, 240, 255, 0.2);
        }

        .control-group {
            display: flex;
            flex-direction: column;
            gap: 10px;
        }

        .slider-header {
            display: flex;
            justify-content: space-between;
            font-size: 0.9rem;
            color: var(--cyan-glow);
        }

        input[type=range] {
            -webkit-appearance: none;
            width: 100%;
            background: transparent;
        }
        
        input[type=range]::-webkit-slider-thumb {
            -webkit-appearance: none;
            height: 16px;
            width: 16px;
            background: var(--cyan-glow);
            cursor: pointer;
            box-shadow: 0 0 10px var(--cyan-glow);
            border-radius: 50%;
            margin-top: -6px;
        }
        
        input[type=range]::-webkit-slider-runnable-track {
            width: 100%;
            height: 4px;
            cursor: pointer;
            background: rgba(0, 240, 255, 0.3);
            border-radius: 2px;
        }

        .param-list {
            list-style: none;
            font-size: 0.85rem;
            line-height: 2;
        }
        .param-list li {
            display: flex;
            justify-content: space-between;
            border-bottom: 1px dashed rgba(255,255,255,0.1);
        }
        .param-value {
            color: var(--cyan-glow);
        }

        /* SVG Styles */
        .tank-shell {
            fill: rgba(0, 50, 100, 0.2);
            stroke: var(--cyan-glow);
            stroke-width: 2;
            filter: drop-shadow(0 0 5px rgba(0, 240, 255, 0.5));
        }
        
        .pu-lining {
            fill: none;
            stroke: var(--green-pu);
            stroke-width: 4;
            stroke-dasharray: 8 4;
            filter: drop-shadow(0 0 6px var(--green-pu));
            animation: dash 20s linear infinite;
        }

        .pipe {
            fill: rgba(0, 50, 100, 0.4);
            stroke: var(--cyan-glow);
            stroke-width: 2;
        }

        .valve {
            fill: #334455;
            stroke: var(--cyan-glow);
            stroke-width: 2;
            transition: all 0.3s;
        }

        .sand-bag {
            fill: rgba(255, 85, 0, 0.1);
            stroke: var(--orange-glow);
            stroke-width: 2;
        }

        .sand-pile {
            fill: var(--orange-glow);
            opacity: 0.8;
            filter: drop-shadow(0 0 10px var(--orange-glow));
            animation: pulse-sand 4s infinite alternate;
        }

        .annotation-line {
            stroke: rgba(255, 255, 255, 0.5);
            stroke-width: 1.5;
            stroke-dasharray: 4 2;
            fill: none;
        }

        .annotation-text {
            fill: #fff;
            font-family: var(--font-tech);
            font-size: 14px;
            letter-spacing: 1px;
        }
        
        .annotation-point {
            fill: var(--cyan-glow);
        }

        @keyframes dash {
            to { stroke-dashoffset: 200; }
        }

        @keyframes pulse-sand {
            0% { opacity: 0.5; }
            100% { opacity: 0.9; }
        }
    </style>
</head>
<body>
    <div class="dashboard">
        <!-- 左侧面板:理论与信息 -->
        <aside class="hud-panel" style="margin-right: auto;">
            <h1>系统重构工程图</h1>
            
            <div class="triz-box">
                <h2>最终理想解 (IFR)</h2>
                <p>流体<span class="highlight">自行完成分离</span>,有害作用(粗砂磨损)自我消除,彻底保护下游蝶阀与管线。</p>
            </div>

            <div class="triz-box">
                <h2>资源利用率 (Resources)</h2>
                <p>巧妙利用含砂浆液自身的压力转化为<span class="highlight">旋流离心动能</span>,零额外动力部件,系统复杂度极低。</p>
            </div>

            <div class="triz-box" style="border-left-color: var(--cyan-glow); background: rgba(0,240,255,0.05);">
                <h2>工作机理</h2>
                <p>外侧粗砂受离心力沿锥斗壁面(聚氨酯耐磨层)螺旋下沉至收集包;中心清液形成上旋涡流,安全进入放液管线。</p>
            </div>
        </aside>

        <!-- 中央 SVG 动画区 -->
        <div class="svg-container">
            <svg viewBox="0 0 1200 800" id="main-svg">
                <defs>
                    <filter id="glow-cyan" x="-20%" y="-20%" width="140%" height="140%">
                        <feGaussianBlur stdDeviation="3" result="blur" />
                        <feComposite in="SourceGraphic" in2="blur" operator="over" />
                    </filter>
                    <filter id="glow-orange" x="-20%" y="-20%" width="140%" height="140%">
                        <feGaussianBlur stdDeviation="4" result="blur" />
                        <feComposite in="SourceGraphic" in2="blur" operator="over" />
                    </filter>
                    <linearGradient id="sand-grad" x1="0" y1="0" x2="0" y2="1">
                        <stop offset="0%" stop-color="#ff5500" stop-opacity="0.2"/>
                        <stop offset="100%" stop-color="#ff5500" stop-opacity="0.8"/>
                    </linearGradient>
                </defs>

                <g id="engineering-drawing">
                    <!-- 顶流出液管 (Overflow) -->
                    <rect x="560" y="80" width="80" height="220" class="pipe" />
                    
                    <!-- 进液管 (Inlet) -->
                    <rect x="250" y="220" width="230" height="80" class="pipe" />
                    
                    <!-- 旋流器主体 (Cyclone Body - Cylinder + Cone) -->
                    <polygon points="480,200 720,200 720,380 640,650 560,650 480,380" class="tank-shell" />
                    
                    <!-- 聚氨酯耐磨衬里 (PU Lining) -->
                    <polygon points="488,208 712,208 712,376 632,642 568,642 488,376" class="pu-lining" />
                    
                    <!-- 沉砂收集包 (Sand Bag) -->
                    <path d="M 540,710 L 660,710 L 640,780 L 560,780 Z" class="sand-bag" />
                    <!-- 堆积的粗砂 -->
                    <path d="M 545,740 L 655,740 L 640,780 L 560,780 Z" class="sand-pile" fill="url(#sand-grad)" />

                    <!-- 排砂手阀 (Manual Valve) -->
                    <g id="valve-group">
                        <rect x="560" y="650" width="80" height="60" class="pipe" />
                        <!-- 蝶阀抽象图形 -->
                        <polygon points="560,660 640,700 560,700 640,660" class="valve" />
                        <line x1="600" y1="680" x2="600" y2="640" stroke="var(--cyan-glow)" stroke-width="3" />
                        <rect x="580" y="635" width="40" height="10" fill="#ffaa00" />
                    </g>
                </g>

                <!-- 粒子动画层 -->
                <g id="particle-layer"></g>

                <!-- 标注系统 -->
                <g id="annotations">
                    <!-- 入口标注 -->
                    <polyline points="300,260 300,180 200,180" class="annotation-line" />
                    <circle cx="300" cy="260" r="3" class="annotation-point" />
                    <text x="190" y="175" class="annotation-text" text-anchor="end">含砂浆液入口 (0.1-0.3MPa)</text>
                    
                    <!-- 清液标注 -->
                    <polyline points="600,120 750,120 780,90" class="annotation-line" />
                    <circle cx="600" cy="120" r="3" class="annotation-point" />
                    <text x="785" y="85" class="annotation-text" fill="var(--cyan-glow)">顶流:中心清液上溢</text>

                    <!-- PU衬里标注 -->
                    <polyline points="700,300 800,300 830,270" class="annotation-line" />
                    <circle cx="700" cy="300" r="3" class="annotation-point" fill="var(--green-pu)" />
                    <text x="835" y="265" class="annotation-text" fill="var(--green-pu)">聚氨酯耐磨衬里层</text>
                    
                    <!-- 旋流分离标注 -->
                    <polyline points="650,450 780,450 810,480" class="annotation-line" />
                    <text x="815" y="485" class="annotation-text">旋流分离区 (粗砂沿壁下沉)</text>

                    <!-- 阀门标注 -->
                    <polyline points="640,680 750,680 780,650" class="annotation-line" />
                    <circle cx="640" cy="680" r="3" class="annotation-point" />
                    <text x="785" y="645" class="annotation-text">排砂手阀 (免受持续磨损)</text>

                    <!-- 收集包标注 -->
                    <polyline points="650,750 750,750 780,780" class="annotation-line" />
                    <circle cx="650" cy="750" r="3" class="annotation-point" fill="var(--orange-glow)" />
                    <text x="785" y="785" class="annotation-text" fill="var(--orange-glow)">沉砂收集包</text>
                </g>
            </svg>
        </div>

        <!-- 右侧面板:参数与控制 -->
        <aside class="hud-panel" style="margin-left: auto;">
            <h2>关键参数监控</h2>
            <ul class="param-list">
                <li><span>分离粒度范围</span> <span class="param-value">0.1 ~ 2mm</span></li>
                <li><span>衬里材质</span> <span class="param-value">Polyurethane (PU)</span></li>
                <li><span>适用工况</span> <span class="param-value">中等含砂量</span></li>
                <li><span>下游保护率</span> <span class="param-value" style="color: #39ff14;">> 98.5%</span></li>
            </ul>

            <div class="controls">
                <div class="control-group">
                    <div class="slider-header">
                        <span>旋流压力控制 (MPa)</span>
                        <span id="pressureValue">0.20</span>
                    </div>
                    <input type="range" id="pressureSlider" min="0.1" max="0.3" step="0.01" value="0.2">
                    <p style="font-size: 0.75rem; color: rgba(255,255,255,0.5); margin-top: 5px;">
                        * 调整压力观察分离效率与流速变化
                    </p>
                </div>
            </div>
        </aside>
    </div>

    <script>
        document.addEventListener('DOMContentLoaded', () => {
            const particleLayer = document.getElementById('particle-layer');
            const pressureSlider = document.getElementById('pressureSlider');
            const pressureValueDisplay = document.getElementById('pressureValue');
            
            // 基础配置
            const NUM_SAND = 90;
            const NUM_WATER = 90;
            const BASE_SPEED = 6; // 基础动画时长(秒)

            // 预生成路径数据
            function generateSpiralPath(startX, startY, endY, maxR, minR, loops, isInner = false) {
                let path = `M ${startX},${startY} `;
                // 如果是外围含砂流体,先有一段水平进入阶段
                if (!isInner) {
                    path = `M 250,260 L 480,260 `;
                }

                const steps = loops * 40;
                // 中心点 X 始终为 600
                const centerX = 600;
                
                for(let i=0; i<=steps; i++) {
                    let t = i/steps;
                    let y = isInner ? startY - (startY - endY) * t : startY + (endY - startY) * t;
                    let r = maxR - (maxR - minR) * t;
                    
                    // 利用余弦计算 X 坐标形成 2D 投影螺旋
                    // 入口后平滑过渡相位
                    let angleOffset = isInner ? 0 : Math.PI; 
                    let angle = angleOffset + t * Math.PI * 2 * loops * (isInner ? -1 : 1);
                    let x = centerX + Math.cos(angle) * r;
                    
                    path += `L ${x},${y} `;
                }
                return path;
            }

            // 生成外侧下降的含砂涡流路径 (Y: 260 -> 640)
            const outerPathStr = generateSpiralPath(480, 260, 640, 110, 30, 7, false);
            // 生成内侧上升的清液涡流路径 (Y: 600 -> 100)
            const innerPathStr = generateSpiralPath(600, 600, 100, 20, 20, 9, true);

            const allAnimations = [];

            function createParticle(type, pathStr, delay, duration) {
                const isSand = type === 'sand';
                const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
                
                // 属性设定
                circle.setAttribute('r', isSand ? (Math.random() * 2 + 1.5) : (Math.random() * 1.5 + 1));
                circle.setAttribute('fill', isSand ? (Math.random() > 0.5 ? '#ffaa00' : '#ff5500') : '#00f0ff');
                circle.setAttribute('filter', isSand ? 'url(#glow-orange)' : 'url(#glow-cyan)');
                circle.setAttribute('opacity', isSand ? '0.9' : '0.7');

                const animate = document.createElementNS('http://www.w3.org/2000/svg', 'animateMotion');
                animate.setAttribute('path', pathStr);
                animate.setAttribute('dur', `${duration}s`);
                animate.setAttribute('begin', `-${delay}s`);
                animate.setAttribute('repeatCount', 'indefinite');
                
                // 模拟 3D 深度淡入淡出(简单的透明度动画与运动同步)
                // 真实 SVG 中用 animateMotion 的同时很难精准同步透明度,这里靠密集度制造视觉错觉
                
                circle.appendChild(animate);
                particleLayer.appendChild(circle);
                allAnimations.push(animate);
            }

            // 初始化粒子系统
            function initParticles() {
                // 外层混合流/粗砂粒子
                for(let i=0; i<NUM_SAND; i++) {
                    const delay = Math.random() * BASE_SPEED;
                    // 外层速度稍慢
                    createParticle('sand', outerPathStr, delay, BASE_SPEED); 
                }
                // 内层纯净液粒子
                for(let i=0; i<NUM_WATER; i++) {
                    const delay = Math.random() * (BASE_SPEED * 0.7);
                    // 内部清液上升速度较快
                    createParticle('water', innerPathStr, delay, BASE_SPEED * 0.7);
                }
            }

            // 交互逻辑:调节压力改变流速
            pressureSlider.addEventListener('input', (e) => {
                const val = parseFloat(e.target.value);
                pressureValueDisplay.textContent = val.toFixed(2);
                
                // 压力越大 (0.3),动画越快 (duration越小)
                // 基础压力 0.2 对应 multiplier 1
                const speedMultiplier = 0.2 / val; 
                
                allAnimations.forEach((anim) => {
                    // 获取初始比例
                    const isWater = anim.parentElement.getAttribute('fill') === '#00f0ff';
                    const baseDur = isWater ? BASE_SPEED * 0.7 : BASE_SPEED;
                    const newDur = baseDur * speedMultiplier;
                    anim.setAttribute('dur', `${newDur}s`);
                });
            });

            // 启动动画
            initParticles();
        });
    </script>
</body>
</html>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>TRIZ 最终理想解 - 旋流沉砂分离结构原理动画</title>
    <style>
        :root {
            --bg-base: #020617;
            --bg-panel: rgba(15, 23, 42, 0.85);
            --border-color: #1e293b;
            --text-main: #f8fafc;
            --text-muted: #94a3b8;
            --accent-cyan: #22d3ee;
            --accent-orange: #f59e0b;
            --accent-green: #a3e635;
        }

        * {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
        }

        body {
            background-color: var(--bg-base);
            color: var(--text-main);
            font-family: system-ui, -apple-system, sans-serif;
            display: flex;
            height: 100vh;
            overflow: hidden;
        }

        /* 侧边控制面板 */
        .sidebar {
            width: 380px;
            background: var(--bg-panel);
            border-right: 1px solid var(--border-color);
            padding: 30px 24px;
            display: flex;
            flex-direction: column;
            gap: 28px;
            backdrop-filter: blur(12px);
            z-index: 10;
            box-shadow: 4px 0 24px rgba(0,0,0,0.5);
        }

        .header h1 {
            font-size: 22px;
            font-weight: 600;
            margin-bottom: 8px;
            background: linear-gradient(90deg, #fff, #94a3b8);
            -webkit-background-clip: text;
            -webkit-text-fill-color: transparent;
        }

        .header p {
            font-size: 13px;
            color: var(--accent-green);
            line-height: 1.5;
        }

        .panel-section {
            background: rgba(255, 255, 255, 0.03);
            border: 1px solid rgba(255, 255, 255, 0.05);
            border-radius: 8px;
            padding: 16px;
        }

        .panel-section h3 {
            font-size: 14px;
            text-transform: uppercase;
            letter-spacing: 1px;
            color: var(--text-muted);
            margin-bottom: 16px;
            display: flex;
            align-items: center;
            gap: 8px;
        }

        .panel-section h3::before {
            content: '';
            display: block;
            width: 4px;
            height: 14px;
            background: var(--accent-cyan);
            border-radius: 2px;
        }

        /* 状态指示器 */
        .status-indicator {
            display: flex;
            align-items: center;
            gap: 10px;
            font-size: 14px;
            font-family: monospace;
            background: rgba(0,0,0,0.3);
            padding: 10px;
            border-radius: 6px;
        }

        .status-dot {
            width: 10px;
            height: 10px;
            background: var(--accent-green);
            border-radius: 50%;
            box-shadow: 0 0 10px var(--accent-green);
            animation: pulse 2s infinite;
        }

        @keyframes pulse {
            0% { transform: scale(0.95); opacity: 0.8; }
            50% { transform: scale(1.1); opacity: 1; }
            100% { transform: scale(0.95); opacity: 0.8; }
        }

        /* 交互滑块 */
        .control-group {
            display: flex;
            flex-direction: column;
            gap: 12px;
        }

        .control-header {
            display: flex;
            justify-content: space-between;
            font-size: 14px;
        }

        .val-display {
            color: var(--accent-cyan);
            font-family: monospace;
            font-weight: bold;
            font-size: 16px;
        }

        input[type=range] {
            -webkit-appearance: none;
            width: 100%;
            background: #1e293b;
            height: 6px;
            border-radius: 3px;
            outline: none;
        }

        input[type=range]::-webkit-slider-thumb {
            -webkit-appearance: none;
            width: 18px;
            height: 18px;
            border-radius: 50%;
            background: var(--accent-cyan);
            cursor: pointer;
            box-shadow: 0 0 12px var(--accent-cyan);
            transition: transform 0.1s;
        }

        input[type=range]::-webkit-slider-thumb:hover {
            transform: scale(1.2);
        }

        .helper-text {
            font-size: 12px;
            color: var(--text-muted);
            line-height: 1.5;
        }

        /* TRIZ 列表 */
        .triz-list {
            list-style: none;
            display: flex;
            flex-direction: column;
            gap: 12px;
        }

        .triz-list li {
            font-size: 13px;
            line-height: 1.6;
            color: #cbd5e1;
            padding-left: 16px;
            position: relative;
        }

        .triz-list li::before {
            content: '▹';
            position: absolute;
            left: 0;
            color: var(--accent-orange);
        }

        .triz-list strong {
            color: #fff;
        }

        /* 主视图区 */
        .main-view {
            flex: 1;
            display: flex;
            justify-content: center;
            align-items: center;
            position: relative;
            background: radial-gradient(circle at center, #0f172a 0%, #020617 100%);
        }

        /* 科技感背景网格 */
        .grid-bg {
            position: absolute;
            inset: 0;
            background-image: 
                linear-gradient(rgba(255,255,255,0.03) 1px, transparent 1px),
                linear-gradient(90deg, rgba(255,255,255,0.03) 1px, transparent 1px);
            background-size: 40px 40px;
            z-index: 1;
            pointer-events: none;
        }

        .svg-container {
            position: relative;
            z-index: 2;
            width: 100%;
            height: 100%;
            max-width: 1100px;
            max-height: 900px;
            display: flex;
            justify-content: center;
            align-items: center;
        }

        svg {
            width: 100%;
            height: 100%;
            filter: drop-shadow(0 20px 30px rgba(0,0,0,0.5));
        }

        .legend {
            position: absolute;
            bottom: 30px;
            right: 40px;
            background: rgba(15, 23, 42, 0.7);
            border: 1px solid var(--border-color);
            padding: 12px 16px;
            border-radius: 8px;
            display: flex;
            gap: 20px;
            z-index: 10;
        }

        .legend-item {
            display: flex;
            align-items: center;
            gap: 8px;
            font-size: 13px;
        }

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

        .legend-color.sand { background: var(--accent-orange); box-shadow: 0 0 8px var(--accent-orange); }
        .legend-color.liquid { background: var(--accent-cyan); box-shadow: 0 0 8px var(--accent-cyan); }

    </style>
</head>
<body>

    <aside class="sidebar">
        <header class="header">
            <h1>旋流沉砂分离系统</h1>
            <p>TRIZ IFR 最终理想解动态演示</p>
        </header>

        <div class="panel-section">
            <h3>运行状态</h3>
            <div class="status-indicator">
                <div class="status-dot"></div>
                <span>自动分离中 (流体驱动)</span>
            </div>
        </div>

        <div class="panel-section">
            <h3>核心参数控制 (交互)</h3>
            <div class="control-group">
                <div class="control-header">
                    <span>旋流进料压力</span>
                    <span class="val-display" id="pressure-val">0.20 MPa</span>
                </div>
                <input type="range" id="pressure-slider" min="0.1" max="0.3" step="0.01" value="0.2">
                <div class="helper-text">
                    * 手动调节滑动条。压力越大,系统动能转化越快,沉砂分离的离心转速越高,分离越彻底。
                </div>
                <div class="helper-text" style="color:var(--text-muted); margin-top:4px;">
                    * 适用分离粒度:0.1~2mm
                </div>
            </div>
        </div>

        <div class="panel-section">
            <h3>TRIZ 原理解析</h3>
            <ul class="triz-list">
                <li><strong>最终理想解 (IFR):</strong> 不增加外部动力过滤设备,通过巧妙的结构构型让混合流体“自己”完成分离,根除下游阀门磨损矛盾。</li>
                <li><strong>有效利用资源:</strong> 直接利用原进料流体自带的压能(0.1-0.3MPa)转化为分离所需的强大离心力。</li>
                <li><strong>局部质量原则:</strong> 仅在最高磨损区(旋流锥斗内壁)增加聚氨酯衬里,实现低成本高可靠性防护。</li>
            </ul>
        </div>
    </aside>

    <main class="main-view">
        <div class="grid-bg"></div>
        
        <div class="legend">
            <div class="legend-item">
                <div class="legend-color sand"></div>
                <span>粗砂及杂质 (下沉)</span>
            </div>
            <div class="legend-item">
                <div class="legend-color liquid"></div>
                <span>上溢清液 (去放液管线)</span>
            </div>
        </div>

        <div class="svg-container">
            <!-- 视口设置足够大以容纳主体及标注,居中对齐 -->
            <svg id="cyclone-svg" viewBox="0 0 1000 800" preserveAspectRatio="xMidYMid meet">
                <defs>
                    <linearGradient id="metalGrad" x1="0%" y1="0%" x2="100%" y2="0%">
                        <stop offset="0%" stop-color="#334155" />
                        <stop offset="50%" stop-color="#64748b" />
                        <stop offset="100%" stop-color="#334155" />
                    </linearGradient>
                    
                    <linearGradient id="coneGrad" x1="0%" y1="0%" x2="100%" y2="0%">
                        <stop offset="0%" stop-color="rgba(30, 41, 59, 0.8)" />
                        <stop offset="50%" stop-color="rgba(71, 85, 105, 0.4)" />
                        <stop offset="100%" stop-color="rgba(30, 41, 59, 0.8)" />
                    </linearGradient>

                    <marker id="arrow-green" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="6" markerHeight="6" orient="auto">
                        <path d="M 0 0 L 10 5 L 0 10 z" fill="#a3e635" />
                    </marker>
                    
                    <!-- 辉光滤镜,用于静态标注的高亮 -->
                    <filter id="glow-text">
                        <feGaussianBlur stdDeviation="2" result="blur" />
                        <feMerge>
                            <feMergeNode in="blur"/>
                            <feMergeNode in="SourceGraphic"/>
                        </feMerge>
                    </filter>
                </defs>

                <!-- 1. 背景环境:现有缓冲罐轮廓 (虚线,表示不增加系统复杂度的原有资源) -->
                <g class="environment" opacity="0.4">
                    <path d="M 150 100 L 150 500 L 500 850 L 850 500 L 850 100" fill="none" stroke="#475569" stroke-width="2" stroke-dasharray="10 6" />
                    <text x="170" y="130" fill="#94a3b8" font-size="16" letter-spacing="2">缓冲罐主体边界 (现有空间)</text>
                </g>

                <!-- 2. 管道与外部结构 (静态底图层) -->
                <g class="infrastructure">
                    <!-- 进料管 -->
                    <path d="M 80 400 L 400 400 L 400 450 L 80 450 Z" fill="url(#metalGrad)" stroke="#1e293b" stroke-width="2"/>
                    <!-- 顶部溢流清液管线 (延伸至右侧放液管线) -->
                    <path d="M 470 450 L 530 450 L 530 250 L 900 250 L 900 310 L 470 310 Z" fill="url(#metalGrad)" stroke="#1e293b" stroke-width="2"/>
                </g>

                <!-- 3. 旋流沉砂结构主体 (后半部分或透明外壳) -->
                <g class="cyclone-body">
                    <!-- 圆柱段 -->
                    <path d="M 400 380 L 600 380 L 600 480 L 400 480 Z" fill="url(#coneGrad)" stroke="#64748b" stroke-width="2"/>
                    <!-- 锥形段 -->
                    <path d="M 400 480 L 600 480 L 525 640 L 475 640 Z" fill="url(#coneGrad)" stroke="#64748b" stroke-width="2"/>
                    
                    <!-- 聚氨酯耐磨衬里 (内部局部强化高亮) -->
                    <path d="M 406 480 L 594 480 L 521 636 L 479 636 Z" fill="none" stroke="#f59e0b" stroke-width="4" stroke-linejoin="round"/>
                </g>

                <!-- 4. 底部沉砂收集与排砂系统 -->
                <g class="bottom-system">
                    <!-- 法兰接口 -->
                    <rect x="465" y="640" width="70" height="15" fill="#475569" rx="2"/>
                    
                    <!-- 排砂手阀 -->
                    <!-- 阀体蝴蝶结形状 -->
                    <path d="M 460 655 L 510 690 M 510 655 L 460 690" fill="none" stroke="#94a3b8" stroke-width="6"/>
                    <circle cx="485" cy="672.5" r="5" fill="#cbd5e1"/>
                    <!-- 阀杆与手轮 -->
                    <line x1="485" y1="672.5" x2="540" y2="672.5" stroke="#cbd5e1" stroke-width="4"/>
                    <rect x="535" y="652.5" width="8" height="40" fill="#ef4444" rx="4"/>
                    
                    <!-- 沉砂收集包 -->
                    <path d="M 450 690 L 520 690 L 535 780 L 435 780 Z" fill="rgba(15, 23, 42, 0.95)" stroke="#64748b" stroke-width="3" stroke-linejoin="round"/>
                </g>

                <!-- 5. 动态粒子层 (由 JS 动态生成控制,展示流体与砂粒轨迹) -->
                <g id="particle-layer"></g>

                <!-- 6. 视觉引导标注层 (位于最上层,明确解释 TRIZ 特征) -->
                <g class="annotations">
                    <!-- 进料动能标注 -->
                    <path d="M 200 370 L 250 310 L 380 310" fill="none" stroke="#a3e635" stroke-width="1.5" stroke-dasharray="4 4"/>
                    <circle cx="200" cy="370" r="4" fill="#a3e635"/>
                    <text x="390" y="315" fill="#a3e635" font-size="15" font-weight="bold" filter="url(#glow-text)">含砂浆液进料 (利用自带压能)</text>
                    <path d="M 120 425 L 180 425" fill="none" stroke="#a3e635" stroke-width="3" marker-end="url(#arrow-green)"/>

                    <!-- IFR 离心分离标注 -->
                    <path d="M 610 430 L 680 430" fill="none" stroke="#f8fafc" stroke-width="1.5"/>
                    <circle cx="610" cy="430" r="4" fill="#f8fafc"/>
                    <text x="690" y="435" fill="#f8fafc" font-size="15" font-weight="bold">IFR 状态:流体动能转化为离心力</text>
                    <text x="690" y="455" fill="#94a3b8" font-size="13">粗砂被甩向外壁,避免引入复杂设备</text>

                    <!-- 聚氨酯衬里标注 -->
                    <path d="M 570 550 L 660 550 L 680 580" fill="none" stroke="#f59e0b" stroke-width="1.5"/>
                    <circle cx="570" cy="550" r="4" fill="#f59e0b"/>
                    <text x="690" y="585" fill="#f59e0b" font-size="15" font-weight="bold">聚氨酯衬里 (橙色边界)</text>
                    <text x="690" y="605" fill="#94a3b8" font-size="13">局部强化原则:抵御高浓度粗砂磨损</text>

                    <!-- 收集包标注 -->
                    <path d="M 545 740 L 630 740" fill="none" stroke="#cbd5e1" stroke-width="1.5"/>
                    <circle cx="535" cy="740" r="4" fill="#cbd5e1"/>
                    <text x="640" y="735" fill="#f8fafc" font-size="15" font-weight="bold">沉砂收集包 & 排砂手阀</text>
                    <text x="640" y="755" fill="#94a3b8" font-size="13">集中收集,定期排空</text>

                    <!-- 清液上溢标注 -->
                    <path d="M 720 250 L 720 200 L 750 200" fill="none" stroke="#22d3ee" stroke-width="1.5"/>
                    <circle cx="720" cy="250" r="4" fill="#22d3ee"/>
                    <text x="760" y="205" fill="#22d3ee" font-size="15" font-weight="bold" filter="url(#glow-text)">上溢清液至放液管线</text>
                    <text x="760" y="225" fill="#94a3b8" font-size="13">彻底消除后端蝶阀与管道的堵塞磨损</text>
                </g>
            </svg>
        </div>
    </main>

    <script>
        document.addEventListener('DOMContentLoaded', () => {
            const particleLayer = document.getElementById('particle-layer');
            const slider = document.getElementById('pressure-slider');
            const valDisplay = document.getElementById('pressure-val');
            
            let particles = [];
            // 为保证动画流畅并充分展示效果,设置合理的粒子密度
            const MAX_PARTICLES = 250; 
            const CENTER_X = 500;
            
            // 创建单个粒子并初始化其状态
            function spawnParticle() {
                // 30%的概率生成粗砂(橙色),70%生成清液(青色)
                const isSand = Math.random() > 0.7;
                const el = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
                
                const p = {
                    el,
                    type: isSand ? 'sand' : 'liquid',
                    phase: 'inlet', // 生命周期阶段:inlet -> cyclone-down -> (sand: bag) / (liquid: cyclone-up -> overflow)
                    x: 60 + Math.random() * 40,
                    y: 405 + Math.random() * 40,
                    angle: Math.PI, 
                    life: 1,
                    radius: isSand ? 3.5 : 2.5
                };

                el.setAttribute('r', p.radius);
                el.setAttribute('fill', isSand ? '#f59e0b' : '#22d3ee');
                
                // 为了性能,仅给少量粗砂加阴影,其余靠纯色对比
                if (isSand && Math.random() > 0.5) {
                    el.style.filter = 'drop-shadow(0 0 3px #f59e0b)';
                }

                particleLayer.appendChild(el);
                particles.push(p);
            }

            // 核心动画循环
            function animate() {
                // 交互性:获取滑块压力值,计算动画速度倍率
                const pressure = parseFloat(slider.value);
                valDisplay.innerText = pressure.toFixed(2) + ' MPa';
                
                // 0.1MPa 对应 1x 速度, 0.3MPa 对应 2.5x 速度,体现压力对动能转换的影响
                const speed = 1 + (pressure - 0.1) * 7.5; 

                particles.forEach(p => {
                    // 1. 直管进料阶段
                    if (p.phase === 'inlet') {
                        p.x += 4 * speed;
                        if (p.x >= 420) {
                            p.phase = 'cyclone-down';
                            p.x = 420; // 锁定进入旋流器的切入点
                        }
                        p.el.setAttribute('cx', p.x);
                        p.el.setAttribute('cy', p.y);
                    } 
                    // 2. 旋流下降阶段 (沿壁贴切下旋)
                    else if (p.phase === 'cyclone-down') {
                        // 旋流角速度和下降速度
                        p.angle -= 0.12 * speed; 
                        p.y += 1.2 * speed;

                        // 计算当前高度对应的锥斗半径 (实现漏斗形下旋)
                        let currentRadius = 90; // 上部圆柱区半径
                        if (p.y > 480) {
                            // 锥形区半径递减,高度480~640, 半径从90降到25
                            let progress = (p.y - 480) / 160;
                            currentRadius = 90 - (65 * progress);
                        }

                        // 将 3D 螺旋投影到 2D 视角,加上垂直倾角产生立体感
                        let vx = CENTER_X + Math.cos(p.angle) * currentRadius;
                        let vy = p.y + Math.sin(p.angle) * currentRadius * 0.25;

                        // 根据螺旋角度调整透明度,模拟粒子在筒体前后的遮挡效果 (IFR视觉暗示)
                        let isFront = Math.sin(p.angle) > 0;
                        p.el.setAttribute('opacity', isFront ? '1' : '0.2');

                        p.el.setAttribute('cx', vx);
                        p.el.setAttribute('cy', vy);

                        // 粗砂落入收集包
                        if (p.type === 'sand' && p.y > 640) {
                            p.phase = 'bag';
                        }
                        // 清液在下旋一段距离后转向中心上升
                        if (p.type === 'liquid' && p.y > 520 + Math.random() * 80) {
                            p.phase = 'cyclone-up';
                        }
                    } 
                    // 3. 中心清液上升阶段 (内旋流)
                    else if (p.phase === 'cyclone-up') {
                        p.angle -= 0.25 * speed; // 内旋流转速更快
                        p.y -= 2 * speed;
                        
                        // 中心涡流半径较小
                        let currentRadius = 15 + Math.random() * 10;
                        
                        let vx = CENTER_X + Math.cos(p.angle) * currentRadius;
                        let vy = p.y + Math.sin(p.angle) * currentRadius * 0.25;

                        p.el.setAttribute('opacity', '0.9'); // 中心液流无遮挡
                        p.el.setAttribute('cx', vx);
                        p.el.setAttribute('cy', vy);

                        // 到达顶部溢流口
                        if (p.y < 300 + Math.random() * 20) {
                            p.phase = 'overflow';
                        }
                    } 
                    // 4. 清液溢出阶段 (去管线)
                    else if (p.phase === 'overflow') {
                        if (!p.outX) p.outX = CENTER_X;
                        p.outX += 4 * speed;
                        p.el.setAttribute('cx', p.outX);
                        p.el.setAttribute('cy', p.y);
                        
                        // 飞出屏幕右侧后销毁
                        if (p.outX > 950) p.life = 0;
                    } 
                    // 5. 沉砂收集阶段 (沉降到底部收集包)
                    else if (p.phase === 'bag') {
                        if (!p.bagY) p.bagY = 640;
                        p.bagY += 1.5 * speed;
                        
                        // 在收集包内随机散落
                        p.el.setAttribute('cx', CENTER_X + (Math.random() - 0.5) * 60);
                        p.el.setAttribute('cy', p.bagY);
                        p.el.setAttribute('opacity', p.life);

                        // 堆积并逐渐淡出 (防止 DOM 节点无限增加)
                        if (p.bagY > 730 + Math.random() * 30) {
                            p.life -= 0.02 * speed;
                        }
                    }
                });

                // 垃圾回收:清理生命周期结束的粒子
                for (let i = particles.length - 1; i >= 0; i--) {
                    if (particles[i].life <= 0) {
                        particleLayer.removeChild(particles[i].el);
                        particles.splice(i, 1);
                    }
                }

                // 根据压力动态调整粒子生成密度
                const targetCount = Math.floor(MAX_PARTICLES * (speed / 1.5));
                if (particles.length < targetCount) {
                    // 每帧补充粒子
                    let spawnCount = Math.ceil(1.5 * speed);
                    for (let i = 0; i < spawnCount; i++) {
                        spawnParticle();
                    }
                }

                // 持续请求下一帧,实现自动播放与流畅动画
                requestAnimationFrame(animate);
            }

            // 启动动画 (满足重开即播、自动播放要求)
            animate();
        });
    </script>
</body>
</html>
<!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>
    <style>
        :root {
            --bg-dark: #0f172a;
            --panel-bg: #1e293b;
            --border-color: #334155;
            --text-main: #f8fafc;
            --text-muted: #94a3b8;
            --accent-blue: #38bdf8;
            --accent-sand: #fcd34d;
            --success: #10b981;
            --danger: #ef4444;
        }

        * {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
        }

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

        .container {
            display: flex;
            width: 100%;
            height: 100%;
            max-width: 1400px;
        }

        /* Sidebar Control Panel */
        .sidebar {
            width: 360px;
            background: var(--panel-bg);
            border-right: 1px solid var(--border-color);
            padding: 2rem;
            display: flex;
            flex-direction: column;
            gap: 2rem;
            box-shadow: 4px 0 20px rgba(0, 0, 0, 0.4);
            z-index: 10;
            overflow-y: auto;
        }

        .header h2 {
            font-size: 1.5rem;
            font-weight: 700;
            margin-bottom: 0.75rem;
            letter-spacing: 0.5px;
        }

        .badge-ifr {
            display: inline-block;
            background: rgba(16, 185, 129, 0.15);
            color: var(--success);
            padding: 0.3rem 0.75rem;
            border-radius: 6px;
            font-size: 0.85rem;
            font-weight: 600;
            border: 1px solid rgba(16, 185, 129, 0.3);
        }

        .info-card {
            background: rgba(15, 23, 42, 0.6);
            padding: 1.25rem;
            border-radius: 8px;
            border-left: 4px solid var(--accent-blue);
            font-size: 0.9rem;
            line-height: 1.6;
            color: var(--text-muted);
            box-shadow: inset 0 2px 10px rgba(0,0,0,0.1);
        }

        .info-card strong {
            color: var(--text-main);
        }

        .info-card p {
            margin-bottom: 0.75rem;
        }
        .info-card p:last-child {
            margin-bottom: 0;
        }

        /* Controls */
        .controls-section {
            display: flex;
            flex-direction: column;
            gap: 1.5rem;
        }

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

        .control-header {
            display: flex;
            justify-content: space-between;
            font-size: 0.9rem;
            color: var(--text-muted);
            font-weight: 500;
        }

        .val-display {
            color: var(--accent-blue);
            font-family: monospace;
            font-size: 1rem;
        }

        input[type=range] {
            -webkit-appearance: none;
            width: 100%;
            background: transparent;
        }

        input[type=range]::-webkit-slider-thumb {
            -webkit-appearance: none;
            height: 16px;
            width: 16px;
            border-radius: 50%;
            background: var(--accent-blue);
            cursor: pointer;
            margin-top: -6px;
            box-shadow: 0 0 10px rgba(56, 189, 248, 0.5);
            transition: transform 0.1s;
        }

        input[type=range]::-webkit-slider-thumb:hover {
            transform: scale(1.2);
        }

        input[type=range]::-webkit-slider-runnable-track {
            width: 100%;
            height: 4px;
            cursor: pointer;
            background: var(--border-color);
            border-radius: 2px;
        }

        .btn {
            background: transparent;
            color: var(--text-main);
            border: 1px solid var(--border-color);
            padding: 0.85rem;
            border-radius: 6px;
            font-size: 0.95rem;
            font-weight: 600;
            cursor: pointer;
            transition: all 0.3s ease;
            text-align: center;
        }

        .btn:hover {
            background: rgba(51, 65, 85, 0.5);
        }

        .btn.active {
            background: rgba(239, 68, 68, 0.15);
            color: var(--danger);
            border-color: rgba(239, 68, 68, 0.4);
            box-shadow: inset 0 0 15px rgba(239, 68, 68, 0.1);
        }

        /* Legend */
        .legend {
            margin-top: auto;
            display: flex;
            flex-direction: column;
            gap: 0.75rem;
            padding-top: 1.5rem;
            border-top: 1px solid var(--border-color);
        }

        .legend-item {
            display: flex;
            align-items: center;
            gap: 0.75rem;
            font-size: 0.85rem;
            color: var(--text-muted);
        }

        .dot {
            width: 12px;
            height: 12px;
            border-radius: 50%;
            display: inline-block;
        }
        .dot.sand { background: var(--accent-sand); box-shadow: 0 0 8px var(--accent-sand); }
        .dot.liquid { background: var(--accent-blue); box-shadow: 0 0 8px var(--accent-blue); }

        /* Main SVG Area */
        .main-view {
            flex: 1;
            position: relative;
            display: flex;
            align-items: center;
            justify-content: center;
            background: radial-gradient(circle at center, #162032 0%, var(--bg-dark) 100%);
        }

        svg {
            width: 100%;
            height: 100%;
            max-width: 900px;
            max-height: 900px;
        }

        /* Responsive */
        @media (max-width: 900px) {
            .container { flex-direction: column; }
            .sidebar { width: 100%; height: auto; max-height: 40vh; border-right: none; border-bottom: 1px solid var(--border-color); }
        }
    </style>
</head>
<body>

<div class="container">
    <aside class="sidebar">
        <div class="header">
            <h2>旋流沉砂分离结构</h2>
            <div class="badge-ifr">TRIZ 最终理想解 (IFR)</div>
        </div>
        
        <div class="info-card">
            <p><strong>核心机理:</strong>引入水力旋流原理,利用流体自身动能使其发生离心分离。粗砂沿壁下沉,中心清液上溢。</p>
            <p><strong>化解矛盾:</strong>无需增加复杂的机械过滤部件(极高资源利用率),彻底阻断了粗砂对下游放液管线与蝶阀的磨损与堵塞。</p>
        </div>

        <div class="controls-section">
            <div class="control-group">
                <div class="control-header">
                    <span>系统旋流压力</span>
                    <span class="val-display" id="pressureVal">0.20 MPa</span>
                </div>
                <input type="range" id="pressureSlider" min="0.1" max="0.3" step="0.01" value="0.2">
            </div>

            <div class="control-group">
                <div class="control-header">
                    <span>浆液含砂比例模拟</span>
                    <span class="val-display" id="sandVal">中等</span>
                </div>
                <input type="range" id="sandSlider" min="0.1" max="0.8" step="0.1" value="0.4">
            </div>

            <button id="dischargeBtn" class="btn">手动开启排砂手阀</button>
        </div>

        <div class="legend">
            <div class="legend-item"><span class="dot sand"></span> 分离沉砂 (粒度 0.1~2mm)</div>
            <div class="legend-item"><span class="dot liquid"></span> 中心溢流清液 (无磨损)</div>
        </div>
    </aside>

    <main class="main-view">
        <svg viewBox="0 0 800 800" preserveAspectRatio="xMidYMid meet" xmlns="http://www.w3.org/2000/svg">
            <defs>
                <!-- Background Grid -->
                <pattern id="grid" width="40" height="40" patternUnits="userSpaceOnUse">
                    <path d="M 40 0 L 0 0 0 40" fill="none" stroke="#1e293b" stroke-width="1"/>
                </pattern>
                
                <!-- Gradients for structural pipes -->
                <linearGradient id="pipeH" x1="0%" y1="0%" x2="0%" y2="100%">
                    <stop offset="0%" stop-color="#1e293b" />
                    <stop offset="50%" stop-color="#334155" />
                    <stop offset="100%" stop-color="#1e293b" />
                </linearGradient>
                <linearGradient id="pipeV" x1="0%" y1="0%" x2="100%" y2="0%">
                    <stop offset="0%" stop-color="#1e293b" />
                    <stop offset="50%" stop-color="#334155" />
                    <stop offset="100%" stop-color="#1e293b" />
                </linearGradient>
            </defs>

            <!-- Background -->
            <rect width="100%" height="100%" fill="url(#grid)" />

            <!-- Highlight Banner -->
            <g transform="translate(480, 40)">
                <rect x="0" y="0" width="280" height="46" rx="6" fill="rgba(16, 185, 129, 0.1)" stroke="#10b981" stroke-width="1.5"/>
                <text x="140" y="28" fill="#10b981" font-size="15" font-weight="bold" text-anchor="middle">无源分离 · 消除系统磨损矛盾</text>
            </g>

            <!-- Particles Layer (Behind foreground glass but in front of background) -->
            <g id="particles-layer"></g>

            <!-- ================= STRUCTURAL COMPONENTS ================= -->
            
            <!-- Inlet Pipe (Left) -->
            <path d="M100 210 L300 210 L300 270 L100 270 Z" fill="url(#pipeH)" stroke="#475569" stroke-width="2"/>
            <path d="M100 150 C100 180 120 210 200 210" fill="none" stroke="#475569" stroke-width="2" stroke-dasharray="6,4"/>
            
            <!-- Inner Vortex Finder (Clear Liquid Exit) -->
            <path d="M380 270 L380 150 L680 150 L680 110 L420 110 L420 270 Z" fill="rgba(15, 23, 42, 0.8)" stroke="#475569" stroke-width="2"/>
            
            <!-- Main Cyclone Outer Shell (Semi-transparent Glassy Look) -->
            <path d="M300 200 L500 200 L500 300 L430 550 L430 650 L370 650 L370 550 L300 300 Z" fill="rgba(30, 41, 59, 0.5)" stroke="#64748b" stroke-width="3" stroke-linejoin="round"/>
            
            <!-- Polyurethane Lining Indicator (Inner edge) -->
            <path d="M304 302 L372 548 M496 302 L428 548" stroke="#f59e0b" stroke-width="4" fill="none" opacity="0.85" stroke-linecap="round"/>
            
            <!-- Sand Discharge Pipe (Bottom) -->
            <path d="M390 650 L410 650 L410 760 L390 760 Z" fill="url(#pipeV)" stroke="#475569" stroke-width="2"/>

            <!-- ================= VALVES ================= -->
            
            <!-- Manual Sand Discharge Valve (Bottom) -->
            <g transform="translate(400, 690)">
                <path d="M-20 -15 L20 10 L-20 10 L20 -15 Z" fill="#475569" stroke="#94a3b8" stroke-width="2" stroke-linejoin="round"/>
                <line x1="20" y1="-2.5" x2="45" y2="-2.5" stroke="#94a3b8" stroke-width="4" stroke-linecap="round"/>
                <!-- Animated Handwheel -->
                <g id="valveWheel" style="transform-origin: 45px -2.5px; transition: transform 0.6s cubic-bezier(0.4, 0, 0.2, 1);">
                    <ellipse cx="45" cy="-2.5" rx="5" ry="22" fill="#ef4444" stroke="#b91c1c" stroke-width="2" />
                    <line x1="45" y1="-22" x2="45" y2="17" stroke="#b91c1c" stroke-width="2"/>
                </g>
            </g>

            <!-- Protected Butterfly Valve (Right Clear Liquid Line) -->
            <g transform="translate(560, 130)">
                <!-- Flanges -->
                <rect x="-22" y="-28" width="6" height="56" fill="#334155" stroke="#94a3b8" stroke-width="1.5" rx="1"/>
                <rect x="16" y="-28" width="6" height="56" fill="#334155" stroke="#94a3b8" stroke-width="1.5" rx="1"/>
                <!-- Valve Body -->
                <rect x="-16" y="-22" width="32" height="44" fill="#1e293b" stroke="#94a3b8" stroke-width="1.5" />
                <!-- Stem & Actuator -->
                <rect x="-4" y="-45" width="8" height="23" fill="#94a3b8" />
                <rect x="-18" y="-55" width="36" height="10" fill="#f59e0b" stroke="#b45309" stroke-width="1.5" rx="2"/>
                <!-- Internal Disc (Open State) -->
                <ellipse cx="0" cy="0" rx="3" ry="18" fill="#f8fafc" transform="rotate(-25)" />
            </g>

            <!-- ================= LABELS & POINTERS ================= -->
            <g class="diagram-labels">
                <!-- Inlet Label -->
                <polyline points="150,180 180,140 250,140" fill="none" stroke="#cbd5e1" stroke-width="1.5" stroke-dasharray="3,3"/>
                <text x="255" y="145" fill="#f8fafc" font-size="14" font-weight="bold">含砂浆液入口</text>
                
                <!-- Lining Label -->
                <polyline points="325,380 270,430 180,430" fill="none" stroke="#f59e0b" stroke-width="1.5" stroke-dasharray="3,3"/>
                <text x="175" y="425" fill="#f59e0b" font-size="14" font-weight="bold" text-anchor="end">聚氨酯衬里</text>
                <text x="175" y="445" fill="#94a3b8" font-size="12" text-anchor="end">耐磨核心工作区</text>

                <!-- Liquid Outlet Label -->
                <polyline points="470,130 500,80 580,80" fill="none" stroke="#38bdf8" stroke-width="1.5" stroke-dasharray="3,3"/>
                <text x="585" y="75" fill="#38bdf8" font-size="14" font-weight="bold">中心清液溢流</text>
                
                <!-- Butterfly Valve Label -->
                <polyline points="560,170 530,220 610,220" fill="none" stroke="#10b981" stroke-width="1.5" stroke-dasharray="3,3"/>
                <text x="615" y="215" fill="#f8fafc" font-size="14" font-weight="bold">目标保护:放液管线与蝶阀</text>
                <text x="615" y="235" fill="#10b981" font-size="12">✔ 磨损源已彻底隔离</text>

                <!-- Sand Collection Label -->
                <polyline points="430,600 480,600" fill="none" stroke="#fcd34d" stroke-width="1.5" stroke-dasharray="3,3"/>
                <text x="490" y="595" fill="#fcd34d" font-size="14" font-weight="bold">沉砂收集包</text>
                <text x="490" y="615" fill="#94a3b8" font-size="12">分离粒度: 0.1~2mm</text>

                <!-- Manual Valve Label -->
                <polyline points="450,690 490,690" fill="none" stroke="#cbd5e1" stroke-width="1.5" stroke-dasharray="3,3"/>
                <text x="500" y="695" fill="#f8fafc" font-size="14" font-weight="bold">排砂手阀</text>
            </g>
        </svg>
    </main>
</div>

<script>
document.addEventListener('DOMContentLoaded', () => {
    // UI Elements
    const pressureSlider = document.getElementById('pressureSlider');
    const pressureVal = document.getElementById('pressureVal');
    const sandSlider = document.getElementById('sandSlider');
    const sandVal = document.getElementById('sandVal');
    const dischargeBtn = document.getElementById('dischargeBtn');
    const valveWheel = document.getElementById('valveWheel');
    const canvas = document.getElementById('particles-layer');

    // State Variables
    let isValveOpen = false;
    let pressureMultiplier = 1.0; 
    let sandRatio = 0.4;
    let particles = [];
    const MAX_PARTICLES = 500;

    // Map pressure (0.1-0.3) to multiplier (0.5 - 1.5 for speed)
    const updatePressure = () => {
        let val = parseFloat(pressureSlider.value);
        pressureVal.textContent = val.toFixed(2) + ' MPa';
        pressureMultiplier = (val - 0.1) * 5 + 0.5; // Maps 0.1->0.5, 0.3->1.5
    };

    const updateSand = () => {
        sandRatio = parseFloat(sandSlider.value);
        if(sandRatio < 0.3) sandVal.textContent = '低含砂';
        else if(sandRatio < 0.6) sandVal.textContent = '中等含砂';
        else sandVal.textContent = '高含砂';
    };

    pressureSlider.addEventListener('input', updatePressure);
    sandSlider.addEventListener('input', updateSand);

    dischargeBtn.addEventListener('click', () => {
        isValveOpen = !isValveOpen;
        if (isValveOpen) {
            dischargeBtn.textContent = '关闭排砂手阀';
            dischargeBtn.classList.add('active');
            valveWheel.style.transform = 'rotate(90deg)';
        } else {
            dischargeBtn.textContent = '手动开启排砂手阀';
            dischargeBtn.classList.remove('active');
            valveWheel.style.transform = 'rotate(0deg)';
        }
    });

    // Particle Generation
    function createParticle() {
        if (particles.length > MAX_PARTICLES) return;

        const isSand = Math.random() < sandRatio;
        const p = {
            id: Math.random(),
            type: isSand ? 'sand' : 'liquid',
            el: document.createElementNS("http://www.w3.org/2000/svg", "circle"),
            phase: 1,
            x: 100, // Starts at left inlet
            y_pos: 240 + (Math.random() * 20 - 10), // Spread vertically in pipe
            angle: 0,
            r: 0, // Swirl radius
            r_ratio: isSand ? (0.85 + Math.random()*0.15) : (0.1 + Math.random()*0.6), // Sand stays outside, liquid inside
            turn_y: 360 + Math.random() * 140, // Where liquid starts moving up
            randOffset: Math.random() * 6 // For organic settling
        };

        p.el.setAttribute("r", isSand ? 3.5 : 2.5);
        p.el.setAttribute("fill", isSand ? "#fcd34d" : "#38bdf8");
        if (!isSand) p.el.setAttribute("opacity", "0.7");
        
        canvas.appendChild(p.el);
        particles.push(p);
    }

    // Main Animation Loop
    function update() {
        // Generate particles based on flow rate (pressure)
        let genChance = 0.6 * pressureMultiplier;
        if (Math.random() < genChance) createParticle();
        if (Math.random() < genChance - 0.5) createParticle(); // extra chance at high pressure

        for (let i = particles.length - 1; i >= 0; i--) {
            let p = particles[i];
            
            // Phase 1: Entry Pipe
            if (p.phase === 1) {
                p.x += 3.5 * pressureMultiplier;
                p.el.setAttribute("cx", p.x);
                p.el.setAttribute("cy", p.y_pos);
                
                // Reach cyclone body
                if (p.x >= 330) {
                    p.phase = 2;
                    p.angle = Math.PI; // Enter tangentially from left
                }
            } 
            // Phase 2: Downward Swirl (Primary Separation)
            else if (p.phase === 2) {
                p.angle -= 0.16 * pressureMultiplier; 
                p.y_pos += 1.8 * pressureMultiplier;
                
                // Calculate max bounds based on cone shape
                let max_r = 95;
                if (p.y_pos > 300) {
                    max_r = 95 - 65 * ((p.y_pos - 300) / 250); // Taper down to radius 30 at y=550
                }
                p.r = max_r * p.r_ratio;
                
                // 2D projection of 3D spiral
                let cx = 400 + p.r * Math.cos(p.angle);
                let cy = p.y_pos + p.r * Math.sin(p.angle) * 0.15; // 0.15 tilt factor
                
                p.el.setAttribute("cx", cx);
                p.el.setAttribute("cy", cy);
                
                // Pseudo-3D Depth opacity
                if (Math.sin(p.angle) < 0) {
                    p.el.style.opacity = p.type === 'sand' ? '0.4' : '0.2';
                } else {
                    p.el.style.opacity = p.type === 'sand' ? '1' : '0.7';
                }

                // Phase transitions
                if (p.type === 'liquid' && p.y_pos > p.turn_y) {
                    p.phase = 3;
                    p.target_r = 5 + Math.random() * 12; // Shrink to inner vortex
                }
                if (p.type === 'sand' && p.y_pos > 550) {
                    p.phase = 5; // Enter collection bag
                }
            }
            // Phase 3: Upward Swirl (Clear Liquid)
            else if (p.phase === 3) {
                p.angle -= 0.3 * pressureMultiplier; // Inner vortex spins faster
                p.y_pos -= 3.5 * pressureMultiplier; // Moves up
                p.r += (p.target_r - p.r) * 0.1; // Smoothly constrain radius
                
                let cx = 400 + p.r * Math.cos(p.angle);
                let cy = p.y_pos + p.r * Math.sin(p.angle) * 0.15;
                
                p.el.setAttribute("cx", cx);
                p.el.setAttribute("cy", cy);
                p.el.style.opacity = "0.8"; // Make clearly visible in inner tube
                
                if (p.y_pos < 130) {
                    p.phase = 4;
                    p.exit_x = cx;
                    p.exit_y = cy;
                }
            }
            // Phase 4: Liquid Exit Pipe
            else if (p.phase === 4) {
                p.exit_x += 4.5 * pressureMultiplier;
                p.el.setAttribute("cx", p.exit_x);
                p.el.setAttribute("cy", p.exit_y);
                
                if (p.exit_x > 750) {
                    p.el.remove();
                    particles.splice(i, 1);
                }
            }
            // Phase 5: Sand Accumulation
            else if (p.phase === 5) {
                if (isValveOpen) {
                    p.phase = 6;
                } else {
                    // Dynamic pile calculation based on settled sand
                    let settledCount = particles.filter(pt => pt.phase === 5 && pt.y_pos >= 560).length;
                    let fillHeight = Math.min(85, settledCount * 0.4); 
                    let actualBottom = 645 - fillHeight + p.randOffset;
                    
                    if (p.y_pos < actualBottom) {
                        // Still falling in bag
                        p.angle -= 0.04 * pressureMultiplier;
                        p.y_pos += 2 * pressureMultiplier;
                    } else if (p.y_pos > actualBottom + 3) {
                        // Slump down if space created
                        p.y_pos += 1;
                    }
                    
                    p.r = 24 * p.r_ratio;
                    let cx = 400 + p.r * Math.cos(p.angle);
                    let cy = p.y_pos + p.r * Math.sin(p.angle) * 0.15;
                    p.el.setAttribute("cx", cx);
                    p.el.setAttribute("cy", cy);
                    
                    // Manage excess particles to prevent DOM bloat
                    if (settledCount > 250 && Math.random() < 0.05 && p.y_pos >= 640) {
                         p.el.remove();
                         particles.splice(i, 1);
                    }
                }
            }
            // Phase 6: Sand Discharge
            else if (p.phase === 6) {
                p.y_pos += 5 * pressureMultiplier;
                let cx = parseFloat(p.el.getAttribute("cx"));
                cx += (400 - cx) * 0.15; // Funnel into discharge pipe center
                
                p.el.setAttribute("cx", cx);
                p.el.setAttribute("cy", p.y_pos);
                
                if (p.y_pos > 780) {
                    p.el.remove();
                    particles.splice(i, 1);
                }
            }
        }
        
        requestAnimationFrame(update);
    }

    // Initialize state & start loop
    updatePressure();
    updateSand();
    requestAnimationFrame(update);
});
</script>

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