分享图
动画工坊
引擎就绪
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>虹吸自动排液系统原理动画</title>
    <style>
        :root {
            --bg-color: #060b15;
            --grid-color: rgba(43, 90, 165, 0.15);
            --tank-edge: #1a3258;
            --tank-fill: rgba(12, 25, 45, 0.8);
            --fluid-base: #00d2ff;
            --fluid-glow: #3a7bd5;
            --text-main: #e2f1ff;
            --text-dim: #749bc2;
            --alert-color: #ff4757;
            --success-color: #2ed573;
            --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Courier New", monospace;
        }

        body, html {
            margin: 0;
            padding: 0;
            width: 100%;
            height: 100%;
            background-color: var(--bg-color);
            display: flex;
            justify-content: center;
            align-items: center;
            font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
            overflow: hidden;
        }

        #animation-container {
            width: 100%;
            max-width: 1200px;
            aspect-ratio: 16 / 9;
            position: relative;
            box-shadow: 0 0 50px rgba(0, 0, 0, 0.5) inset;
        }

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

        /* 绝对定位的说明文本区域,严格控制在边缘 */
        .hud-panel {
            position: absolute;
            background: rgba(6, 11, 21, 0.75);
            border: 1px solid var(--tank-edge);
            backdrop-filter: blur(4px);
            padding: 12px 16px;
            border-radius: 6px;
            pointer-events: none;
        }

        .hud-top-left { top: 20px; left: 20px; }
        .hud-top-right { top: 20px; right: 20px; }
        .hud-bottom-left { bottom: 20px; left: 20px; }

        .hud-title {
            color: var(--fluid-base);
            font-size: 14px;
            font-weight: bold;
            letter-spacing: 1px;
            margin-bottom: 6px;
            text-transform: uppercase;
        }

        .hud-text {
            color: var(--text-dim);
            font-size: 12px;
            line-height: 1.5;
            margin: 0;
            font-family: var(--font-mono);
        }

        .hud-value {
            color: var(--text-main);
            font-weight: bold;
        }

        .status-indicator {
            display: inline-block;
            width: 8px;
            height: 8px;
            border-radius: 50%;
            margin-right: 6px;
            background: var(--text-dim);
            box-shadow: 0 0 8px currentColor;
        }

        .status-filling { color: var(--text-dim); background: var(--text-dim); }
        .status-priming { color: #ffa502; background: #ffa502; }
        .status-siphoning { color: var(--fluid-base); background: var(--fluid-base); }
        .status-broken { color: var(--alert-color); background: var(--alert-color); }

        /* SVG 内部样式 */
        .pipe-base {
            fill: none;
            stroke: #16243d;
            stroke-width: 24;
            stroke-linecap: round;
            stroke-linejoin: round;
        }
        
        .pipe-inner {
            fill: none;
            stroke: #0a111f;
            stroke-width: 16;
            stroke-linecap: round;
            stroke-linejoin: round;
        }

        .pipe-fluid {
            fill: none;
            stroke: var(--fluid-base);
            stroke-width: 16;
            stroke-linecap: round;
            stroke-linejoin: round;
            filter: drop-shadow(0 0 6px var(--fluid-glow));
            transition: stroke-dashoffset 0.1s linear;
        }

        .pipe-flow-lines {
            fill: none;
            stroke: #ffffff;
            stroke-width: 4;
            stroke-linecap: round;
            stroke-dasharray: 8 24;
            opacity: 0;
        }

        @keyframes flowAnimation {
            to { stroke-dashoffset: -32; }
        }

        .flowing .pipe-flow-lines {
            opacity: 0.8;
            animation: flowAnimation 0.5s linear infinite;
        }

        .tank-glass {
            fill: var(--tank-fill);
            stroke: var(--tank-edge);
            stroke-width: 2;
        }

        .liquid {
            fill: url(#liquidGrad);
            opacity: 0.9;
        }

        .liquid-top {
            fill: var(--fluid-base);
            opacity: 0.6;
        }

        .dashed-line {
            stroke: var(--text-dim);
            stroke-width: 1;
            stroke-dasharray: 4 4;
            opacity: 0.5;
        }

        .measure-text {
            fill: var(--text-dim);
            font-size: 10px;
            font-family: var(--font-mono);
            letter-spacing: 0.5px;
        }
    </style>
</head>
<body>

    <div id="animation-container">
        <!-- 边缘数据面板 -->
        <div class="hud-panel hud-top-left">
            <div class="hud-title">系统运行状态</div>
            <p class="hud-text">
                <span id="ui-status-dot" class="status-indicator status-filling"></span>
                <span id="ui-status-text">缓冲罐蓄液中...</span>
            </p>
            <p class="hud-text" style="margin-top: 4px;">当前排量: <span id="ui-flow-rate" class="hud-value">0.0</span> m³/h</p>
        </div>

        <div class="hud-panel hud-top-right">
            <div class="hud-title">核心工程参数</div>
            <p class="hud-text">管路规格: <span class="hud-value">DN80</span></p>
            <p class="hud-text">额定排量: <span class="hud-value">25 m³/h</span></p>
            <p class="hud-text">触发液位: <span class="hud-value">85%</span></p>
        </div>

        <div class="hud-panel hud-bottom-left">
            <div class="hud-title">TRIZ 最终理想解 (IFR)</div>
            <p class="hud-text" style="max-width: 280px; white-space: normal;">
                利用重力势能与大气压资源,在无需增加持续动力源的极简构架下,实现超量液体的自动感应、触发排放与自动停止。
            </p>
        </div>

        <!-- 核心动画 SVG -->
        <svg viewBox="0 0 1000 562" preserveAspectRatio="xMidYMid meet" xmlns="http://www.w3.org/2000/svg">
            <defs>
                <!-- 背景网格 -->
                <pattern id="grid" width="40" height="40" patternUnits="userSpaceOnUse">
                    <path d="M 40 0 L 0 0 0 40" fill="none" stroke="var(--grid-color)" stroke-width="0.5"/>
                    <circle cx="0" cy="0" r="1" fill="var(--grid-color)" />
                </pattern>

                <!-- 液体渐变 -->
                <linearGradient id="liquidGrad" x1="0%" y1="0%" x2="100%" y2="0%">
                    <stop offset="0%" stop-color="#0052d4" />
                    <stop offset="50%" stop-color="#4364f7" />
                    <stop offset="100%" stop-color="#6fb1fc" />
                </linearGradient>

                <!-- 发光滤镜 -->
                <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="intense-glow" x="-50%" y="-50%" width="200%" height="200%">
                    <feGaussianBlur stdDeviation="8" result="blur1" />
                    <feGaussianBlur stdDeviation="3" result="blur2" />
                    <feMerge>
                        <feMergeNode in="blur1"/>
                        <feMergeNode in="blur2"/>
                        <feMergeNode in="SourceGraphic"/>
                    </feMerge>
                </filter>
            </defs>

            <!-- 绘制背景 -->
            <rect width="100%" height="100%" fill="url(#grid)" />

            <!-- 标尺与辅助线 -->
            <!-- 85% 触发线 -->
            <line x1="180" y1="217" x2="380" y2="217" class="dashed-line" stroke="#ffa502" />
            <text x="130" y="221" class="measure-text" fill="#ffa502">85% 触发液位</text>
            
            <!-- 虹吸破坏线 -->
            <line x1="180" y1="410" x2="380" y2="410" class="dashed-line" stroke="#ff4757" />
            <text x="115" y="414" class="measure-text" fill="#ff4757">吸入口/破坏液位</text>

            <!-- 高差指示 -->
            <line x1="380" y1="460" x2="800" y2="460" class="dashed-line" />
            <line x1="630" y1="360" x2="800" y2="360" class="dashed-line" />
            <path d="M 780 360 L 780 460" fill="none" stroke="var(--text-dim)" stroke-width="1" />
            <polygon points="777,365 783,365 780,360" fill="var(--text-dim)" />
            <polygon points="777,455 783,455 780,460" fill="var(--text-dim)" />
            <text x="790" y="415" class="measure-text">ΔH ≥ 1.5m</text>

            <!-- 虹吸管路背面 (外壳) -->
            <path id="main-pipe-bg" class="pipe-base" d="M 280 405 L 280 120 Q 280 80 320 80 L 680 80 Q 720 80 720 120 L 720 480" />
            <path id="main-pipe-inner" class="pipe-inner" d="M 280 405 L 280 120 Q 280 80 320 80 L 680 80 Q 720 80 720 120 L 720 480" />

            <!-- 管路内部流体 -->
            <!-- 使用 dasharray 控制流体的填充和流动 -->
            <path id="fluid-fill" class="pipe-fluid" d="M 280 405 L 280 120 Q 280 80 320 80 L 680 80 Q 720 80 720 120 L 720 480" />
            
            <g id="flow-group">
                <path id="fluid-flow-lines" class="pipe-flow-lines" d="M 280 405 L 280 120 Q 280 80 320 80 L 680 80 Q 720 80 720 120 L 720 480" />
            </g>

            <!-- 防涡流罩 (吸入口) -->
            <path d="M 260 405 L 300 405 L 310 420 L 250 420 Z" fill="#2c3e50" stroke="#1a252f" stroke-width="2" />
            <text x="280" y="435" class="measure-text" text-anchor="middle">防涡流罩</text>

            <!-- 引水罐/阀门 -->
            <rect x="475" y="55" width="50" height="25" rx="4" fill="#2c3e50" stroke="#1a252f" stroke-width="2" />
            <circle id="valve-light" cx="500" cy="67.5" r="4" fill="#555" />
            <text x="500" y="45" class="measure-text" text-anchor="middle">引水机构</text>

            <!-- 缓冲罐 (左侧高位) -->
            <g id="buffer-tank">
                <!-- 罐体后壁 -->
                <rect x="200" y="160" width="160" height="300" class="tank-glass" />
                <!-- 液体 -->
                <rect id="buffer-liquid" x="202" y="380" width="156" height="78" class="liquid" />
                <!-- 液体顶面反光 -->
                <rect id="buffer-liquid-top" x="202" y="380" width="156" height="4" class="liquid-top" />
                <!-- 罐体前壁刻度 -->
                <rect x="200" y="160" width="160" height="300" fill="none" stroke="var(--tank-edge)" stroke-width="2" />
                <path d="M 200 217 L 210 217 M 200 292 L 210 292 M 200 367 L 210 292" stroke="var(--tank-edge)" stroke-width="2" />
                <text x="280" y="480" class="measure-text" text-anchor="middle" fill="var(--text-main)">缓冲罐</text>
            </g>

            <!-- 循环罐 (右侧低位) -->
            <g id="circ-tank">
                <rect x="630" y="360" width="180" height="150" class="tank-glass" />
                <rect id="circ-liquid" x="632" y="460" width="176" height="48" class="liquid" />
                <rect id="circ-liquid-top" x="632" y="460" width="176" height="4" class="liquid-top" />
                <rect x="630" y="360" width="180" height="150" fill="none" stroke="var(--tank-edge)" stroke-width="2" />
                <text x="720" y="530" class="measure-text" text-anchor="middle" fill="var(--text-main)">循环罐</text>
            </g>
            
            <!-- 气泡动画组 (用于展示虹吸破坏瞬间) -->
            <g id="bubbles" opacity="0">
                <circle cx="280" cy="410" r="3" fill="#fff" />
                <circle cx="275" cy="390" r="4" fill="#fff" />
                <circle cx="285" cy="370" r="2.5" fill="#fff" />
                <circle cx="278" cy="350" r="3.5" fill="#fff" />
                <circle cx="282" cy="330" r="2" fill="#fff" />
            </g>

        </svg>
    </div>

    <script>
        document.addEventListener("DOMContentLoaded", () => {
            // DOM 元素获取
            const bufferLiquid = document.getElementById('buffer-liquid');
            const bufferLiquidTop = document.getElementById('buffer-liquid-top');
            const circLiquid = document.getElementById('circ-liquid');
            const circLiquidTop = document.getElementById('circ-liquid-top');
            
            const pipeFluid = document.getElementById('fluid-fill');
            const flowGroup = document.getElementById('flow-group');
            const valveLight = document.getElementById('valve-light');
            const bubbles = document.getElementById('bubbles');
            
            const uiStatusDot = document.getElementById('ui-status-dot');
            const uiStatusText = document.getElementById('ui-status-text');
            const uiFlowRate = document.getElementById('ui-flow-rate');

            // 测量管路长度以设置 dasharray
            const pipeLength = pipeFluid.getTotalLength();
            pipeFluid.style.strokeDasharray = pipeLength;
            pipeFluid.style.strokeDashoffset = pipeLength; // 初始全空

            // 物理与几何常量映射
            const TANK_BASE_Y = 458; // 缓冲罐底部Y坐标 (内径)
            const TANK_HEIGHT = 296; // 缓冲罐最大内高度
            
            const LVL_85_Y = 217; // 85% 液位 Y坐标
            const LVL_15_Y = 410; // 15% 液位 Y坐标 (吸入口)

            const CIRC_BASE_Y = 508;
            
            // 动画时间轴配置 (总周期 12秒)
            const DURATION = 12000; 
            let startTime = null;

            // 核心动画循环
            function animate(timestamp) {
                if (!startTime) startTime = timestamp;
                
                // 确保动画在页面重新聚焦或重新加载时持续运行
                let elapsed = timestamp - startTime;
                let progress = (elapsed % DURATION) / DURATION; 
                
                // progress 在 0.0 ~ 1.0 之间循环
                updateSystemState(progress);

                requestAnimationFrame(animate);
            }

            // 根据时间轴进度更新物理状态与视觉呈现
            function updateSystemState(p) {
                let bY, bH, cY, cH;
                let pipeOffset = pipeLength;
                let flowRate = "0.0";
                
                // 阶段 1: 缓冲罐蓄液 (0.0 - 0.35)
                if (p < 0.35) {
                    let subP = p / 0.35; // 0 -> 1
                    
                    // 缓冲罐液位从 15% 上升到 85%
                    bY = LVL_15_Y - (LVL_15_Y - LVL_85_Y) * subP;
                    
                    // 循环罐缓慢排空 (模拟系统后处理)
                    cY = 420 + (CIRC_BASE_Y - 420) * (subP * 0.5); 
                    
                    pipeOffset = pipeLength; // 管路空
                    flowGroup.classList.remove('flowing');
                    valveLight.setAttribute('fill', '#555');
                    valveLight.removeAttribute('filter');
                    bubbles.setAttribute('opacity', '0');
                    
                    updateUI('filling', '缓冲罐蓄液中...', '0.0');

                } 
                // 阶段 2: 引水触发 (0.35 - 0.40)
                else if (p < 0.40) {
                    let subP = (p - 0.35) / 0.05;
                    
                    bY = LVL_85_Y; // 液位满
                    cY = 420 + (CIRC_BASE_Y - 420) * 0.5;
                    
                    // 流体被吸上左侧管路至顶点
                    // 左侧上行管长度约 325
                    pipeOffset = pipeLength - (325 * subP);
                    
                    valveLight.setAttribute('fill', '#ffa502');
                    valveLight.setAttribute('filter', 'url(#intense-glow)');
                    
                    updateUI('priming', '引水阀开启,建立真空...', '0.0');

                } 
                // 阶段 3: 虹吸持续排液 (0.40 - 0.85)
                else if (p < 0.85) {
                    let subP = (p - 0.40) / 0.45;
                    
                    // 缓冲罐液位从 85% 降至 15%
                    bY = LVL_85_Y + (LVL_15_Y - LVL_85_Y) * subP;
                    
                    // 循环罐液位上升
                    cY = 460 - (460 - 410) * subP;
                    
                    pipeOffset = 0; // 管路全满
                    flowGroup.classList.add('flowing');
                    
                    valveLight.setAttribute('fill', '#555');
                    valveLight.removeAttribute('filter');
                    
                    // 模拟流量波动
                    let randomFluctuation = (Math.random() * 0.4 - 0.2).toFixed(1);
                    flowRate = (25.0 + parseFloat(randomFluctuation)).toFixed(1);
                    
                    updateUI('siphoning', '虹吸效应形成,持续排液', flowRate);

                } 
                // 阶段 4: 虹吸破坏,管路排空 (0.85 - 0.90)
                else if (p < 0.90) {
                    let subP = (p - 0.85) / 0.05;
                    
                    bY = LVL_15_Y;
                    cY = 410;
                    
                    flowGroup.classList.remove('flowing');
                    
                    // 模拟流体从顶点向两侧落下/排空
                    // 通过改变 stroke-dashoffset 和 dasharray 实现断开效果比较复杂
                    // 这里用简化的视觉处理:迅速增加 offset 模拟抽空
                    pipeOffset = pipeLength * subP;
                    
                    // 显示气泡进入吸入口
                    bubbles.setAttribute('opacity', '1');
                    bubbles.setAttribute('transform', `translate(0, ${-20 * subP})`);
                    
                    updateUI('broken', '液位低于吸口,虹吸破坏', '0.0');
                }
                // 阶段 5: 归位静置 (0.90 - 1.0)
                else {
                    bY = LVL_15_Y;
                    cY = 410 + (CIRC_BASE_Y - 420) * ((p - 0.9) / 0.1 * 0.5); // 准备连接下一周期的液位
                    pipeOffset = pipeLength;
                    bubbles.setAttribute('opacity', '0');
                    updateUI('filling', '系统复位,准备下一周期', '0.0');
                }

                // 应用计算出的几何属性
                bH = TANK_BASE_Y - bY;
                bufferLiquid.setAttribute('y', bY);
                bufferLiquid.setAttribute('height', bH);
                bufferLiquidTop.setAttribute('y', bY);

                cH = CIRC_BASE_Y - cY;
                circLiquid.setAttribute('y', cY);
                circLiquid.setAttribute('height', cH);
                circLiquidTop.setAttribute('y', cY);

                pipeFluid.style.strokeDashoffset = pipeOffset;
            }

            // 更新 UI 状态面板
            let currentStatusCls = '';
            function updateUI(statusKey, text, flow) {
                const cls = `status-${statusKey}`;
                if (currentStatusCls !== cls) {
                    uiStatusDot.className = `status-indicator ${cls}`;
                    uiStatusText.textContent = text;
                    currentStatusCls = cls;
                }
                uiFlowRate.textContent = flow;
            }

            // 启动动画
            requestAnimationFrame(animate);
        });
    </script>
</body>
</html>
积分规则:每轮对话扣10分