分享图
动画工坊
引擎就绪
<!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-base: #0a0d14;
            --bg-grid: #131826;
            --text-main: #e2e8f0;
            --text-muted: #64748b;
            --accent-cyan: #00f0ff;
            --accent-green: #39ff14;
            --accent-orange: #ff5500;
            --panel-bg: rgba(13, 17, 26, 0.75);
            --panel-border: rgba(0, 240, 255, 0.2);
        }

        body, html {
            margin: 0;
            padding: 0;
            width: 100%;
            height: 100%;
            background-color: var(--bg-base);
            font-family: 'SF Pro Display', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
            overflow: hidden;
            display: flex;
            justify-content: center;
            align-items: center;
        }

        /* 核心动画容器,确保大尺寸且居中 */
        .animation-container {
            position: relative;
            width: 100%;
            max-width: 1440px;
            height: 100vh;
            display: flex;
            justify-content: center;
            align-items: center;
            background-image: 
                linear-gradient(var(--bg-grid) 1px, transparent 1px),
                linear-gradient(90deg, var(--bg-grid) 1px, transparent 1px);
            background-size: 40px 40px;
            background-position: center center;
        }

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

        /* 信息面板,绝对定位在角落,字体极小,避免遮挡核心区 */
        .data-panel {
            position: absolute;
            background: var(--panel-bg);
            border: 1px solid var(--panel-border);
            border-radius: 8px;
            padding: 16px;
            backdrop-filter: blur(10px);
            -webkit-backdrop-filter: blur(10px);
            box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
            z-index: 10;
        }

        .panel-top-left { top: 32px; left: 32px; width: 260px; }
        .panel-bottom-left { bottom: 32px; left: 32px; width: 280px; }
        .panel-bottom-right { bottom: 32px; right: 32px; width: 260px; }

        .panel-header {
            font-size: 11px;
            text-transform: uppercase;
            letter-spacing: 2px;
            color: var(--accent-cyan);
            margin-bottom: 12px;
            border-bottom: 1px solid rgba(0, 240, 255, 0.1);
            padding-bottom: 6px;
            display: flex;
            justify-content: space-between;
        }

        .data-row {
            display: flex;
            justify-content: space-between;
            margin-bottom: 8px;
            font-size: 13px;
            color: var(--text-main);
            align-items: center;
        }

        .data-row:last-child { margin-bottom: 0; }
        
        .data-label { color: var(--text-muted); font-size: 12px; }
        
        .data-value { 
            font-family: 'ui-monospace', 'SFMono-Regular', Consolas, monospace;
            font-weight: 600;
            text-align: right;
            transition: color 0.3s;
        }

        .status-dot {
            width: 6px;
            height: 6px;
            border-radius: 50%;
            display: inline-block;
            margin-right: 6px;
            background-color: var(--accent-cyan);
            box-shadow: 0 0 8px var(--accent-cyan);
        }

        .status-dot.warning { background-color: var(--accent-orange); box-shadow: 0 0 8px var(--accent-orange); }
        .status-dot.active { background-color: var(--accent-green); box-shadow: 0 0 8px var(--accent-green); }

        /* 流动动画类 */
        .flow-path {
            stroke-dasharray: 8 8;
            animation: flowAnim 1s linear infinite;
        }
        
        @keyframes flowAnim {
            to { stroke-dashoffset: -16; }
        }

        .signal-line {
            stroke-dasharray: 4 4;
            opacity: 0.3;
            transition: opacity 0.3s, stroke 0.3s;
        }
        
        .signal-line.active {
            opacity: 1;
            animation: flowAnim 0.5s linear infinite;
        }

        /* 探头波纹动画 */
        .radar-wave { opacity: 0; transform-origin: center; }
        .radar-active .radar-wave:nth-child(1) { animation: ping 1.5s cubic-bezier(0, 0, 0.2, 1) infinite; }
        .radar-active .radar-wave:nth-child(2) { animation: ping 1.5s cubic-bezier(0, 0, 0.2, 1) infinite 0.5s; }

        @keyframes ping {
            0% { transform: scale(0.5); opacity: 0.8; }
            100% { transform: scale(2.5); opacity: 0; }
        }

        /* IFR 理念角标 */
        .ifr-badge {
            position: absolute;
            top: 32px;
            right: 32px;
            background: rgba(0, 240, 255, 0.05);
            border: 1px solid rgba(0, 240, 255, 0.3);
            color: var(--accent-cyan);
            font-size: 12px;
            padding: 6px 12px;
            border-radius: 20px;
            letter-spacing: 1px;
            display: flex;
            align-items: center;
            gap: 8px;
        }
    </style>
</head>
<body>

<div class="animation-container">
    
    <!-- 左上角:系统概述面板 -->
    <div class="data-panel panel-top-left">
        <div class="panel-header">
            <span>智能中枢 PLC 状态</span>
            <span id="plc-clock">00:00.0</span>
        </div>
        <div class="data-row">
            <span class="data-label">运行模式</span>
            <span class="data-value" style="color: var(--accent-cyan);"><span class="status-dot"></span>双级闭环自动监控</span>
        </div>
        <div class="data-row">
            <span class="data-label">当前阶段</span>
            <span class="data-value" id="sys-phase" style="font-size: 12px;">待机监控中</span>
        </div>
        <div style="margin-top: 12px; font-size: 11px; color: var(--text-muted); line-height: 1.5;">
            * 最终理想解 (IFR):利用现有动力资源与冗余空间,通过时序联动消除次级失效,实现系统自我平衡。
        </div>
    </div>

    <!-- 左下角:前端振动筛控制环 -->
    <div class="data-panel panel-bottom-left">
        <div class="panel-header">一级控制环 (防跑浆抑制)</div>
        <div class="data-row">
            <span class="data-label">跑浆探头状态 (距筛面 300mm)</span>
            <span class="data-value" id="ui-probe-screen"><span class="status-dot"></span>正常</span>
        </div>
        <div class="data-row">
            <span class="data-label">进料口堰板开度</span>
            <span class="data-value" id="ui-weir">100.0%</span>
        </div>
        <div class="data-row">
            <span class="data-label">电动螺杆升降行程</span>
            <span class="data-value" id="ui-screw">0.0 mm</span>
        </div>
    </div>

    <!-- 右下角:后段缓冲罐控制环 -->
    <div class="data-panel panel-bottom-right">
        <div class="panel-header">二级控制环 (液位防溢防溃)</div>
        <div class="data-row">
            <span class="data-label">缓冲罐液位</span>
            <span class="data-value" id="ui-tank-level">50.0%</span>
        </div>
        <div class="data-row">
            <span class="data-label">安全阈值触发点</span>
            <span class="data-value">85.0%</span>
        </div>
        <div class="data-row">
            <span class="data-label">底部电动蝶阀</span>
            <span class="data-value" id="ui-valve"><span class="status-dot" style="background-color: var(--text-muted); box-shadow: none;"></span>关闭待机</span>
        </div>
    </div>

    <div class="ifr-badge">
        <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2v20M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6"/></svg>
        全系统物理量平衡监控图
    </div>

    <!-- 核心 SVG 动画区 -->
    <svg viewBox="0 0 1200 700" preserveAspectRatio="xMidYMid meet">
        <defs>
            <!-- 滤镜与发光效果 -->
            <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="6" result="blur" />
                <feColorMatrix type="matrix" values="0 0 0 0 1   0 0 0 0 0.33   0 0 0 0 0   0 0 0 1 0" in="blur" result="coloredBlur"/>
                <feComposite in="SourceGraphic" in2="coloredBlur" operator="over" />
            </filter>
            <filter id="glow-green" x="-20%" y="-20%" width="140%" height="140%">
                <feGaussianBlur stdDeviation="4" result="blur" />
                <feColorMatrix type="matrix" values="0 0 0 0 0.22   0 0 0 0 1   0 0 0 0 0.08   0 0 0 1 0" in="blur" result="coloredBlur"/>
                <feComposite in="SourceGraphic" in2="coloredBlur" operator="over" />
            </filter>

            <!-- 渐变定义 -->
            <linearGradient id="pipe-grad" x1="0%" y1="0%" x2="0%" y2="100%">
                <stop offset="0%" stop-color="#2a334a" />
                <stop offset="50%" stop-color="#1e2436" />
                <stop offset="100%" stop-color="#0f1423" />
            </linearGradient>
            <linearGradient id="fluid-grad" x1="0%" y1="0%" x2="100%" y2="0%">
                <stop offset="0%" stop-color="#0066ff" />
                <stop offset="100%" stop-color="#00f0ff" />
            </linearGradient>
            <linearGradient id="fluid-warning-grad" x1="0%" y1="0%" x2="100%" y2="0%">
                <stop offset="0%" stop-color="#ff0044" />
                <stop offset="100%" stop-color="#ff5500" />
            </linearGradient>
            <linearGradient id="tank-grad" x1="0%" y1="0%" x2="100%" y2="0%">
                <stop offset="0%" stop-color="rgba(30, 36, 54, 0.4)" />
                <stop offset="50%" stop-color="rgba(30, 36, 54, 0.1)" />
                <stop offset="100%" stop-color="rgba(30, 36, 54, 0.5)" />
            </linearGradient>

            <pattern id="mesh-pattern" width="10" height="10" patternUnits="userSpaceOnUse">
                <path d="M 10 0 L 0 10 M 0 0 L 10 10" fill="none" stroke="#3a4b6b" stroke-width="0.5" opacity="0.5"/>
            </pattern>
        </defs>

        <!-- 背景连接线基准 -->
        <g opacity="0.1">
            <line x1="200" y1="400" x2="800" y2="400" stroke="#fff" stroke-width="1" stroke-dasharray="2 4"/>
            <line x1="800" y1="100" x2="800" y2="600" stroke="#fff" stroke-width="1" stroke-dasharray="2 4"/>
        </g>

        <!-- 进料管道 -->
        <g transform="translate(100, 150)">
            <path d="M 0 0 L 150 0 Q 180 0 180 30 L 180 150" fill="none" stroke="url(#pipe-grad)" stroke-width="40" stroke-linecap="round"/>
            <!-- 进料流体 -->
            <path id="svg-inlet-fluid" class="flow-path" d="M 0 0 L 150 0 Q 180 0 180 30 L 180 150" fill="none" stroke="url(#fluid-grad)" stroke-width="20" stroke-linecap="round"/>
            
            <!-- 插板堰板 (Weir Plate) -->
            <rect id="svg-weir" x="155" y="40" width="50" height="60" fill="#3a4b6b" stroke="#00f0ff" stroke-width="1" rx="4"/>
            <rect x="175" y="10" width="10" height="40" fill="#64748b"/>
            <text x="215" y="60" fill="var(--text-muted)" font-size="10">插板堰板 (伺服驱动)</text>
        </g>

        <!-- 振动筛主体与转轴 -->
        <g id="svg-screen-group" transform="rotate(0, 250, 360)">
            <!-- 下方漏斗 -->
            <path d="M 280 360 L 600 360 L 520 440 L 360 440 Z" fill="rgba(30, 36, 54, 0.4)" stroke="#2a334a" stroke-width="2"/>
            
            <!-- 筛面框 -->
            <rect x="250" y="345" width="400" height="15" fill="#1e2436" stroke="#475569" stroke-width="2" rx="2"/>
            <!-- 筛网 -->
            <rect x="260" y="340" width="380" height="5" fill="url(#mesh-pattern)" stroke="#3a4b6b" stroke-width="1"/>
            
            <!-- 筛上流体 (泥浆) -->
            <path id="svg-screen-fluid" d="M 270 335 L 450 335" fill="none" stroke="url(#fluid-grad)" stroke-width="12" stroke-linecap="round" class="flow-path"/>

            <text x="420" y="380" fill="var(--text-muted)" font-size="12">振动筛组件</text>
        </g>
        <!-- 支点 -->
        <circle cx="250" cy="360" r="8" fill="#475569" stroke="#0f1423" stroke-width="3"/>

        <!-- 前端升降螺杆 -->
        <g transform="translate(630, 380)">
            <!-- 底座 -->
            <rect x="0" y="100" width="40" height="20" fill="#1e2436" rx="2"/>
            <!-- 螺杆外壳 -->
            <rect x="10" y="40" width="20" height="60" fill="#2a334a"/>
            <!-- 升降内杆 -->
            <rect id="svg-screw-rod" x="14" y="0" width="12" height="40" fill="#94a3b8"/>
            <!-- 螺纹修饰 -->
            <path d="M 14 5 L 26 10 M 14 15 L 26 20 M 14 25 L 26 30" stroke="#475569" stroke-width="1.5" fill="none"/>
            <text x="50" y="110" fill="var(--text-muted)" font-size="10">电动螺杆升降机构</text>
        </g>

        <!-- 跑浆检测雷达探头 -->
        <g id="svg-radar-probe" transform="translate(600, 200)">
            <path d="M 0 0 L 20 0 L 15 20 L 5 20 Z" fill="#00f0ff" stroke="#fff" stroke-width="1" filter="url(#glow-cyan)"/>
            <rect x="8" y="-15" width="4" height="15" fill="#64748b"/>
            <!-- 波纹 -->
            <g class="radar-wave">
                <path d="M -10 30 Q 10 40 30 30" fill="none" stroke="#00f0ff" stroke-width="2"/>
            </g>
            <g class="radar-wave">
                <path d="M -20 40 Q 10 55 40 40" fill="none" stroke="#00f0ff" stroke-width="2"/>
            </g>
            <text x="35" y="-5" fill="var(--text-muted)" font-size="10">雷达液位计 (距筛面 300mm)</text>
        </g>

        <!-- 缓冲罐及液位探头 -->
        <g transform="translate(800, 300)">
            <!-- 罐体后壁 -->
            <rect x="0" y="0" width="160" height="220" fill="url(#tank-grad)" rx="10"/>
            
            <!-- 罐内流体 (通过控制 Y 和 height 来模拟液位上升) -->
            <!-- 底部 Y = 210, 满罐 height = 180 (从 Y=30 到 210) -->
            <rect id="svg-tank-fluid" x="5" y="110" width="150" height="100" fill="url(#fluid-grad)" rx="5" opacity="0.8"/>
            
            <!-- 罐体玻璃/边框 -->
            <rect x="0" y="0" width="160" height="220" fill="none" stroke="#3a4b6b" stroke-width="4" rx="10"/>
            
            <!-- 刻度线 -->
            <line x1="160" y1="30" x2="170" y2="30" stroke="#ff5500" stroke-width="2"/>
            <text x="175" y="34" fill="#ff5500" font-size="10">85% 触发线</text>
            <line x1="160" y1="110" x2="165" y2="110" stroke="#64748b" stroke-width="1"/>
            <line x1="160" y1="180" x2="170" y2="180" stroke="#00f0ff" stroke-width="2"/>
            <text x="175" y="184" fill="#00f0ff" font-size="10">10% 安全线</text>

            <!-- 顶部静压液位探头 -->
            <g id="svg-tank-probe" transform="translate(80, -20)">
                <rect x="-8" y="0" width="16" height="25" fill="#00f0ff" rx="2" filter="url(#glow-cyan)"/>
                <line x1="0" y1="25" x2="0" y2="150" stroke="#00f0ff" stroke-width="1" stroke-dasharray="2 2" opacity="0.5"/>
            </g>

            <!-- 底部出口管道与电动蝶阀 -->
            <g transform="translate(80, 220)">
                <path d="M -20 0 L -20 60 L 20 60 L 20 0" fill="none" stroke="url(#pipe-grad)" stroke-width="40"/>
                <!-- 底部排出流体 -->
                <path id="svg-outlet-fluid" class="flow-path" d="M 0 0 L 0 60" fill="none" stroke="url(#fluid-grad)" stroke-width="20" opacity="0"/>
                
                <!-- 蝶阀结构 -->
                <circle cx="0" cy="30" r="25" fill="#1e2436" stroke="#475569" stroke-width="3"/>
                <!-- 阀瓣 (旋转控制开闭) -->
                <line id="svg-valve-disc" x1="-20" y1="30" x2="20" y2="30" stroke="#00f0ff" stroke-width="4" stroke-linecap="round" transform="rotate(0, 0, 30)"/>
                
                <!-- 阀门执行器马达 -->
                <rect x="25" y="20" width="30" height="20" fill="#3a4b6b" rx="2"/>
                <text x="60" y="34" fill="var(--text-muted)" font-size="10">电动蝶阀 (排液控制)</text>
            </g>
            <text x="45" y="240" fill="var(--text-muted)" font-size="12">缓冲罐</text>
        </g>

        <!-- 泥浆流入缓冲罐的连接流体 -->
        <path id="svg-drain-fluid" class="flow-path" d="M 440 440 C 440 500, 850 400, 850 350" fill="none" stroke="url(#fluid-grad)" stroke-width="8" opacity="0.6"/>

        <!-- 核心控制中枢 PLC -->
        <g transform="translate(450, 60)">
            <rect x="0" y="0" width="180" height="80" fill="rgba(15, 20, 35, 0.9)" stroke="#00f0ff" stroke-width="2" rx="6" filter="url(#glow-cyan)"/>
            <!-- 芯片纹理 -->
            <rect x="10" y="10" width="160" height="60" fill="none" stroke="#2a334a" stroke-width="1"/>
            <path d="M 40 10 L 40 70 M 70 10 L 70 70 M 110 10 L 110 70 M 140 10 L 140 70" stroke="#2a334a" stroke-width="1"/>
            
            <text x="90" y="45" fill="#fff" font-size="16" font-weight="bold" text-anchor="middle" letter-spacing="2">PLC 中枢</text>
            <circle id="svg-plc-led" cx="160" cy="20" r="4" fill="#00f0ff"/>
        </g>

        <!-- 信号连线 (动效路径) -->
        <g id="signal-lines-group" fill="none" stroke-width="2">
            <!-- 跑浆探头 -> PLC -->
            <path id="sig-probe1-plc" class="signal-line" stroke="#00f0ff" d="M 610 200 L 610 100 L 630 100"/>
            <!-- PLC -> 堰板 -->
            <path id="sig-plc-weir" class="signal-line" stroke="#00f0ff" d="M 450 100 L 290 100 L 290 180 L 225 180"/>
            <!-- PLC -> 螺杆 -->
            <path id="sig-plc-screw" class="signal-line" stroke="#00f0ff" d="M 630 130 L 670 130 L 670 480 L 650 480"/>
            
            <!-- 缓冲罐探头 -> PLC -->
            <path id="sig-probe2-plc" class="signal-line" stroke="#00f0ff" d="M 880 280 L 880 120 L 630 120"/>
            <!-- PLC -> 蝶阀 -->
            <path id="sig-plc-valve" class="signal-line" stroke="#00f0ff" d="M 630 80 L 980 80 L 980 540 L 935 540"/>
        </g>

    </svg>
</div>

<script>
    /**
     * 核心控制逻辑:基于时间的无尽循环动画机
     * 严格遵照最终理想解理念,展现单点异常通过双环联动自动消解的过程。
     */
    
    // 配置参数
    const TOTAL_DURATION = 18000; // 总周期 18秒
    
    // 获取 DOM 元素
    const D = {
        clock: document.getElementById('plc-clock'),
        phase: document.getElementById('sys-phase'),
        uiProbeScreen: document.getElementById('ui-probe-screen'),
        uiWeir: document.getElementById('ui-weir'),
        uiScrew: document.getElementById('ui-screw'),
        uiTankLevel: document.getElementById('ui-tank-level'),
        uiValve: document.getElementById('ui-valve'),
        
        weir: document.getElementById('svg-weir'),
        screenFluid: document.getElementById('svg-screen-fluid'),
        screenGroup: document.getElementById('svg-screen-group'),
        screwRod: document.getElementById('svg-screw-rod'),
        radarProbe: document.getElementById('svg-radar-probe'),
        radarProbeDot: document.querySelector('#svg-radar-probe path'),
        
        tankFluid: document.getElementById('svg-tank-fluid'),
        tankProbe: document.getElementById('svg-tank-probe').querySelector('rect'),
        valveDisc: document.getElementById('svg-valve-disc'),
        outletFluid: document.getElementById('svg-outlet-fluid'),
        plcBox: document.querySelector('g[transform="translate(450, 60)"] rect'),
        plcLed: document.getElementById('svg-plc-led'),
        
        sigProbe1: document.getElementById('sig-probe1-plc'),
        sigWeir: document.getElementById('sig-plc-weir'),
        sigScrew: document.getElementById('sig-plc-screw'),
        sigProbe2: document.getElementById('sig-probe2-plc'),
        sigValve: document.getElementById('sig-plc-valve')
    };

    // 颜色常量
    const C = {
        cyan: '#00f0ff',
        orange: '#ff5500',
        green: '#39ff14',
        warningGlow: 'url(#glow-orange)',
        cyanGlow: 'url(#glow-cyan)',
        greenGlow: 'url(#glow-green)',
        normalFluid: 'url(#fluid-grad)',
        warningFluid: 'url(#fluid-warning-grad)'
    };

    // 缓动函数 (Ease In Out Quad)
    function easeInOutQuad(t) { return t < .5 ? 2 * t * t : -1 + (4 - 2 * t) * t; }

    // 插值器,支持多关键帧
    function interpolate(time, keyframes) {
        if (time <= keyframes[0].t) return keyframes[0].v;
        if (time >= keyframes[keyframes.length - 1].t) return keyframes[keyframes.length - 1].v;
        
        for (let i = 0; i < keyframes.length - 1; i++) {
            if (time >= keyframes[i].t && time < keyframes[i+1].t) {
                let progress = (time - keyframes[i].t) / (keyframes[i+1].t - keyframes[i].t);
                let eased = easeInOutQuad(progress);
                return keyframes[i].v + (keyframes[i+1].v - keyframes[i].v) * eased;
            }
        }
        return 0;
    }

    // 定义各变量的时间轴轨迹
    // t: 时间(ms), v: 目标值
    const TIMELINES = {
        // 堰板 Y 坐标开度 (原始Y=40,全关假设Y=70,60%开度约 Y=52)
        weirY: [
            {t: 0, v: 40}, {t: 3000, v: 40}, 
            {t: 4500, v: 52}, // 关至60%
            {t: 14000, v: 52}, {t: 16000, v: 40} // 恢复
        ],
        // 螺杆 Y 位移 (0 -> 120mm 物理量映射到动画缩放,视觉上位移 -30)
        screwOffset: [
            {t: 0, v: 0}, {t: 3500, v: 0}, 
            {t: 5500, v: -20}, // 抬升到位 (用时约2秒模拟4s全行程的比例)
            {t: 14000, v: -20}, {t: 16000, v: 0}
        ],
        // 筛面倾角 (度)
        screenAngle: [
            {t: 0, v: 0}, {t: 3500, v: 0}, 
            {t: 5500, v: -4}, // 抬升引起倾斜
            {t: 14000, v: -4}, {t: 16000, v: 0}
        ],
        // 筛面流体长度 (X向拉伸)
        screenFluidLen: [
            {t: 0, v: 450}, {t: 2000, v: 450}, 
            {t: 3000, v: 620}, // 跑浆发生,流体逼近末端
            {t: 4500, v: 620}, {t: 6000, v: 400}, // 抬升后流体回退
            {t: 14000, v: 400}, {t: 16000, v: 450}
        ],
        // 缓冲罐液位百分比 (0-100)
        tankLevelPct: [
            {t: 0, v: 50}, {t: 4000, v: 50},
            {t: 8500, v: 85}, // 进料减量,但因前端抬升致使滤液积聚,液位连锁升高至85%
            {t: 9500, v: 85},
            {t: 13000, v: 38}, // 阀门开启,排液降至安全线以下10% (约40%)
            {t: 16000, v: 50}, {t: 18000, v: 50}
        ],
        // 蝶阀角度 (0=关, 90=开)
        valveAngle: [
            {t: 0, v: 0}, {t: 8500, v: 0}, 
            {t: 9000, v: 90}, // 开启
            {t: 13000, v: 90}, {t: 13500, v: 0} // 关闭
        ]
    };

    let startTime = null;

    function formatTime(ms) {
        let secs = (ms / 1000).toFixed(1);
        return `00:${secs.padStart(4, '0')}`;
    }

    // 渲染帧
    function render(time) {
        if (!startTime) startTime = time;
        let elapsed = (time - startTime) % TOTAL_DURATION;
        let t = elapsed;

        D.clock.innerText = formatTime(elapsed);

        // --- 1. 计算当前时刻的插值状态 ---
        let wY = interpolate(t, TIMELINES.weirY);
        let sOff = interpolate(t, TIMELINES.screwOffset);
        let sAng = interpolate(t, TIMELINES.screenAngle);
        let sFluid = interpolate(t, TIMELINES.screenFluidLen);
        let tLevel = interpolate(t, TIMELINES.tankLevelPct);
        let vAng = interpolate(t, TIMELINES.valveAngle);

        // --- 2. 映射到 DOM 属性更新 ---
        
        // 前端环机械动作
        D.weir.setAttribute('y', wY);
        D.screwRod.setAttribute('y', sOff);
        D.screwRod.setAttribute('height', 40 - sOff);
        D.screenGroup.setAttribute('transform', `rotate(${sAng}, 250, 360)`);
        
        // 跑浆流体视觉 (超过550视为跑浆危险)
        D.screenFluid.setAttribute('d', `M 270 335 L ${sFluid} 335`);
        if (sFluid > 550 && t < 6000) {
            D.screenFluid.setAttribute('stroke', C.warningFluid);
            D.radarProbe.classList.add('radar-active');
            D.radarProbeDot.setAttribute('fill', C.orange);
            D.radarProbeDot.setAttribute('filter', C.warningGlow);
            D.sigProbe1.classList.add('active');
            D.sigProbe1.setAttribute('stroke', C.orange);
        } else {
            D.screenFluid.setAttribute('stroke', C.normalFluid);
            D.radarProbe.classList.remove('radar-active');
            D.radarProbeDot.setAttribute('fill', C.cyan);
            D.radarProbeDot.setAttribute('filter', C.cyanGlow);
            D.sigProbe1.classList.remove('active');
            D.sigProbe1.setAttribute('stroke', C.cyan);
        }

        // 缓冲罐液位映射 (满100%时 h=180, Y=30; 0%时 h=0, Y=210)
        let tHeight = (tLevel / 100) * 180;
        let tY = 210 - tHeight;
        D.tankFluid.setAttribute('height', tHeight);
        D.tankFluid.setAttribute('y', tY);

        // 液位探头警告状态 (>=85%)
        if (tLevel >= 84.5 && t < 13000) {
            D.tankProbe.setAttribute('fill', C.orange);
            D.tankProbe.setAttribute('filter', C.warningGlow);
            D.sigProbe2.classList.add('active');
            D.sigProbe2.setAttribute('stroke', C.orange);
        } else {
            D.tankProbe.setAttribute('fill', C.cyan);
            D.tankProbe.setAttribute('filter', C.cyanGlow);
            D.sigProbe2.classList.remove('active');
            D.sigProbe2.setAttribute('stroke', C.cyan);
        }

        // 蝶阀排液状态
        D.valveDisc.setAttribute('transform', `rotate(${vAng}, 0, 30)`);
        if (vAng > 5) {
            D.outletFluid.style.opacity = '1';
        } else {
            D.outletFluid.style.opacity = '0';
        }

        // --- 3. PLC 信号线与阶段文本管理 ---
        let phaseText = "待机监控中";
        D.plcBox.setAttribute('stroke', C.cyan);
        D.plcBox.setAttribute('filter', C.cyanGlow);
        D.plcLed.setAttribute('fill', C.cyan);
        
        // 信号线默认静默
        D.sigWeir.classList.remove('active'); D.sigWeir.setAttribute('stroke', C.cyan);
        D.sigScrew.classList.remove('active'); D.sigScrew.setAttribute('stroke', C.cyan);
        D.sigValve.classList.remove('active'); D.sigValve.setAttribute('stroke', C.cyan);

        // 细分状态阶段
        if (t > 3000 && t < 4000) {
            phaseText = "检测到筛面跑浆趋势!";
            D.plcBox.setAttribute('stroke', C.orange);
            D.plcBox.setAttribute('filter', C.warningGlow);
            D.plcLed.setAttribute('fill', C.orange);
        } 
        else if (t >= 4000 && t < 6000) {
            phaseText = "环一:PLC 下发干预指令";
            D.plcBox.setAttribute('stroke', C.green);
            D.plcBox.setAttribute('filter', C.greenGlow);
            D.plcLed.setAttribute('fill', C.green);
            D.sigWeir.classList.add('active'); D.sigWeir.setAttribute('stroke', C.green);
            D.sigScrew.classList.add('active'); D.sigScrew.setAttribute('stroke', C.green);
        }
        else if (t >= 6000 && t < 8500) {
            phaseText = "前端抑制成功,但引发液位连锁升高";
        }
        else if (t >= 8500 && t < 9000) {
            phaseText = "缓冲罐液位触及 85% 危险线!";
            D.plcBox.setAttribute('stroke', C.orange);
            D.plcBox.setAttribute('filter', C.warningGlow);
            D.plcLed.setAttribute('fill', C.orange);
        }
        else if (t >= 9000 && t < 13000) {
            phaseText = "环二:PLC 联动开阀排液平衡";
            D.plcBox.setAttribute('stroke', C.green);
            D.plcBox.setAttribute('filter', C.greenGlow);
            D.plcLed.setAttribute('fill', C.green);
            D.sigValve.classList.add('active'); D.sigValve.setAttribute('stroke', C.green);
        }
        else if (t >= 13000 && t < 14000) {
            phaseText = "液位恢复安全,准备系统复位";
            D.sigValve.classList.add('active'); D.sigValve.setAttribute('stroke', C.cyan);
        }
        else if (t >= 14000 && t < 16000) {
            phaseText = "全系统复原";
            D.sigWeir.classList.add('active'); D.sigWeir.setAttribute('stroke', C.cyan);
            D.sigScrew.classList.add('active'); D.sigScrew.setAttribute('stroke', C.cyan);
        }
        
        D.phase.innerText = phaseText;
        D.phase.style.color = (t>3000&&t<4000) || (t>=8500&&t<9000) ? C.orange : (t>=4000&&t<6000) || (t>=9000&&t<13000) ? C.green : C.cyan;

        // --- 4. 侧边栏 UI 数值更新 ---
        
        // 螺杆 (0到 -20 映射为 0 到 120mm)
        let mm = Math.abs(sOff / 20) * 120;
        D.uiScrew.innerText = mm.toFixed(1) + " mm";
        
        // 堰板开度 (Y从40(100%) 到 70(0%), 52对应60%)
        let weirPct = 100 - ((wY - 40) / 30) * 100;
        D.uiWeir.innerText = weirPct.toFixed(1) + " %";

        // 跑浆探头 UI
        if (sFluid > 550 && t < 6000) {
            D.uiProbeScreen.innerHTML = `<span class="status-dot warning"></span>跑浆告警`;
            D.uiProbeScreen.style.color = C.orange;
        } else {
            D.uiProbeScreen.innerHTML = `<span class="status-dot"></span>正常监控`;
            D.uiProbeScreen.style.color = C.cyan;
        }

        // 缓冲罐 UI
        D.uiTankLevel.innerText = tLevel.toFixed(1) + " %";
        if (tLevel >= 84.5) D.uiTankLevel.style.color = C.orange;
        else D.uiTankLevel.style.color = C.cyan;

        // 阀门 UI
        if (vAng > 10) {
            D.uiValve.innerHTML = `<span class="status-dot active"></span>开启排液`;
            D.uiValve.style.color = C.green;
        } else {
            D.uiValve.innerHTML = `<span class="status-dot" style="background-color: var(--text-muted); box-shadow: none;"></span>关闭待机`;
            D.uiValve.style.color = 'var(--text-main)';
        }

        requestAnimationFrame(render);
    }

    // 页面加载完毕即启动动画,满足无干预自动播放要求
    window.addEventListener('DOMContentLoaded', () => {
        requestAnimationFrame(render);
    });

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