独立渲染引擎就绪引擎就绪
<!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>
@import url('https://fonts.googleapis.com/css2?family=Share+Tech+Mono&family=Noto+Sans+SC:wght@300;500;700&display=swap');
:root {
--bg-dark: #050b14;
--grid-color: rgba(30, 58, 138, 0.15);
--neon-cyan: #00f0ff;
--neon-green: #00ff66;
--neon-red: #ff003c;
--neon-yellow: #ffcc00;
--panel-bg: rgba(10, 20, 40, 0.8);
--text-main: #e2e8f0;
--text-muted: #64748b;
}
body {
margin: 0;
padding: 0;
background-color: #02060d;
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: 'Noto Sans SC', sans-serif;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
overflow: hidden;
}
#app-container {
position: relative;
width: 100vw;
height: 100vh;
max-width: 1600px;
max-height: 900px;
display: flex;
justify-content: center;
align-items: center;
box-shadow: inset 0 0 150px rgba(0,0,0,0.9);
}
svg {
width: 100%;
height: 100%;
filter: drop-shadow(0 0 20px rgba(0, 240, 255, 0.1));
}
/* Core Animations */
.flow-fluid {
stroke-dasharray: 12 12;
animation: flowMove linear infinite;
transition: stroke-width 0.8s cubic-bezier(0.4, 0, 0.2, 1), stroke 0.5s ease;
}
@keyframes flowMove {
from { stroke-dashoffset: 24; }
to { stroke-dashoffset: 0; }
}
.signal-pulse {
stroke-dasharray: 5 10;
animation: signalMove 0.5s linear infinite;
opacity: 0;
transition: opacity 0.3s;
}
.signal-active {
opacity: 1;
}
@keyframes signalMove {
from { stroke-dashoffset: 15; }
to { stroke-dashoffset: 0; }
}
.vibrate-normal { animation: shakeNormal 0.1s linear infinite; }
.vibrate-heavy { animation: shakeHeavy 0.08s linear infinite; }
.vibrate-low { animation: shakeLow 0.15s linear infinite; }
@keyframes shakeNormal {
0%, 100% { transform: translate(0, 0); }
25% { transform: translate(1px, 0.5px); }
75% { transform: translate(-0.5px, -1px); }
}
@keyframes shakeHeavy {
0%, 100% { transform: translate(0, 0); }
25% { transform: translate(2px, 1.5px) rotate(0.5deg); }
75% { transform: translate(-1.5px, -2px) rotate(-0.5deg); }
}
@keyframes shakeLow {
0%, 100% { transform: translate(0, 0); }
50% { transform: translate(0.5px, 0.5px); }
}
/* UI Overlay elements via HTML for sharper text rendering */
.overlay-panel {
position: absolute;
background: var(--panel-bg);
border: 1px solid rgba(0, 240, 255, 0.3);
border-radius: 4px;
padding: 15px;
backdrop-filter: blur(4px);
font-family: 'Share Tech Mono', monospace, 'Noto Sans SC';
z-index: 10;
}
.top-left { top: 30px; left: 30px; }
.bottom-right { bottom: 30px; right: 30px; }
.panel-title {
font-size: 12px;
color: var(--neon-cyan);
text-transform: uppercase;
letter-spacing: 2px;
margin-bottom: 8px;
border-bottom: 1px solid rgba(0, 240, 255, 0.2);
padding-bottom: 4px;
}
.panel-data {
font-size: 11px;
line-height: 1.6;
color: var(--text-main);
}
.highlight-data { color: var(--neon-yellow); font-weight: bold; }
.status-text {
font-family: 'Share Tech Mono', monospace;
font-size: 14px;
font-weight: bold;
transition: fill 0.3s;
}
/* PLC Node styles */
.plc-core {
transform-origin: center;
transition: fill 0.3s, filter 0.3s;
}
.processing {
animation: plcPulse 1s ease-in-out infinite alternate;
}
@keyframes plcPulse {
from { filter: drop-shadow(0 0 10px var(--neon-cyan)); fill: rgba(0, 240, 255, 0.2); }
to { filter: drop-shadow(0 0 25px var(--neon-cyan)); fill: rgba(0, 240, 255, 0.5); }
}
.valve-indicator {
transition: transform 0.5s cubic-bezier(0.68, -0.55, 0.265, 1.55);
transform-origin: center;
}
</style>
</head>
<body>
<div id="app-container">
<!-- UI Panels -->
<div class="overlay-panel top-left">
<div class="panel-title">系统状态 | SCADA 总控</div>
<div class="panel-data">
<div>运行模式: <span style="color: var(--neon-green)">自动全局寻优</span></div>
<div>总输入流量: 100% (240 m³/h)</div>
<div>当前阶段: <span id="sys-phase" class="highlight-data">正常均分</span></div>
</div>
</div>
<div class="overlay-panel bottom-right">
<div class="panel-title">核心指标监控</div>
<div class="panel-data">
<div>分配误差控制: <span style="color: var(--neon-green)">≤±5%</span></div>
<div>自适应切换时间: <span style="color: var(--neon-green)">≤5s</span></div>
<div>联锁状态: 2-4台协同就绪</div>
<div style="margin-top: 5px; font-size: 10px; color: var(--text-muted);">
*最终理想解(IFR): 系统利用现有阀门与算法自发消除局部过载矛盾
</div>
</div>
</div>
<!-- Main SVG Animation -->
<svg viewBox="0 0 1200 800" preserveAspectRatio="xMidYMid meet">
<defs>
<!-- Gradients -->
<linearGradient id="pipe-grad" 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>
<linearGradient id="tank-grad" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" stop-color="rgba(15, 23, 42, 0.9)" />
<stop offset="100%" stop-color="rgba(2, 6, 23, 0.9)" />
</linearGradient>
<!-- Glow Filters -->
<filter id="glow-cyan" x="-20%" y="-20%" width="140%" height="140%">
<feGaussianBlur stdDeviation="8" result="blur" />
<feComposite in="SourceGraphic" in2="blur" operator="over" />
</filter>
<filter id="glow-red" x="-20%" y="-20%" width="140%" height="140%">
<feGaussianBlur stdDeviation="8" result="blur" />
<feComposite in="SourceGraphic" in2="blur" operator="over" />
</filter>
<filter id="glow-green" x="-20%" y="-20%" width="140%" height="140%">
<feGaussianBlur stdDeviation="4" result="blur" />
<feComposite in="SourceGraphic" in2="blur" operator="over" />
</filter>
<!-- Grid Pattern for Shaker -->
<pattern id="screen-mesh" width="10" height="10" patternUnits="userSpaceOnUse">
<path d="M 10 0 L 0 10 M 0 0 L 10 10" fill="none" stroke="rgba(255,255,255,0.1)" stroke-width="0.5"/>
</pattern>
</defs>
<!-- Background Infrastructure -->
<g id="infrastructure" stroke="#334155" stroke-width="12" fill="none" stroke-linecap="round" stroke-linejoin="round">
<!-- Main Input Pipe -->
<path d="M600,0 L600,150" />
<!-- Distribution Manifold Structure -->
<rect x="450" y="150" width="300" height="80" rx="4" fill="#0f172a" stroke="#475569" stroke-width="2"/>
<text x="600" y="195" fill="#64748b" font-size="12" text-anchor="middle" font-family="'Share Tech Mono', sans-serif" stroke="none">多路电动阀 / 流量分配器</text>
<!-- Pipes to Shakers -->
<path d="M500,230 L500,280 L250,280 L250,450" /> <!-- To Shaker 1 -->
<path d="M600,230 L600,450" /> <!-- To Shaker 2 -->
<path d="M700,230 L700,280 L950,280 L950,450" /> <!-- To Shaker 3 -->
</g>
<!-- Active Fluid Flows -->
<g id="fluid-flows" fill="none" stroke-linecap="round" stroke-linejoin="round" filter="url(#glow-cyan)">
<!-- Main Input Fluid -->
<path d="M600,0 L600,150" stroke="var(--neon-cyan)" stroke-width="12" class="flow-fluid" style="animation-duration: 0.8s;" />
<!-- Distributed Fluids (Widths controlled by JS) -->
<path id="fluid-1" d="M500,230 L500,280 L250,280 L250,450" stroke="var(--neon-cyan)" stroke-width="6" class="flow-fluid" style="animation-duration: 1s;" />
<path id="fluid-2" d="M600,230 L600,450" stroke="var(--neon-cyan)" stroke-width="6" class="flow-fluid" style="animation-duration: 1s;" />
<path id="fluid-3" d="M700,230 L700,280 L950,280 L950,450" stroke="var(--neon-cyan)" stroke-width="6" class="flow-fluid" style="animation-duration: 1s;" />
</g>
<!-- PLC Controller Brain -->
<g id="plc-system" transform="translate(600, 100)">
<!-- Signal lines from Shakers to PLC -->
<g fill="none" stroke-width="2">
<path id="sig-1" d="M-350,350 L-350,300 L-50,300 L-50,0" stroke="var(--neon-cyan)" class="signal-pulse"/>
<path id="sig-2" d="M0,350 L0,0" stroke="var(--neon-cyan)" class="signal-pulse"/>
<path id="sig-3" d="M350,350 L350,300 L50,300 L50,0" stroke="var(--neon-cyan)" class="signal-pulse"/>
</g>
<!-- PLC Node -->
<rect x="-60" y="-40" width="120" height="80" rx="8" fill="rgba(15, 23, 42, 0.9)" stroke="var(--neon-cyan)" stroke-width="2" class="plc-core" id="plc-box"/>
<polygon points="-70,0 -60,-10 -60,10" fill="var(--neon-cyan)"/>
<polygon points="70,0 60,-10 60,10" fill="var(--neon-cyan)"/>
<text x="0" y="-10" fill="#fff" font-size="14" font-weight="bold" text-anchor="middle" font-family="'Noto Sans SC'">总控 PLC</text>
<text x="0" y="10" fill="var(--neon-cyan)" font-size="10" text-anchor="middle" font-family="'Share Tech Mono'" id="plc-algo-text">ALG: EQUILIBRIUM</text>
<!-- Connection to manifold -->
<path d="M0,40 L0,50" stroke="var(--neon-cyan)" stroke-width="4" stroke-dasharray="2 4" animation="flowMove 0.5s linear infinite"/>
</g>
<!-- Valve Mechanisms (Visual representation inside manifold) -->
<g id="valves" transform="translate(600, 190)">
<!-- Valve 1 -->
<g transform="translate(-100, 0)">
<circle cx="0" cy="0" r="15" fill="#1e293b" stroke="var(--neon-cyan)" stroke-width="2"/>
<path id="valve-dial-1" d="M-10,0 L10,0" stroke="var(--neon-cyan)" stroke-width="3" class="valve-indicator" transform="rotate(0)"/>
</g>
<!-- Valve 2 -->
<g transform="translate(0, 0)">
<circle cx="0" cy="0" r="15" fill="#1e293b" stroke="var(--neon-cyan)" stroke-width="2"/>
<path id="valve-dial-2" d="M-10,0 L10,0" stroke="var(--neon-cyan)" stroke-width="3" class="valve-indicator" transform="rotate(0)"/>
</g>
<!-- Valve 3 -->
<g transform="translate(100, 0)">
<circle cx="0" cy="0" r="15" fill="#1e293b" stroke="var(--neon-cyan)" stroke-width="2"/>
<path id="valve-dial-3" d="M-10,0 L10,0" stroke="var(--neon-cyan)" stroke-width="3" class="valve-indicator" transform="rotate(0)"/>
</g>
</g>
<!-- Shale Shakers (振动筛) -->
<g id="shaker-system">
<!-- Shaker 1 -->
<g transform="translate(250, 520)">
<!-- Base Buffer Tank -->
<path d="M-80,40 L80,40 L60,120 L-60,120 Z" fill="url(#tank-grad)" stroke="#334155" stroke-width="2"/>
<text x="0" y="90" fill="#64748b" font-size="12" text-anchor="middle">缓冲罐 1#</text>
<!-- Vibrating Screen Unit -->
<g id="screen-1" class="vibrate-normal">
<rect x="-90" y="-40" width="180" height="80" rx="4" fill="#0f172a" stroke="#475569" stroke-width="2"/>
<rect x="-80" y="-30" width="160" height="60" fill="url(#screen-mesh)"/>
<rect x="-90" y="40" width="180" height="5" fill="#334155"/> <!-- mount -->
<!-- Sensor -->
<circle cx="-70" cy="-20" r="5" fill="var(--neon-green)" filter="url(#glow-green)" id="sensor-1"/>
</g>
<!-- Data Display -->
<rect x="-40" y="-80" width="80" height="30" rx="2" fill="rgba(0,0,0,0.6)" stroke="#334155" stroke-width="1"/>
<text x="0" y="-60" fill="var(--neon-cyan)" font-size="14" text-anchor="middle" class="status-text" id="val-1">33.3%</text>
<text x="0" y="140" fill="#e2e8f0" font-size="14" font-weight="bold" text-anchor="middle">振动筛 1#</text>
</g>
<!-- Shaker 2 (The one that will overload) -->
<g transform="translate(600, 520)">
<path d="M-80,40 L80,40 L60,120 L-60,120 Z" fill="url(#tank-grad)" stroke="#334155" stroke-width="2"/>
<text x="0" y="90" fill="#64748b" font-size="12" text-anchor="middle">缓冲罐 2#</text>
<g id="screen-2" class="vibrate-normal">
<rect x="-90" y="-40" width="180" height="80" rx="4" fill="#0f172a" stroke="#475569" stroke-width="2"/>
<rect x="-80" y="-30" width="160" height="60" fill="url(#screen-mesh)"/>
<rect x="-90" y="40" width="180" height="5" fill="#334155"/>
<circle cx="-70" cy="-20" r="5" fill="var(--neon-green)" filter="url(#glow-green)" id="sensor-2"/>
<!-- Mud buildup visualization (hidden initially) -->
<path id="mud-buildup" d="M-70,20 Q0,30 70,20 Q0,-10 -70,20" fill="rgba(255, 100, 0, 0)" transition="fill 1s"/>
</g>
<rect x="-40" y="-80" width="80" height="30" rx="2" fill="rgba(0,0,0,0.6)" stroke="#334155" stroke-width="1"/>
<text x="0" y="-60" fill="var(--neon-cyan)" font-size="14" text-anchor="middle" class="status-text" id="val-2">33.3%</text>
<text x="0" y="140" fill="#e2e8f0" font-size="14" font-weight="bold" text-anchor="middle">振动筛 2#</text>
</g>
<!-- Shaker 3 -->
<g transform="translate(950, 520)">
<path d="M-80,40 L80,40 L60,120 L-60,120 Z" fill="url(#tank-grad)" stroke="#334155" stroke-width="2"/>
<text x="0" y="90" fill="#64748b" font-size="12" text-anchor="middle">缓冲罐 3#</text>
<g id="screen-3" class="vibrate-normal">
<rect x="-90" y="-40" width="180" height="80" rx="4" fill="#0f172a" stroke="#475569" stroke-width="2"/>
<rect x="-80" y="-30" width="160" height="60" fill="url(#screen-mesh)"/>
<rect x="-90" y="40" width="180" height="5" fill="#334155"/>
<circle cx="-70" cy="-20" r="5" fill="var(--neon-green)" filter="url(#glow-green)" id="sensor-3"/>
</g>
<rect x="-40" y="-80" width="80" height="30" rx="2" fill="rgba(0,0,0,0.6)" stroke="#334155" stroke-width="1"/>
<text x="0" y="-60" fill="var(--neon-cyan)" font-size="14" text-anchor="middle" class="status-text" id="val-3">33.3%</text>
<text x="0" y="140" fill="#e2e8f0" font-size="14" font-weight="bold" text-anchor="middle">振动筛 3#</text>
</g>
</g>
</svg>
</div>
<script>
document.addEventListener("DOMContentLoaded", () => {
// DOM Elements
const fluid1 = document.getElementById('fluid-1');
const fluid2 = document.getElementById('fluid-2');
const fluid3 = document.getElementById('fluid-3');
const val1 = document.getElementById('val-1');
const val2 = document.getElementById('val-2');
const val3 = document.getElementById('val-3');
const screen1 = document.getElementById('screen-1');
const screen2 = document.getElementById('screen-2');
const screen3 = document.getElementById('screen-3');
const sensor1 = document.getElementById('sensor-1');
const sensor2 = document.getElementById('sensor-2');
const sensor3 = document.getElementById('sensor-3');
const sig1 = document.getElementById('sig-1');
const sig2 = document.getElementById('sig-2');
const sig3 = document.getElementById('sig-3');
const plcBox = document.getElementById('plc-box');
const plcAlgoText = document.getElementById('plc-algo-text');
const sysPhaseText = document.getElementById('sys-phase');
const mudBuildup = document.getElementById('mud-buildup');
const valveDial1 = document.getElementById('valve-dial-1');
const valveDial2 = document.getElementById('valve-dial-2');
const valveDial3 = document.getElementById('valve-dial-3');
// Colors
const colorCyan = 'var(--neon-cyan)';
const colorRed = 'var(--neon-red)';
const colorYellow = 'var(--neon-yellow)';
const colorGreen = 'var(--neon-green)';
// State Variables
let currentPhase = 0; // 0: Normal, 1: Overload, 2: Rebalance
// Animation Timeline Config
const PHASE_NORMAL = 4000; // Time spent in normal state
const PHASE_OVERLOAD = 2000; // Time indicating problem before action
const PHASE_REBALANCE = 5000; // Time in rebalanced state solving the problem
function updateValue(element, targetValue, duration = 1000) {
const startValue = parseFloat(element.textContent);
const startTime = performance.now();
function animate(currentTime) {
const elapsed = currentTime - startTime;
const progress = Math.min(elapsed / duration, 1);
// Easing: easeOutQuart
const easeProgress = 1 - Math.pow(1 - progress, 4);
const currentVal = startValue + (targetValue - startValue) * easeProgress;
element.textContent = currentVal.toFixed(1) + '%';
if (progress < 1) {
requestAnimationFrame(animate);
}
}
requestAnimationFrame(animate);
}
function setNormalState() {
sysPhaseText.textContent = "正常均分";
sysPhaseText.style.color = colorCyan;
// Fluids
fluid1.style.strokeWidth = '6';
fluid2.style.strokeWidth = '6';
fluid3.style.strokeWidth = '6';
fluid1.style.stroke = colorCyan;
fluid2.style.stroke = colorCyan;
fluid3.style.stroke = colorCyan;
// Values
updateValue(val1, 33.3);
updateValue(val2, 33.3);
updateValue(val3, 33.3);
val1.style.fill = colorCyan;
val2.style.fill = colorCyan;
val3.style.fill = colorCyan;
// Shakers
screen1.className.baseVal = 'vibrate-normal';
screen2.className.baseVal = 'vibrate-normal';
screen3.className.baseVal = 'vibrate-normal';
mudBuildup.style.fill = 'rgba(255, 100, 0, 0)';
// Sensors & Signals
sensor2.style.fill = colorGreen;
sensor2.setAttribute('filter', 'url(#glow-green)');
sig1.classList.remove('signal-active');
sig2.classList.remove('signal-active');
sig3.classList.remove('signal-active');
// PLC
plcBox.classList.remove('processing');
plcBox.style.stroke = colorCyan;
plcAlgoText.textContent = "ALG: EQUILIBRIUM";
plcAlgoText.style.fill = colorCyan;
// Valves
valveDial1.setAttribute('transform', 'rotate(0)');
valveDial2.setAttribute('transform', 'rotate(0)');
valveDial3.setAttribute('transform', 'rotate(0)');
}
function setOverloadState() {
sysPhaseText.textContent = "2# 跑浆预警";
sysPhaseText.style.color = colorRed;
// Shaker 2 struggles
screen2.className.baseVal = 'vibrate-heavy';
mudBuildup.style.fill = 'rgba(255, 100, 0, 0.6)';
// Sensor detects
sensor2.style.fill = colorRed;
sensor2.setAttribute('filter', 'url(#glow-red)');
// Signal sent to PLC
sig2.style.stroke = colorRed;
sig2.classList.add('signal-active');
// PLC Starts processing
plcBox.classList.add('processing');
plcBox.style.stroke = colorRed;
plcAlgoText.textContent = "DETECT: OVERLOAD";
plcAlgoText.style.fill = colorRed;
val2.style.fill = colorRed;
}
function setRebalanceState() {
sysPhaseText.textContent = "动态重分配解耦";
sysPhaseText.style.color = colorYellow;
// PLC executes algorithm
plcBox.style.stroke = colorYellow;
plcAlgoText.textContent = "EXEC: REALLOCATE";
plcAlgoText.style.fill = colorYellow;
// Signals indicating control action
sig1.style.stroke = colorYellow;
sig2.style.stroke = colorYellow;
sig3.style.stroke = colorYellow;
sig1.classList.add('signal-active');
sig2.classList.add('signal-active');
sig3.classList.add('signal-active');
// VALVES ADJUST (IFR action)
// Shaker 2 gets less flow, 1 & 3 get more
fluid1.style.strokeWidth = '9';
fluid2.style.strokeWidth = '2';
fluid3.style.strokeWidth = '9';
// Fluid color indicating active balancing
fluid2.style.stroke = 'rgba(0, 240, 255, 0.4)';
// Values update (Mathematical optimal distribution)
updateValue(val1, 45.0);
updateValue(val2, 10.0);
updateValue(val3, 45.0);
val1.style.fill = colorYellow;
val2.style.fill = colorGreen; // Recovered
val3.style.fill = colorYellow;
// Visual representation of valve angles
valveDial1.setAttribute('transform', 'rotate(45)');
valveDial2.setAttribute('transform', 'rotate(-75)');
valveDial3.setAttribute('transform', 'rotate(45)');
// Shaker 2 recovers
setTimeout(() => {
screen2.className.baseVal = 'vibrate-low';
mudBuildup.style.fill = 'rgba(255, 100, 0, 0)';
sensor2.style.fill = colorGreen;
sensor2.setAttribute('filter', 'url(#glow-green)');
plcBox.classList.remove('processing');
sysPhaseText.textContent = "最优稳态运行";
sysPhaseText.style.color = colorGreen;
plcAlgoText.textContent = "ALG: OPTIMIZED";
plcAlgoText.style.fill = colorGreen;
}, 1500);
}
// Main Animation Loop
function runAnimationSequence() {
// Step 1: Normal
setNormalState();
// Step 2: Overload (after normal phase)
setTimeout(() => {
setOverloadState();
// Step 3: Rebalance (after overload detection)
setTimeout(() => {
setRebalanceState();
// Loop back to start
setTimeout(runAnimationSequence, PHASE_REBALANCE);
}, PHASE_OVERLOAD);
}, PHASE_NORMAL);
}
// Initial start
setTimeout(runAnimationSequence, 500);
});
</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-color: #0b101e;
--panel-bg: rgba(16, 26, 48, 0.85);
--border-glow: #00f0ff;
--normal-flow: #00e5ff;
--warning-color: #ff3333;
--adjust-color: #ffaa00;
--success-color: #00ffaa;
--text-main: #e0f2fe;
--font-mono: 'Consolas', 'Courier New', monospace;
}
body {
margin: 0;
padding: 0;
background-color: var(--bg-color);
color: var(--text-main);
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
min-height: 100vh;
overflow: hidden;
background-image:
radial-gradient(circle at 50% 50%, #152243 0%, #0b101e 100%),
linear-gradient(rgba(0, 240, 255, 0.03) 1px, transparent 1px),
linear-gradient(90deg, rgba(0, 240, 255, 0.03) 1px, transparent 1px);
background-size: 100% 100%, 40px 40px, 40px 40px;
}
.container {
width: 100%;
max-width: 1400px;
display: flex;
flex-direction: column;
align-items: center;
position: relative;
}
svg {
width: 100%;
height: auto;
max-height: 85vh;
filter: drop-shadow(0 0 10px rgba(0, 240, 255, 0.1));
}
/* 动画类 */
.flow-dash {
stroke-dasharray: 15, 10;
animation: flowAnim 1s linear infinite;
}
.flow-dash.fast {
stroke-dasharray: 25, 15;
animation-duration: 0.5s;
stroke-width: 8px;
}
.flow-dash.slow {
stroke-dasharray: 8, 12;
animation-duration: 2s;
stroke-width: 3px;
}
@keyframes flowAnim {
to { stroke-dashoffset: -50; }
}
@keyframes flowAnimReverse {
to { stroke-dashoffset: 50; }
}
.signal-dash {
stroke-dasharray: 5, 5;
animation: flowAnimReverse 0.8s linear infinite;
}
.shaker-box {
transform-origin: center;
animation: vibrate 0.1s linear infinite;
}
.shaker-box.heavy {
animation: vibrate-heavy 0.1s linear infinite;
}
@keyframes vibrate {
0%, 100% { transform: translateY(0); }
25% { transform: translateY(-1px) translateX(-1px); }
75% { transform: translateY(1px) translateX(1px); }
}
@keyframes vibrate-heavy {
0%, 100% { transform: translateY(0); }
25% { transform: translateY(-3px) translateX(-2px); }
75% { transform: translateY(3px) translateX(2px); }
}
@keyframes blink {
0%, 100% { opacity: 1; }
50% { opacity: 0.3; }
}
.blink {
animation: blink 0.5s ease-in-out infinite;
}
/* UI Controls */
.controls {
position: absolute;
bottom: 20px;
display: flex;
gap: 15px;
background: var(--panel-bg);
padding: 15px 30px;
border-radius: 30px;
border: 1px solid rgba(0, 240, 255, 0.3);
backdrop-filter: blur(10px);
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
z-index: 10;
}
button {
background: rgba(0, 240, 255, 0.1);
border: 1px solid var(--border-glow);
color: var(--border-glow);
padding: 8px 20px;
border-radius: 20px;
font-family: inherit;
font-weight: 600;
cursor: pointer;
transition: all 0.3s;
text-transform: uppercase;
letter-spacing: 1px;
}
button:hover, button.active {
background: var(--border-glow);
color: var(--bg-color);
box-shadow: 0 0 15px rgba(0, 240, 255, 0.5);
}
.triz-label {
position: absolute;
top: 30px;
left: 30px;
background: linear-gradient(135deg, rgba(255, 170, 0, 0.2), rgba(255, 51, 51, 0.2));
border-left: 4px solid var(--adjust-color);
padding: 15px 25px;
font-size: 14px;
line-height: 1.5;
backdrop-filter: blur(4px);
}
.triz-label h3 {
margin: 0 0 5px 0;
color: var(--adjust-color);
font-size: 16px;
}
</style>
</head>
<body>
<div class="container">
<!-- 侧边 TRIZ 理念说明 -->
<div class="triz-label">
<h3>TRIZ 最终理想解 (IFR)</h3>
系统无需增加额外的庞大缓冲罐<br>
利用现有“总控PLC + 多路电动阀”<br>
实现全局联锁与钻井液自平衡分配<br>
<i>矛盾消除:流量冲击与处理能力的失配</i>
</div>
<svg viewBox="0 0 1200 800" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid meet">
<defs>
<filter id="glow-cyan" x="-20%" y="-20%" width="140%" height="140%">
<feGaussianBlur stdDeviation="5" result="blur" />
<feComposite in="SourceGraphic" in2="blur" operator="over" />
</filter>
<filter id="glow-warning" x="-20%" y="-20%" width="140%" height="140%">
<feGaussianBlur stdDeviation="8" result="blur" />
<feComposite in="SourceGraphic" in2="blur" operator="over" />
</filter>
<linearGradient id="pipeGrad" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" stop-color="#1a253c" />
<stop offset="50%" stop-color="#2d3f61" />
<stop offset="100%" stop-color="#1a253c" />
</linearGradient>
<linearGradient id="pipeGradVert" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" stop-color="#1a253c" />
<stop offset="50%" stop-color="#2d3f61" />
<stop offset="100%" stop-color="#1a253c" />
</linearGradient>
</defs>
<!-- 基础管道背景 -->
<g id="base-pipes" stroke="#2d3f61" stroke-width="12" fill="none" stroke-linejoin="round">
<!-- 主进料管 -->
<path d="M 600 -10 L 600 150" />
<!-- 横向分配管 -->
<path d="M 250 150 L 950 150" />
<!-- 三条支管 -->
<path d="M 250 150 L 250 550" />
<path d="M 600 150 L 600 550" />
<path d="M 950 150 L 950 550" />
</g>
<!-- 流体动画路径 (通过 JS 动态控制颜色和粗细) -->
<g id="flow-paths" fill="none" stroke-linecap="round" stroke-linejoin="round" filter="url(#glow-cyan)">
<!-- 主进料流体 -->
<path d="M 600 0 L 600 150" stroke="var(--normal-flow)" stroke-width="6" class="flow-dash fast" />
<!-- 支流 1 -->
<path id="flow1" d="M 600 150 L 250 150 L 250 550" stroke="var(--normal-flow)" stroke-width="6" class="flow-dash" />
<!-- 支流 2 -->
<path id="flow2" d="M 600 150 L 600 550" stroke="var(--normal-flow)" stroke-width="6" class="flow-dash" />
<!-- 支流 3 -->
<path id="flow3" d="M 600 150 L 950 150 L 950 550" stroke="var(--normal-flow)" stroke-width="6" class="flow-dash" />
</g>
<!-- 信号线 (通过 JS 控制显示隐藏和颜色) -->
<g id="signal-lines" stroke-width="2" fill="none" opacity="0">
<!-- S2 to PLC -->
<path id="sig-s2-plc" d="M 600 550 L 600 180 L 800 180 L 800 130" stroke="var(--warning-color)" class="signal-dash" />
<!-- PLC to Valves -->
<path id="sig-plc-v1" d="M 750 130 L 750 200 L 250 200 L 250 250" stroke="var(--adjust-color)" class="signal-dash" />
<path id="sig-plc-v2" d="M 800 130 L 800 200 L 600 200 L 600 250" stroke="var(--adjust-color)" class="signal-dash" />
<path id="sig-plc-v3" d="M 850 130 L 850 200 L 950 200 L 950 250" stroke="var(--adjust-color)" class="signal-dash" />
</g>
<!-- 阀门组 (多路电动阀/分配器) -->
<g id="valves">
<!-- 阀 1 -->
<g transform="translate(250, 280)">
<polygon points="-20,-15 20,15 20,-15 -20,15" fill="#1e293b" stroke="var(--border-glow)" stroke-width="2"/>
<circle cx="0" cy="0" r="6" fill="var(--border-glow)" id="valve-core-1"/>
<text x="-45" y="5" fill="#8ba3cb" font-size="14" font-family="var(--font-mono)">MV-01</text>
<text x="30" y="5" fill="#00f0ff" font-size="14" font-family="var(--font-mono)" id="valve-val-1">33.3%</text>
</g>
<!-- 阀 2 -->
<g transform="translate(600, 280)">
<polygon points="-20,-15 20,15 20,-15 -20,15" fill="#1e293b" stroke="var(--border-glow)" stroke-width="2"/>
<circle cx="0" cy="0" r="6" fill="var(--border-glow)" id="valve-core-2"/>
<text x="-45" y="5" fill="#8ba3cb" font-size="14" font-family="var(--font-mono)">MV-02</text>
<text x="30" y="5" fill="#00f0ff" font-size="14" font-family="var(--font-mono)" id="valve-val-2">33.3%</text>
</g>
<!-- 阀 3 -->
<g transform="translate(950, 280)">
<polygon points="-20,-15 20,15 20,-15 -20,15" fill="#1e293b" stroke="var(--border-glow)" stroke-width="2"/>
<circle cx="0" cy="0" r="6" fill="var(--border-glow)" id="valve-core-3"/>
<text x="-45" y="5" fill="#8ba3cb" font-size="14" font-family="var(--font-mono)">MV-03</text>
<text x="30" y="5" fill="#00f0ff" font-size="14" font-family="var(--font-mono)" id="valve-val-3">33.3%</text>
</g>
</g>
<!-- 流量计 -->
<g id="flowmeters">
<!-- 计 1 -->
<g transform="translate(250, 400)">
<circle cx="0" cy="0" r="18" fill="#0b101e" stroke="#8ba3cb" stroke-width="2"/>
<text x="0" y="4" fill="#8ba3cb" font-size="12" text-anchor="middle" font-family="var(--font-mono)">FM1</text>
</g>
<g transform="translate(600, 400)">
<circle cx="0" cy="0" r="18" fill="#0b101e" stroke="#8ba3cb" stroke-width="2"/>
<text x="0" y="4" fill="#8ba3cb" font-size="12" text-anchor="middle" font-family="var(--font-mono)">FM2</text>
</g>
<g transform="translate(950, 400)">
<circle cx="0" cy="0" r="18" fill="#0b101e" stroke="#8ba3cb" stroke-width="2"/>
<text x="0" y="4" fill="#8ba3cb" font-size="12" text-anchor="middle" font-family="var(--font-mono)">FM3</text>
</g>
</g>
<!-- 振动筛组 -->
<g id="shakers">
<!-- Shaker 1 -->
<g transform="translate(170, 550)" class="shaker-box" id="shaker-1">
<rect x="0" y="0" width="160" height="90" rx="8" fill="#16223b" stroke="#37538c" stroke-width="2"/>
<line x1="20" y1="30" x2="140" y2="30" stroke="#00f0ff" stroke-width="1" stroke-dasharray="4,4" opacity="0.5"/>
<line x1="20" y1="50" x2="140" y2="50" stroke="#00f0ff" stroke-width="1" stroke-dasharray="4,4" opacity="0.5"/>
<line x1="20" y1="70" x2="140" y2="70" stroke="#00f0ff" stroke-width="1" stroke-dasharray="4,4" opacity="0.5"/>
<text x="80" y="22" fill="#fff" font-size="16" text-anchor="middle" font-weight="bold">1# 振动筛</text>
<text x="80" y="80" fill="#00ffaa" font-size="12" text-anchor="middle" id="status-1">正常运行</text>
</g>
<!-- Shaker 2 -->
<g transform="translate(520, 550)" class="shaker-box" id="shaker-2">
<rect x="0" y="0" width="160" height="90" rx="8" fill="#16223b" stroke="#37538c" stroke-width="2" id="shaker-rect-2"/>
<line x1="20" y1="30" x2="140" y2="30" stroke="#00f0ff" stroke-width="1" stroke-dasharray="4,4" opacity="0.5"/>
<line x1="20" y1="50" x2="140" y2="50" stroke="#00f0ff" stroke-width="1" stroke-dasharray="4,4" opacity="0.5"/>
<line x1="20" y1="70" x2="140" y2="70" stroke="#00f0ff" stroke-width="1" stroke-dasharray="4,4" opacity="0.5"/>
<text x="80" y="22" fill="#fff" font-size="16" text-anchor="middle" font-weight="bold">2# 振动筛</text>
<text x="80" y="80" fill="#00ffaa" font-size="12" text-anchor="middle" id="status-2">正常运行</text>
<!-- 跑浆溢出效果 (隐藏默认) -->
<path d="M -10 90 Q 80 120 170 90 Q 80 140 -10 90" fill="var(--warning-color)" opacity="0" id="mud-overflow"/>
</g>
<!-- Shaker 3 -->
<g transform="translate(870, 550)" class="shaker-box" id="shaker-3">
<rect x="0" y="0" width="160" height="90" rx="8" fill="#16223b" stroke="#37538c" stroke-width="2"/>
<line x1="20" y1="30" x2="140" y2="30" stroke="#00f0ff" stroke-width="1" stroke-dasharray="4,4" opacity="0.5"/>
<line x1="20" y1="50" x2="140" y2="50" stroke="#00f0ff" stroke-width="1" stroke-dasharray="4,4" opacity="0.5"/>
<line x1="20" y1="70" x2="140" y2="70" stroke="#00f0ff" stroke-width="1" stroke-dasharray="4,4" opacity="0.5"/>
<text x="80" y="22" fill="#fff" font-size="16" text-anchor="middle" font-weight="bold">3# 振动筛</text>
<text x="80" y="80" fill="#00ffaa" font-size="12" text-anchor="middle" id="status-3">正常运行</text>
</g>
</g>
<!-- 总控 PLC 与 SCADA 面板 -->
<g id="plc-panel" transform="translate(700, 30)">
<rect x="0" y="0" width="300" height="100" rx="10" fill="rgba(16, 26, 48, 0.9)" stroke="var(--border-glow)" stroke-width="1.5" filter="url(#glow-cyan)"/>
<text x="150" y="25" fill="#fff" font-size="18" text-anchor="middle" font-weight="bold" letter-spacing="2">总控 PLC 算法中心</text>
<line x1="20" y1="35" x2="280" y2="35" stroke="var(--border-glow)" stroke-width="1" opacity="0.3"/>
<text x="20" y="55" fill="#8ba3cb" font-size="13">全局流量分配误差:</text>
<text x="280" y="55" fill="var(--success-color)" font-size="14" text-anchor="end" font-family="var(--font-mono)" font-weight="bold" id="data-error">≤ ±2.1%</text>
<text x="20" y="75" fill="#8ba3cb" font-size="13">联锁切换响应时间:</text>
<text x="280" y="75" fill="var(--success-color)" font-size="14" text-anchor="end" font-family="var(--font-mono)" font-weight="bold" id="data-time">0.0s (待命)</text>
<text x="150" y="93" fill="var(--border-glow)" font-size="12" text-anchor="middle" id="plc-status-text">系统状态:正常均流监控中...</text>
</g>
</svg>
<div class="controls">
<button id="btn-normal" onclick="setMode('normal')" class="active">① 正常均衡模式</button>
<button id="btn-warning" onclick="setMode('warning')">② 2#筛跑浆预警</button>
<button id="btn-balance" onclick="setMode('balance')">③ IFR 系统自平衡</button>
</div>
</div>
<script>
// DOM Elements mapping
const els = {
flow1: document.getElementById('flow1'),
flow2: document.getElementById('flow2'),
flow3: document.getElementById('flow3'),
val1: document.getElementById('valve-val-1'),
val2: document.getElementById('valve-val-2'),
val3: document.getElementById('valve-val-3'),
core1: document.getElementById('valve-core-1'),
core2: document.getElementById('valve-core-2'),
core3: document.getElementById('valve-core-3'),
shaker2: document.getElementById('shaker-2'),
shakerRect2: document.getElementById('shaker-rect-2'),
mudOverflow: document.getElementById('mud-overflow'),
status2: document.getElementById('status-2'),
sigLines: document.getElementById('signal-lines'),
sigS2Plc: document.getElementById('sig-s2-plc'),
plcStatus: document.getElementById('plc-status-text'),
dataError: document.getElementById('data-error'),
dataTime: document.getElementById('data-time'),
btnNormal: document.getElementById('btn-normal'),
btnWarning: document.getElementById('btn-warning'),
btnBalance: document.getElementById('btn-balance')
};
// 动画状态机控制
let autoPlayTimer = null;
let timeCounter = 0;
let timeInterval = null;
function updateUIButtons(activeId) {
[els.btnNormal, els.btnWarning, els.btnBalance].forEach(btn => btn.classList.remove('active'));
document.getElementById(activeId).classList.add('active');
}
function setMode(mode) {
// 清理原有状态
clearInterval(timeInterval);
els.shaker2.classList.remove('heavy');
els.status2.classList.remove('blink');
els.sigLines.style.opacity = '0';
els.mudOverflow.style.opacity = '0';
els.shakerRect2.setAttribute('stroke', '#37538c');
if (mode === 'normal') {
updateUIButtons('btn-normal');
// 恢复等比例流量
els.flow1.className.baseVal = 'flow-dash';
els.flow2.className.baseVal = 'flow-dash';
els.flow3.className.baseVal = 'flow-dash';
els.flow1.setAttribute('stroke', 'var(--normal-flow)');
els.flow2.setAttribute('stroke', 'var(--normal-flow)');
els.flow3.setAttribute('stroke', 'var(--normal-flow)');
// 恢复数值与颜色
els.val1.textContent = '33.3%';
els.val2.textContent = '33.3%';
els.val3.textContent = '33.3%';
[els.core1, els.core2, els.core3].forEach(c => c.setAttribute('fill', 'var(--border-glow)'));
[els.val1, els.val2, els.val3].forEach(v => v.setAttribute('fill', 'var(--border-glow)'));
// 面板复位
els.status2.textContent = '正常运行';
els.status2.setAttribute('fill', 'var(--success-color)');
els.plcStatus.textContent = '系统状态:正常均流监控中...';
els.plcStatus.setAttribute('fill', 'var(--border-glow)');
els.dataTime.textContent = '0.0s (待命)';
els.dataTime.setAttribute('fill', 'var(--success-color)');
els.dataError.textContent = '≤ ±1.5%';
} else if (mode === 'warning') {
updateUIButtons('btn-warning');
// 2# 筛超载跑浆
els.shaker2.classList.add('heavy');
els.shakerRect2.setAttribute('stroke', 'var(--warning-color)');
els.mudOverflow.style.opacity = '0.8';
els.status2.textContent = '跑浆溢出警告!';
els.status2.setAttribute('fill', 'var(--warning-color)');
els.status2.classList.add('blink');
// 流量颜色变红暗示危险
els.flow2.setAttribute('stroke', 'var(--warning-color)');
els.core2.setAttribute('fill', 'var(--warning-color)');
els.val2.setAttribute('fill', 'var(--warning-color)');
// PLC报警响应
els.plcStatus.textContent = '系统状态:检测到 2# 跑浆,启动预案';
els.plcStatus.setAttribute('fill', 'var(--warning-color)');
// 信号线闪烁传导
els.sigLines.style.opacity = '1';
els.sigS2Plc.setAttribute('stroke', 'var(--warning-color)');
} else if (mode === 'balance') {
updateUIButtons('btn-balance');
// 展现 IFR 状态: 动态截流与分配
els.shakerRect2.setAttribute('stroke', 'var(--adjust-color)');
els.mudOverflow.style.opacity = '0';
els.status2.textContent = '降载恢复中';
els.status2.setAttribute('fill', 'var(--adjust-color)');
// 流量粗细和速度调整 (核心)
els.flow1.className.baseVal = 'flow-dash fast';
els.flow3.className.baseVal = 'flow-dash fast';
els.flow2.className.baseVal = 'flow-dash slow';
els.flow2.setAttribute('stroke', 'var(--adjust-color)');
els.flow1.setAttribute('stroke', 'var(--success-color)');
els.flow3.setAttribute('stroke', 'var(--success-color)');
els.val1.textContent = '45.0%';
els.val2.textContent = '10.0%';
els.val3.textContent = '45.0%';
els.core2.setAttribute('fill', 'var(--adjust-color)');
els.val2.setAttribute('fill', 'var(--adjust-color)');
els.core1.setAttribute('fill', 'var(--success-color)');
els.val1.setAttribute('fill', 'var(--success-color)');
els.core3.setAttribute('fill', 'var(--success-color)');
els.val3.setAttribute('fill', 'var(--success-color)');
// 信号线下发控制指令
els.sigLines.style.opacity = '1';
els.sigS2Plc.setAttribute('stroke', '#37538c'); // 关闭报警上传,转为常规监控
// 计时器视觉效果
els.plcStatus.textContent = '系统状态:阀门重组完成 (理想解)';
els.plcStatus.setAttribute('fill', 'var(--success-color)');
timeCounter = 0;
timeInterval = setInterval(() => {
timeCounter += 0.1;
els.dataTime.textContent = timeCounter.toFixed(1) + 's (≤5s)';
if(timeCounter >= 3.2) { // 模拟切换在 3.2s 处完成
clearInterval(timeInterval);
els.dataError.textContent = '±4.2% (≤±5%)';
els.dataError.setAttribute('fill', 'var(--adjust-color)');
}
}, 100);
}
}
// 自动循环播放机制,符合页面加载后自动播放以及重载重播的要求
function startAutoPlay() {
let step = 0;
const sequence = ['normal', 'warning', 'balance'];
const delays = [4000, 3000, 5000]; // 每个状态停留时间
function nextStep() {
setMode(sequence[step]);
let currentDelay = delays[step];
step = (step + 1) % sequence.length;
autoPlayTimer = setTimeout(nextStep, currentDelay);
}
nextStep(); // 立即启动
}
// 绑定用户交互: 点击按钮时停止自动播放,改为手动控制
['btn-normal', 'btn-warning', 'btn-balance'].forEach(id => {
document.getElementById(id).addEventListener('click', () => {
clearTimeout(autoPlayTimer);
});
});
// 页面初始化
document.addEventListener('DOMContentLoaded', () => {
// 不依赖用户点击,自动启动演示流
startAutoPlay();
});
</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>振动筛智能联锁分配系统 - 最终理想解</title>
<style>
:root {
--bg-color: #0b1120;
--grid-color: #1e293b;
--text-main: #f8fafc;
--text-muted: #94a3b8;
--accent-blue: #38bdf8;
--accent-green: #34d399;
--accent-warning: #fbbf24;
--accent-danger: #f87171;
--pipe-bg: #334155;
--panel-bg: rgba(30, 41, 59, 0.7);
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
background-color: var(--bg-color);
color: var(--text-main);
font-family: 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
overflow: hidden;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh;
background-image:
linear-gradient(var(--grid-color) 1px, transparent 1px),
linear-gradient(90deg, var(--grid-color) 1px, transparent 1px);
background-size: 40px 40px;
background-position: center center;
}
.animation-container {
width: 100%;
max-width: 1400px;
height: 85vh;
display: flex;
justify-content: center;
align-items: center;
position: relative;
}
svg {
width: 100%;
height: 100%;
filter: drop-shadow(0 0 20px rgba(0,0,0,0.5));
}
/* SVG Typography */
.scada-title { font-size: 18px; font-weight: bold; fill: var(--accent-blue); letter-spacing: 2px; }
.scada-text { font-size: 14px; fill: var(--text-muted); }
.scada-value { font-size: 20px; font-weight: bold; font-family: monospace; fill: var(--accent-green); transition: fill 0.3s; }
.scada-value.warning { fill: var(--accent-danger); }
.component-label { font-size: 16px; font-weight: bold; fill: #fff; text-anchor: middle; }
.triz-label { font-size: 12px; fill: var(--accent-warning); opacity: 0.8; font-style: italic; }
/* Animations */
@keyframes dash-flow {
to { stroke-dashoffset: -100; }
}
@keyframes signal-flow {
to { stroke-dashoffset: -40; }
}
@keyframes pulse-danger {
0% { filter: drop-shadow(0 0 5px var(--accent-danger)); opacity: 0.8; }
50% { filter: drop-shadow(0 0 20px var(--accent-danger)); opacity: 1; }
100% { filter: drop-shadow(0 0 5px var(--accent-danger)); opacity: 0.8; }
}
.flow-line {
fill: none;
stroke-dasharray: 15, 10;
animation: dash-flow linear infinite;
transition: stroke-width 0.5s ease, stroke 0.5s ease;
}
.signal-line {
fill: none;
stroke: var(--accent-blue);
stroke-width: 2;
stroke-dasharray: 8, 8;
opacity: 0.3;
transition: opacity 0.3s, stroke 0.3s;
}
.signal-line.active {
opacity: 1;
animation: signal-flow 1s linear infinite;
stroke: var(--accent-danger);
}
.signal-line.active-control {
opacity: 1;
animation: signal-flow 0.5s linear infinite;
stroke: var(--accent-warning);
}
.shaker-box {
transition: all 0.5s ease;
}
.shaker-warning-light {
fill: #475569;
transition: fill 0.3s ease;
}
.shaker-warning-light.alert {
fill: var(--accent-danger);
animation: pulse-danger 1s infinite;
}
.valve-core {
transition: transform 0.5s ease, fill 0.5s ease;
transform-origin: center;
}
/* Controls */
.controls {
position: absolute;
bottom: 30px;
display: flex;
gap: 15px;
background: var(--panel-bg);
padding: 15px 30px;
border-radius: 50px;
border: 1px solid var(--grid-color);
backdrop-filter: blur(10px);
box-shadow: 0 10px 30px rgba(0,0,0,0.5);
z-index: 10;
}
.btn {
background: #1e293b;
color: var(--text-main);
border: 1px solid #334155;
padding: 10px 20px;
border-radius: 25px;
cursor: pointer;
font-size: 14px;
font-weight: 600;
transition: all 0.3s ease;
display: flex;
align-items: center;
gap: 8px;
}
.btn:hover {
background: #334155;
border-color: var(--accent-blue);
box-shadow: 0 0 15px rgba(56, 189, 248, 0.3);
}
.btn.active {
background: var(--accent-blue);
color: #000;
border-color: var(--accent-blue);
box-shadow: 0 0 20px rgba(56, 189, 248, 0.5);
}
.btn-danger.active {
background: var(--accent-danger);
border-color: var(--accent-danger);
box-shadow: 0 0 20px rgba(248, 113, 113, 0.5);
}
.triz-panel {
position: absolute;
top: 30px;
left: 30px;
width: 320px;
background: var(--panel-bg);
border-left: 4px solid var(--accent-warning);
padding: 20px;
border-radius: 4px;
backdrop-filter: blur(5px);
}
.triz-panel h3 { color: var(--accent-warning); margin-bottom: 10px; font-size: 16px; }
.triz-panel p { font-size: 13px; color: #cbd5e1; line-height: 1.6; margin-bottom: 8px; }
.triz-highlight { color: #fff; font-weight: bold; background: rgba(251, 191, 36, 0.2); padding: 2px 4px; border-radius: 3px; }
</style>
</head>
<body>
<div class="triz-panel">
<h3>TRIZ 最终理想解 (IFR)</h3>
<p>系统自动消除“局部跑浆”与“全局效率”的矛盾,实现<span class="triz-highlight">无人工干预的动态平衡</span>。</p>
<p><strong>资源利用:</strong>复用现有PLC与多路阀,以<span class="triz-highlight">极低复杂度</span>引入“流量再分配算法”,变被动停机为主动均衡。</p>
</div>
<div class="animation-container">
<svg viewBox="0 0 1400 800" preserveAspectRatio="xMidYMid meet">
<defs>
<!-- Glow Filters -->
<filter id="glow-blue" x="-20%" y="-20%" width="140%" height="140%">
<feGaussianBlur stdDeviation="5" result="blur" />
<feComposite in="SourceGraphic" in2="blur" operator="over" />
</filter>
<filter id="glow-green" x="-20%" y="-20%" width="140%" height="140%">
<feGaussianBlur stdDeviation="8" result="blur" />
<feComposite in="SourceGraphic" in2="blur" operator="over" />
</filter>
<!-- Gradients -->
<linearGradient id="mud-grad" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" stop-color="#92400e" />
<stop offset="100%" stop-color="#d97706" />
</linearGradient>
<linearGradient id="tank-grad" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" stop-color="#1e293b" />
<stop offset="100%" stop-color="#0f172a" />
</linearGradient>
<linearGradient id="shaker-grad" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" stop-color="#334155" />
<stop offset="100%" stop-color="#1e293b" />
</linearGradient>
<linearGradient id="plc-grad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#0f172a" />
<stop offset="100%" stop-color="#1e3a8a" />
</linearGradient>
<!-- Patterns -->
<pattern id="mesh" width="10" height="10" patternUnits="userSpaceOnUse">
<path d="M 10 0 L 0 10 M 0 0 L 10 10" fill="none" stroke="#475569" stroke-width="0.5" opacity="0.3"/>
</pattern>
</defs>
<!-- Background Grid (Optional enhancement within SVG) -->
<rect width="1400" height="800" fill="transparent" />
<!-- ================= PIPING BACKGROUNDS ================= -->
<!-- Main to Distributor -->
<path d="M 700 150 L 700 280" fill="none" stroke="var(--pipe-bg)" stroke-width="24" stroke-linecap="round" />
<!-- Distributor to Shakers -->
<path d="M 680 320 L 350 320 L 350 520" fill="none" stroke="var(--pipe-bg)" stroke-width="20" stroke-linejoin="round" />
<path d="M 700 320 L 700 520" fill="none" stroke="var(--pipe-bg)" stroke-width="20" stroke-linejoin="round" />
<path d="M 720 320 L 1050 320 L 1050 520" fill="none" stroke="var(--pipe-bg)" stroke-width="20" stroke-linejoin="round" />
<!-- ================= ANIMATED MUD FLOWS ================= -->
<!-- Main Flow -->
<path d="M 700 150 L 700 280" class="flow-line" stroke="#fbbf24" stroke-width="14" style="animation-duration: 1.5s;" stroke-linecap="round" />
<!-- Branches -->
<path id="flow-A" d="M 680 320 L 350 320 L 350 520" class="flow-line" stroke="#fbbf24" stroke-width="10" style="animation-duration: 2s;" stroke-linejoin="round" />
<path id="flow-B" d="M 700 320 L 700 520" class="flow-line" stroke="#fbbf24" stroke-width="10" style="animation-duration: 2s;" stroke-linejoin="round" />
<path id="flow-C" d="M 720 320 L 1050 320 L 1050 520" class="flow-line" stroke="#fbbf24" stroke-width="10" style="animation-duration: 2s;" stroke-linejoin="round" />
<!-- ================= SIGNAL LINES ================= -->
<!-- Shaker to PLC Sensors -->
<path id="sig-sensor-A" d="M 350 520 L 350 480 L 1150 480 L 1150 250" class="signal-line" />
<path id="sig-sensor-B" d="M 700 520 L 700 490 L 1150 490 L 1150 250" class="signal-line" />
<path id="sig-sensor-C" d="M 1050 520 L 1050 500 L 1150 500 L 1150 250" class="signal-line" />
<!-- PLC to Valve Controls -->
<path id="sig-ctrl-A" d="M 1150 250 L 1150 200 L 670 200 L 670 290" class="signal-line" />
<path id="sig-ctrl-B" d="M 1150 250 L 1150 210 L 700 210 L 700 290" class="signal-line" />
<path id="sig-ctrl-C" d="M 1150 250 L 1150 220 L 730 220 L 730 290" class="signal-line" />
<!-- ================= COMPONENTS ================= -->
<!-- Buffer Tank (Inlet) -->
<g transform="translate(600, 50)">
<rect width="200" height="100" rx="10" fill="url(#tank-grad)" stroke="#475569" stroke-width="2" />
<rect x="10" y="10" width="180" height="80" rx="5" fill="url(#mesh)" />
<text x="100" y="55" class="component-label">缓冲罐 (泥浆源)</text>
<text x="100" y="80" font-size="12" fill="#94a3b8" text-anchor="middle">总流量: 100%</text>
</g>
<!-- Distributor (Multi-way Valve) -->
<g transform="translate(620, 270)">
<rect width="160" height="60" rx="8" fill="#1e293b" stroke="#64748b" stroke-width="2" />
<text x="80" y="20" font-size="12" fill="#cbd5e1" text-anchor="middle" font-weight="bold">多路执行器阀组</text>
<!-- Valve A -->
<circle cx="50" cy="40" r="12" fill="#334155" />
<circle id="valve-core-A" class="valve-core" cx="50" cy="40" r="8" fill="var(--accent-green)" />
<!-- Valve B -->
<circle cx="80" cy="40" r="12" fill="#334155" />
<circle id="valve-core-B" class="valve-core" cx="80" cy="40" r="8" fill="var(--accent-green)" />
<!-- Valve C -->
<circle cx="110" cy="40" r="12" fill="#334155" />
<circle id="valve-core-C" class="valve-core" cx="110" cy="40" r="8" fill="var(--accent-green)" />
</g>
<!-- Multiple Flow Meters -->
<g transform="translate(280, 420)">
<rect width="50" height="30" rx="4" fill="#0f172a" stroke="#38bdf8" stroke-width="1" />
<text x="25" y="20" font-size="10" fill="#38bdf8" text-anchor="middle">流量计</text>
</g>
<g transform="translate(630, 420)">
<rect width="50" height="30" rx="4" fill="#0f172a" stroke="#38bdf8" stroke-width="1" />
<text x="25" y="20" font-size="10" fill="#38bdf8" text-anchor="middle">流量计</text>
</g>
<g transform="translate(980, 420)">
<rect width="50" height="30" rx="4" fill="#0f172a" stroke="#38bdf8" stroke-width="1" />
<text x="25" y="20" font-size="10" fill="#38bdf8" text-anchor="middle">流量计</text>
</g>
<!-- Shakers -->
<!-- Shaker A -->
<g transform="translate(270, 520)" class="shaker-box" id="shaker-group-A">
<rect width="160" height="120" rx="8" fill="url(#shaker-grad)" stroke="#475569" stroke-width="2" />
<rect x="10" y="40" width="140" height="70" fill="#0f172a" rx="4" />
<!-- Filter meshes -->
<line x1="20" y1="50" x2="140" y2="50" stroke="#475569" stroke-width="2" stroke-dasharray="4,2"/>
<line x1="20" y1="70" x2="140" y2="70" stroke="#475569" stroke-width="2" stroke-dasharray="4,2"/>
<line x1="20" y1="90" x2="140" y2="90" stroke="#475569" stroke-width="2" stroke-dasharray="4,2"/>
<text x="80" y="25" class="component-label">振动筛 A</text>
<circle id="light-A" cx="140" cy="20" r="6" class="shaker-warning-light" />
<text id="shaker-val-A" x="80" y="105" font-size="18" fill="var(--accent-green)" text-anchor="middle" font-weight="bold">33.3%</text>
</g>
<!-- Shaker B -->
<g transform="translate(620, 520)" class="shaker-box" id="shaker-group-B">
<rect width="160" height="120" rx="8" fill="url(#shaker-grad)" stroke="#475569" stroke-width="2" />
<rect x="10" y="40" width="140" height="70" fill="#0f172a" rx="4" />
<line x1="20" y1="50" x2="140" y2="50" stroke="#475569" stroke-width="2" stroke-dasharray="4,2"/>
<line x1="20" y1="70" x2="140" y2="70" stroke="#475569" stroke-width="2" stroke-dasharray="4,2"/>
<line x1="20" y1="90" x2="140" y2="90" stroke="#475569" stroke-width="2" stroke-dasharray="4,2"/>
<text x="80" y="25" class="component-label">振动筛 B</text>
<circle id="light-B" cx="140" cy="20" r="6" class="shaker-warning-light" />
<text id="shaker-val-B" x="80" y="105" font-size="18" fill="var(--accent-green)" text-anchor="middle" font-weight="bold">33.3%</text>
</g>
<!-- Shaker C -->
<g transform="translate(970, 520)" class="shaker-box" id="shaker-group-C">
<rect width="160" height="120" rx="8" fill="url(#shaker-grad)" stroke="#475569" stroke-width="2" />
<rect x="10" y="40" width="140" height="70" fill="#0f172a" rx="4" />
<line x1="20" y1="50" x2="140" y2="50" stroke="#475569" stroke-width="2" stroke-dasharray="4,2"/>
<line x1="20" y1="70" x2="140" y2="70" stroke="#475569" stroke-width="2" stroke-dasharray="4,2"/>
<line x1="20" y1="90" x2="140" y2="90" stroke="#475569" stroke-width="2" stroke-dasharray="4,2"/>
<text x="80" y="25" class="component-label">振动筛 C</text>
<circle id="light-C" cx="140" cy="20" r="6" class="shaker-warning-light" />
<text id="shaker-val-C" x="80" y="105" font-size="18" fill="var(--accent-green)" text-anchor="middle" font-weight="bold">33.3%</text>
</g>
<!-- ================= SCADA / PLC PANEL ================= -->
<g transform="translate(1050, 50)" filter="url(#glow-blue)">
<!-- PLC Body -->
<rect width="280" height="200" rx="12" fill="url(#plc-grad)" stroke="var(--accent-blue)" stroke-width="2" />
<rect x="10" y="40" width="260" height="150" rx="6" fill="#020617" />
<text x="140" y="25" class="scada-title" text-anchor="middle">总控 PLC / SCADA</text>
<!-- SCADA Data -->
<text x="20" y="65" class="scada-text">实时流量分配:</text>
<text x="30" y="90" class="scada-text">筛 A:</text>
<text id="scada-a" x="80" y="92" class="scada-value">33.3%</text>
<text x="140" y="90" class="scada-text">筛 B:</text>
<text id="scada-b" x="190" y="92" class="scada-value">33.3%</text>
<text x="30" y="120" class="scada-text">筛 C:</text>
<text id="scada-c" x="80" y="122" class="scada-value">33.3%</text>
<line x1="20" y1="135" x2="260" y2="135" stroke="#334155" stroke-dasharray="2,2" />
<text x="20" y="160" class="scada-text">分配误差:</text>
<text id="scada-error" x="90" y="162" class="scada-value" fill="var(--accent-green)">≤±2.1%</text>
<text x="20" y="180" class="scada-text">切换时间:</text>
<text id="scada-time" x="90" y="182" class="scada-value" fill="var(--accent-blue)">- -</text>
<text id="scada-status" x="200" y="172" font-size="16" fill="var(--accent-green)" font-weight="bold" text-anchor="middle">系统均衡</text>
</g>
</svg>
</div>
<div class="controls">
<button class="btn active" id="btn-normal" onclick="forceState('normal')">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path><polyline points="22 4 12 14.01 9 11.01"></polyline></svg>
正常均分
</button>
<button class="btn btn-danger" id="btn-overload-b" onclick="forceState('overloadB')">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"></path><line x1="12" y1="9" x2="12" y2="13"></line><line x1="12" y1="17" x2="12.01" y2="17"></line></svg>
模拟筛B跑浆
</button>
<button class="btn btn-danger" id="btn-overload-a" onclick="forceState('overloadA')">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"></path><line x1="12" y1="9" x2="12" y2="13"></line><line x1="12" y1="17" x2="12.01" y2="17"></line></svg>
模拟筛A跑浆
</button>
</div>
<script>
// Constants for configurations
const COLORS = {
green: 'var(--accent-green)',
red: 'var(--accent-danger)',
orange: 'var(--accent-warning)',
muted: '#475569'
};
let autoPlayTimer = null;
let currentStateIndex = 0;
const stateSequence = ['normal', 'overloadB', 'normal', 'overloadA'];
// DOM Elements
const els = {
flows: { A: document.getElementById('flow-A'), B: document.getElementById('flow-B'), C: document.getElementById('flow-C') },
valves: { A: document.getElementById('valve-core-A'), B: document.getElementById('valve-core-B'), C: document.getElementById('valve-core-C') },
lights: { A: document.getElementById('light-A'), B: document.getElementById('light-B'), C: document.getElementById('light-C') },
scada: { A: document.getElementById('scada-a'), B: document.getElementById('scada-b'), C: document.getElementById('scada-c'),
err: document.getElementById('scada-error'), time: document.getElementById('scada-time'), status: document.getElementById('scada-status') },
shakerVals: { A: document.getElementById('shaker-val-A'), B: document.getElementById('shaker-val-B'), C: document.getElementById('shaker-val-C') },
sigsSensor: { A: document.getElementById('sig-sensor-A'), B: document.getElementById('sig-sensor-B'), C: document.getElementById('sig-sensor-C') },
sigsCtrl: { A: document.getElementById('sig-ctrl-A'), B: document.getElementById('sig-ctrl-B'), C: document.getElementById('sig-ctrl-C') },
btns: { normal: document.getElementById('btn-normal'), overloadB: document.getElementById('btn-overload-b'), overloadA: document.getElementById('btn-overload-a') }
};
// State Definitions
const states = {
normal: {
flows: { A: { w: 10, spd: '2s' }, B: { w: 10, spd: '2s' }, C: { w: 10, spd: '2s' } },
vals: { A: '33.3%', B: '33.3%', C: '33.3%' },
valves: { A: COLORS.green, B: COLORS.green, C: COLORS.green },
lights: { A: false, B: false, C: false },
scadaStatus: '系统均衡',
scadaColor: COLORS.green,
time: '- -',
activeBtn: 'btn-normal'
},
overloadB: {
flows: { A: { w: 14, spd: '1s' }, B: { w: 4, spd: '4s' }, C: { w: 14, spd: '1s' } },
vals: { A: '45.0%', B: '10.0%', C: '45.0%' },
valves: { A: COLORS.green, B: COLORS.red, C: COLORS.green },
lights: { A: false, B: true, C: false },
scadaStatus: '联锁干预',
scadaColor: COLORS.orange,
time: '1.2s',
activeBtn: 'btn-overload-b'
},
overloadA: {
flows: { A: { w: 4, spd: '4s' }, B: { w: 14, spd: '1s' }, C: { w: 14, spd: '1s' } },
vals: { A: '10.0%', B: '45.0%', C: '45.0%' },
valves: { A: COLORS.red, B: COLORS.green, C: COLORS.green },
lights: { A: true, B: false, C: false },
scadaStatus: '联锁干预',
scadaColor: COLORS.orange,
time: '1.4s',
activeBtn: 'btn-overload-a'
}
};
function updateElementState(stateData, key) {
// Update Pipe Flows
els.flows[key].style.strokeWidth = stateData.flows[key].w;
els.flows[key].style.animationDuration = stateData.flows[key].spd;
// Update Text Values
els.scada[key].textContent = stateData.vals[key];
els.shakerVals[key].textContent = stateData.vals[key];
// Update Colors & Warnings
els.valves[key].style.fill = stateData.valves[key];
if(stateData.lights[key]) {
els.lights[key].classList.add('alert');
els.scada[key].classList.add('warning');
els.shakerVals[key].style.fill = COLORS.red;
els.sigsSensor[key].classList.add('active');
els.sigsCtrl[key].classList.add('active-control');
} else {
els.lights[key].classList.remove('alert');
els.scada[key].classList.remove('warning');
els.shakerVals[key].style.fill = COLORS.green;
els.sigsSensor[key].classList.remove('active');
els.sigsCtrl[key].classList.remove('active-control');
}
}
function applyState(stateName) {
const data = states[stateName];
['A', 'B', 'C'].forEach(key => updateElementState(data, key));
els.scada.status.textContent = data.scadaStatus;
els.scada.status.style.fill = data.scadaColor;
if(stateName !== 'normal') {
// Simulate time counter animation
let t = 0.0;
let finalT = parseFloat(data.time);
let intv = setInterval(() => {
t += 0.2;
if(t >= finalT) {
t = finalT;
clearInterval(intv);
}
els.scada.time.textContent = t.toFixed(1) + 's';
}, 50);
} else {
els.scada.time.textContent = data.time;
}
// Update UI Buttons
Object.values(els.btns).forEach(btn => btn.classList.remove('active'));
document.getElementById(data.activeBtn).classList.add('active');
}
function playSequence() {
applyState(stateSequence[currentStateIndex]);
currentStateIndex = (currentStateIndex + 1) % stateSequence.length;
autoPlayTimer = setTimeout(playSequence, 6000); // Change state every 6 seconds
}
function forceState(stateName) {
clearTimeout(autoPlayTimer);
applyState(stateName);
// Resume autoplay after manual intervention
setTimeout(() => {
currentStateIndex = stateSequence.indexOf(stateName) + 1;
if(currentStateIndex >= stateSequence.length) currentStateIndex = 0;
playSequence();
}, 8000);
}
// Initialize: Ensure auto-play on load as requested
document.addEventListener('DOMContentLoaded', () => {
playSequence();
});
</script>
</body>
</html>
这里为您提供一份基于“深色工业SCADA控制系统”风格(Dark Industrial Cyber-Physical)的高保真 SVG 交互式动画代码。
设觉设计与 TRIZ (IFR) 结合点说明:
- 最终理想解 (IFR) 呈现:动画不展示传统的人工发现与排查过程,而是直接展示“智能化闭环”的最终形态——当局部(2号振动筛)发生跑浆风险时,系统无需人工干预,通过自身算法瞬间完成流量全局重分配。
- 资源巧妙利用:突出展示“总控PLC + 多路分配阀”作为关键资源,如何在极短的切换时间(<5s)内,不增加额外处理设备,仅靠分配逻辑就化解了局部负荷过大的矛盾。
- 视觉引导:采用霓虹荧光色系进行状态区分。正常流量为青色(Cyan),跑浆告警为橙红色(Orange/Red),重分配后的最优状态为翠绿色(Green)。PLC信号脉冲和阀门动态缩放直观呈现了系统的控制逻辑。
- 交互体验:内置了状态自循环(默认自动播放满足要求),同时也提供了一个右上角的“SCADA 手动介入面板”,允许用户亲自拖拽滑块,体验流量动态平衡和误差计算的过程。
<!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: #0b0f19;
--grid-color: #1a2235;
--text-main: #94a3b8;
--text-highlight: #f8fafc;
--accent-cyan: #00f0ff;
--accent-green: #00ffa3;
--accent-red: #ff3c00;
--pipe-base: #2a364a;
--panel-bg: rgba(15, 23, 42, 0.85);
}
body, html {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
background-color: var(--bg-color);
font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
color: var(--text-main);
}
.scada-container {
position: relative;
width: 100%;
max-width: 1400px;
height: 100%;
max-height: 900px;
background-image:
linear-gradient(var(--grid-color) 1px, transparent 1px),
linear-gradient(90deg, var(--grid-color) 1px, transparent 1px);
background-size: 30px 30px;
box-shadow: inset 0 0 100px rgba(0,0,0,0.8);
border: 1px solid #1e293b;
border-radius: 8px;
overflow: hidden;
}
/* Top HUD Panel */
.hud-top {
position: absolute;
top: 20px;
left: 30px;
z-index: 10;
}
.hud-title {
font-size: 24px;
font-weight: 600;
color: var(--accent-cyan);
letter-spacing: 2px;
text-transform: uppercase;
text-shadow: 0 0 10px rgba(0, 240, 255, 0.3);
margin-bottom: 5px;
}
.hud-subtitle {
font-size: 12px;
font-family: 'Courier New', Courier, monospace;
color: #64748b;
}
/* SCADA Interactive Panel */
.scada-panel {
position: absolute;
top: 20px;
right: 30px;
background: var(--panel-bg);
border: 1px solid #334155;
padding: 20px;
border-radius: 8px;
backdrop-filter: blur(10px);
width: 320px;
z-index: 10;
box-shadow: 0 10px 30px rgba(0,0,0,0.5);
}
.panel-header {
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid #334155;
padding-bottom: 10px;
margin-bottom: 15px;
}
.mode-toggle {
display: flex;
gap: 10px;
}
.btn-mode {
background: transparent;
border: 1px solid #475569;
color: #94a3b8;
padding: 5px 10px;
font-size: 12px;
cursor: pointer;
border-radius: 4px;
transition: all 0.3s;
}
.btn-mode.active {
background: rgba(0, 240, 255, 0.1);
border-color: var(--accent-cyan);
color: var(--accent-cyan);
box-shadow: 0 0 8px rgba(0, 240, 255, 0.2);
}
.data-row {
display: flex;
justify-content: space-between;
margin-bottom: 10px;
font-family: 'Courier New', Courier, monospace;
font-size: 13px;
}
.data-val {
color: var(--text-highlight);
font-weight: bold;
}
.data-val.cyan { color: var(--accent-cyan); }
.data-val.green { color: var(--accent-green); }
.data-val.red { color: var(--accent-red); }
.slider-group {
margin-top: 15px;
display: none; /* Shown in manual mode */
}
.slider-group.active { display: block; }
.slider-row { margin-bottom: 12px; }
.slider-label {
font-size: 12px;
margin-bottom: 5px;
display: flex;
justify-content: space-between;
}
input[type=range] {
width: 100%;
-webkit-appearance: none;
background: #1e293b;
height: 4px;
border-radius: 2px;
outline: none;
}
input[type=range]::-webkit-slider-thumb {
-webkit-appearance: none;
width: 14px;
height: 14px;
border-radius: 50%;
background: var(--accent-cyan);
cursor: pointer;
box-shadow: 0 0 5px var(--accent-cyan);
}
/* SVG Animation Specifics */
svg {
width: 100%;
height: 100%;
}
.flow-dash {
stroke-dasharray: 10 15;
animation: flowMove 1s linear infinite;
}
@keyframes flowMove {
to { stroke-dashoffset: -25; }
}
.signal-dash {
stroke-dasharray: 5 10;
animation: signalMove 0.5s linear infinite;
}
@keyframes signalMove {
to { stroke-dashoffset: -15; }
}
.shaker-mesh {
animation: vibrate 0.1s linear infinite;
}
@keyframes vibrate {
0% { transform: translate(0, 0); }
50% { transform: translate(1px, -1px); }
100% { transform: translate(0, 0); }
}
.plc-pulse {
animation: plcGlow 2s ease-in-out infinite alternate;
}
@keyframes plcGlow {
from { filter: drop-shadow(0 0 5px rgba(0,240,255,0.2)); }
to { filter: drop-shadow(0 0 20px rgba(0,240,255,0.6)); }
}
</style>
</head>
<body>
<div class="scada-container">
<!-- Top Left HUD -->
<div class="hud-top">
<div class="hud-title">VIBRATION SHAKER INTERLOCK MGT</div>
<div class="hud-subtitle">TOTAL FLOW ALLOCATION ALGORITHM V2.4 // STATUS: ONLINE</div>
</div>
<!-- Top Right SCADA Control Panel -->
<div class="scada-panel">
<div class="panel-header">
<span style="font-size: 14px; font-weight: bold; color: #cbd5e1;">系统状态监控</span>
<div class="mode-toggle">
<button class="btn-mode active" id="btn-auto">AUTO</button>
<button class="btn-mode" id="btn-manual">MANUAL</button>
</div>
</div>
<div class="data-row">
<span>主回路总流量:</span>
<span class="data-val cyan" id="txt-total-flow">1500 L/min</span>
</div>
<div class="data-row">
<span>分配误差界限:</span>
<span class="data-val">≤ ±5%</span>
</div>
<div class="data-row">
<span>当前最大分配误差:</span>
<span class="data-val green" id="txt-error">±1.2%</span>
</div>
<div class="data-row">
<span>动作切换耗时:</span>
<span class="data-val" id="txt-time">-- s (限值≤5s)</span>
</div>
<div class="data-row" style="margin-top: 15px; border-top: 1px dashed #334155; padding-top: 10px;">
<span>系统状态:</span>
<span class="data-val cyan" id="txt-status">正常均衡运行</span>
</div>
<div class="slider-group" id="manual-controls">
<div class="slider-row">
<div class="slider-label"><span>1#分配阀开度</span><span id="val-v1">33.3%</span></div>
<input type="range" id="slider-v1" min="10" max="80" value="33.3" step="0.1">
</div>
<div class="slider-row">
<div class="slider-label"><span>2#分配阀开度</span><span id="val-v2">33.3%</span></div>
<input type="range" id="slider-v2" min="10" max="80" value="33.3" step="0.1">
</div>
<div class="slider-row">
<div class="slider-label"><span>3#分配阀开度</span><span id="val-v3">33.3%</span></div>
<input type="range" id="slider-v3" min="10" max="80" value="33.3" step="0.1">
</div>
<div style="font-size: 11px; color: #64748b; margin-top: 10px; line-height: 1.4;">
* 手动模式下,调整单阀开度将自动通过PLC算法补偿其他阀门,维持总进料量100%恒定。
</div>
</div>
</div>
<!-- Main SVG Canvas -->
<svg viewBox="0 0 1400 900" preserveAspectRatio="xMidYMid meet" id="main-svg">
<defs>
<!-- Glow Filters -->
<filter id="glow-cyan" x="-20%" y="-20%" width="140%" height="140%">
<feGaussianBlur stdDeviation="8" result="blur" />
<feComposite in="SourceGraphic" in2="blur" operator="over" />
</filter>
<filter id="glow-red" x="-20%" y="-20%" width="140%" height="140%">
<feGaussianBlur stdDeviation="8" result="blur" />
<feComposite in="SourceGraphic" in2="blur" operator="over" />
</filter>
<filter id="glow-green" x="-20%" y="-20%" width="140%" height="140%">
<feGaussianBlur stdDeviation="8" result="blur" />
<feComposite in="SourceGraphic" in2="blur" operator="over" />
</filter>
<!-- Pipe Gradient -->
<linearGradient id="pipe-grad" 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>
<linearGradient id="pipe-grad-v" 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>
<!-- Valve Symbol Component -->
<g id="valve">
<polygon points="-15,-15 15,15 -15,15 15,-15" fill="#475569" stroke="#94a3b8" stroke-width="2"/>
<line x1="0" y1="0" x2="0" y2="-25" stroke="#94a3b8" stroke-width="3"/>
<rect x="-12" y="-35" width="24" height="10" fill="#0ea5e9" rx="2"/>
</g>
</defs>
<!-- ================= BACKGROUND STRUCTURAL PIPES ================= -->
<!-- Main Feed Pipe -->
<rect x="670" y="80" width="60" height="200" fill="url(#pipe-grad-v)" stroke="#0f172a" stroke-width="2"/>
<text x="740" y="150" fill="#64748b" font-family="monospace" font-size="14">MAIN FEED / 缓冲罐来料</text>
<!-- Manifold Horizontal -->
<rect x="270" y="250" width="860" height="60" fill="url(#pipe-grad)" stroke="#0f172a" stroke-width="2"/>
<!-- Branches (1, 2, 3) -->
<rect x="270" y="310" width="60" height="250" fill="url(#pipe-grad-v)" stroke="#0f172a" stroke-width="2"/>
<rect x="670" y="310" width="60" height="250" fill="url(#pipe-grad-v)" stroke="#0f172a" stroke-width="2"/>
<rect x="1070" y="310" width="60" height="250" fill="url(#pipe-grad-v)" stroke="#0f172a" stroke-width="2"/>
<!-- ================= ACTIVE FLUID FLOW PATHS ================= -->
<!-- Main Flow -->
<path d="M 700 80 L 700 280" stroke="var(--accent-cyan)" stroke-width="24" fill="none" class="flow-dash" filter="url(#glow-cyan)" id="flow-main"/>
<!-- Manifold Distribution Flows -->
<path d="M 700 280 L 300 280 L 300 560" stroke="var(--accent-cyan)" stroke-width="12" fill="none" class="flow-dash" filter="url(#glow-cyan)" id="flow-branch1"/>
<path d="M 700 280 L 700 560" stroke="var(--accent-cyan)" stroke-width="12" fill="none" class="flow-dash" filter="url(#glow-cyan)" id="flow-branch2"/>
<path d="M 700 280 L 1100 280 L 1100 560" stroke="var(--accent-cyan)" stroke-width="12" fill="none" class="flow-dash" filter="url(#glow-cyan)" id="flow-branch3"/>
<!-- ================= PLC CONTROLLER ================= -->
<g transform="translate(600, 750)" class="plc-pulse">
<rect x="-150" y="-50" width="300" height="100" fill="#0f172a" stroke="var(--accent-cyan)" stroke-width="2" rx="8"/>
<rect x="-140" y="-40" width="280" height="80" fill="#1e293b" rx="4"/>
<text x="0" y="-10" fill="#f8fafc" font-size="20" font-weight="bold" text-anchor="middle">TOTAL CONTROL PLC</text>
<text x="0" y="15" fill="var(--accent-cyan)" font-size="12" font-family="monospace" text-anchor="middle">最优分配算法运算核心</text>
<!-- PLC Status Leds -->
<circle cx="-110" cy="20" r="5" fill="var(--accent-cyan)" id="led-normal"/>
<text x="-95" y="24" fill="#64748b" font-size="10">SYS OK</text>
<circle cx="-30" cy="20" r="5" fill="#334155" id="led-warn"/>
<text x="-15" y="24" fill="#64748b" font-size="10">OVERLOAD</text>
<circle cx="50" cy="20" r="5" fill="#334155" id="led-calc"/>
<text x="65" y="24" fill="#64748b" font-size="10">CALC</text>
</g>
<!-- ================= SIGNAL LINES ================= -->
<g stroke="#0ea5e9" stroke-width="1.5" fill="none" class="signal-dash" opacity="0.6" id="signal-lines">
<!-- To Valve 1 -->
<path d="M 450 750 L 370 750 L 370 420 L 320 420" />
<!-- To Valve 2 -->
<path d="M 600 700 L 600 650 L 730 650 L 730 420 L 715 420" />
<!-- To Valve 3 -->
<path d="M 750 750 L 1030 750 L 1030 420 L 1080 420" />
<!-- Feedback from flow meters -->
<path d="M 320 350 L 400 350 L 400 690 L 550 690" stroke="#10b981" />
<path d="M 720 350 L 770 350 L 770 690 L 650 690" stroke="#10b981" />
<path d="M 1080 350 L 980 350 L 980 690 L 680 690" stroke="#10b981" />
</g>
<!-- ================= HARDWARE COMPONENTS (VALVES & METERS) ================= -->
<!-- Branch 1 -->
<g transform="translate(300, 350)">
<circle cx="0" cy="0" r="25" fill="#1e293b" stroke="#64748b" stroke-width="2"/>
<text x="0" y="5" fill="#0ea5e9" font-family="monospace" font-size="12" text-anchor="middle" id="meter1">33%</text>
<text x="-40" y="5" fill="#94a3b8" font-size="12" text-anchor="end">流量计(FI-01)</text>
</g>
<g transform="translate(300, 420)">
<use href="#valve" />
<text x="-40" y="0" fill="#94a3b8" font-size="12" text-anchor="end">电液分配阀(XV-01)</text>
<text x="40" y="0" fill="var(--accent-cyan)" font-size="14" font-weight="bold" id="valve-val-1">33%</text>
</g>
<!-- Branch 2 -->
<g transform="translate(700, 350)">
<circle cx="0" cy="0" r="25" fill="#1e293b" stroke="#64748b" stroke-width="2"/>
<text x="0" y="5" fill="#0ea5e9" font-family="monospace" font-size="12" text-anchor="middle" id="meter2">33%</text>
<text x="40" y="5" fill="#94a3b8" font-size="12" text-anchor="start">流量计(FI-02)</text>
</g>
<g transform="translate(700, 420)">
<use href="#valve" />
<text x="40" y="0" fill="#94a3b8" font-size="12" text-anchor="start">电液分配阀(XV-02)</text>
<text x="-40" y="0" fill="var(--accent-cyan)" font-size="14" font-weight="bold" text-anchor="end" id="valve-val-2">33%</text>
</g>
<!-- Branch 3 -->
<g transform="translate(1100, 350)">
<circle cx="0" cy="0" r="25" fill="#1e293b" stroke="#64748b" stroke-width="2"/>
<text x="0" y="5" fill="#0ea5e9" font-family="monospace" font-size="12" text-anchor="middle" id="meter3">33%</text>
<text x="40" y="5" fill="#94a3b8" font-size="12" text-anchor="start">流量计(FI-03)</text>
</g>
<g transform="translate(1100, 420)">
<use href="#valve" />
<text x="40" y="0" fill="#94a3b8" font-size="12" text-anchor="start">电液分配阀(XV-03)</text>
<text x="-40" y="0" fill="var(--accent-cyan)" font-size="14" font-weight="bold" text-anchor="end" id="valve-val-3">33%</text>
</g>
<!-- ================= SHALE SHAKERS ================= -->
<!-- Shaker Base Function -->
<defs>
<g id="shaker-model">
<!-- Base Frame -->
<path d="M -80 0 L 80 0 L 90 60 L -90 60 Z" fill="#1e293b" stroke="#475569" stroke-width="2"/>
<!-- Support Legs -->
<line x1="-70" y1="60" x2="-70" y2="90" stroke="#475569" stroke-width="8"/>
<line x1="70" y1="60" x2="70" y2="90" stroke="#475569" stroke-width="8"/>
<rect x="-85" y="90" width="30" height="10" fill="#0f172a"/>
<rect x="55" y="90" width="30" height="10" fill="#0f172a"/>
<!-- Vibrating Mesh -->
<path d="M -75 10 L 75 10 M -78 20 L 78 20 M -80 30 L 80 30" stroke="#94a3b8" stroke-width="2" class="shaker-mesh" stroke-dasharray="4 4"/>
</g>
</defs>
<!-- Shaker 1 -->
<g transform="translate(300, 560)">
<!-- Mud Pool Volume Inside Shaker -->
<path d="M -60 50 L 60 50 L 70 50 L -70 50 Z" fill="var(--accent-cyan)" opacity="0.6" filter="url(#glow-cyan)" id="mud-pool-1" style="transition: all 0.5s;"/>
<use href="#shaker-model" />
<text x="0" y="120" fill="#f8fafc" font-size="14" font-weight="bold" text-anchor="middle">1# 振动筛</text>
<text x="0" y="140" fill="#64748b" font-size="12" text-anchor="middle">承载率: <tspan id="load-1">33%</tspan></text>
</g>
<!-- Shaker 2 (The one that will overload) -->
<g transform="translate(700, 560)">
<path d="M -60 50 L 60 50 L 70 50 L -70 50 Z" fill="var(--accent-cyan)" opacity="0.6" filter="url(#glow-cyan)" id="mud-pool-2" style="transition: all 0.5s;"/>
<use href="#shaker-model" />
<text x="0" y="120" fill="#f8fafc" font-size="14" font-weight="bold" text-anchor="middle">2# 振动筛</text>
<text x="0" y="140" fill="#64748b" font-size="12" text-anchor="middle">承载率: <tspan id="load-2">33%</tspan></text>
<!-- Alert Icon (Hidden normally) -->
<g id="alert-icon-2" opacity="0" style="transition: opacity 0.3s;">
<circle cx="0" cy="-30" r="16" fill="var(--accent-red)" />
<text x="0" y="-25" fill="#fff" font-size="16" font-weight="bold" text-anchor="middle">!</text>
<text x="0" y="-55" fill="var(--accent-red)" font-size="14" font-weight="bold" text-anchor="middle" filter="url(#glow-red)">跑浆风险告警</text>
</g>
</g>
<!-- Shaker 3 -->
<g transform="translate(1100, 560)">
<path d="M -60 50 L 60 50 L 70 50 L -70 50 Z" fill="var(--accent-cyan)" opacity="0.6" filter="url(#glow-cyan)" id="mud-pool-3" style="transition: all 0.5s;"/>
<use href="#shaker-model" />
<text x="0" y="120" fill="#f8fafc" font-size="14" font-weight="bold" text-anchor="middle">3# 振动筛</text>
<text x="0" y="140" fill="#64748b" font-size="12" text-anchor="middle">承载率: <tspan id="load-3">33%</tspan></text>
</g>
</svg>
</div>
<script>
// --- State Management ---
let mode = 'auto'; // 'auto' | 'manual'
let autoPhase = 0;
let autoTimer = null;
let valves = [33.3, 33.3, 33.4]; // Current distribution
const BASE_THICKNESS = 36; // Max stroke width for 100% flow
// --- DOM Elements ---
const els = {
flows: [document.getElementById('flow-branch1'), document.getElementById('flow-branch2'), document.getElementById('flow-branch3')],
meters: [document.getElementById('meter1'), document.getElementById('meter2'), document.getElementById('meter3')],
valveVals: [document.getElementById('valve-val-1'), document.getElementById('valve-val-2'), document.getElementById('valve-val-3')],
mudPools: [document.getElementById('mud-pool-1'), document.getElementById('mud-pool-2'), document.getElementById('mud-pool-3')],
loads: [document.getElementById('load-1'), document.getElementById('load-2'), document.getElementById('load-3')],
alert2: document.getElementById('alert-icon-2'),
ledNormal: document.getElementById('led-normal'),
ledWarn: document.getElementById('led-warn'),
ledCalc: document.getElementById('led-calc'),
signalLines: document.getElementById('signal-lines'),
txtError: document.getElementById('txt-error'),
txtTime: document.getElementById('txt-time'),
txtStatus: document.getElementById('txt-status'),
sliders: [document.getElementById('slider-v1'), document.getElementById('slider-v2'), document.getElementById('slider-v3')],
sliderVals: [document.getElementById('val-v1'), document.getElementById('val-v2'), document.getElementById('val-v3')]
};
// --- Core Logic: Update UI based on Valve Distribution ---
function updateSystem(v, isAlarm = false, isOptimized = false) {
valves = v;
let maxError = Math.abs(valves[0] - 33.3);
maxError = Math.max(maxError, Math.abs(valves[1] - 33.3), Math.abs(valves[2] - 33.3));
for (let i=0; i<3; i++) {
let p = valves[i];
let width = (p / 100) * BASE_THICKNESS;
if(width < 2) width = 2; // Min visibility
// Update Flows
els.flows[i].style.strokeWidth = width + 'px';
// Update Texts
let pStr = p.toFixed(1) + '%';
els.meters[i].textContent = pStr;
els.valveVals[i].textContent = pStr;
els.loads[i].textContent = pStr;
if(mode === 'manual') {
els.sliders[i].value = p.toFixed(1);
els.sliderVals[i].textContent = pStr;
}
// Update Mud Pool Visuals (d attribute animation)
// Normal base is M -75 40 L 75 40 (approx). Height scales with %
let h = 50 - (p / 50) * 45; // Higher % = lower Y value (higher pool)
if (h < 5) h = 5;
let d = `M -60 ${h} L 60 ${h} L 70 50 L -70 50 Z`;
els.mudPools[i].setAttribute('d', d);
// Color logic
if (isAlarm && i === 1) {
els.flows[i].setAttribute('stroke', 'var(--accent-red)');
els.flows[i].setAttribute('filter', 'url(#glow-red)');
els.mudPools[i].setAttribute('fill', 'var(--accent-red)');
els.mudPools[i].setAttribute('filter', 'url(#glow-red)');
els.loads[i].style.fill = 'var(--accent-red)';
} else if (isOptimized && !isAlarm) {
els.flows[i].setAttribute('stroke', 'var(--accent-green)');
els.flows[i].setAttribute('filter', 'url(#glow-green)');
els.mudPools[i].setAttribute('fill', 'var(--accent-cyan)'); // pool returns to normal color
els.mudPools[i].setAttribute('filter', 'url(#glow-cyan)');
els.loads[i].style.fill = '#94a3b8';
} else {
els.flows[i].setAttribute('stroke', 'var(--accent-cyan)');
els.flows[i].setAttribute('filter', 'url(#glow-cyan)');
els.mudPools[i].setAttribute('fill', 'var(--accent-cyan)');
els.mudPools[i].setAttribute('filter', 'url(#glow-cyan)');
els.loads[i].style.fill = '#94a3b8';
}
}
// SCADA Data Updates
els.txtError.textContent = '±' + (isAlarm ? '18.5%' : (maxError > 0 ? (maxError/10).toFixed(1) + '%' : '1.2%')); // Simulating algorithm error bound
els.txtError.className = isAlarm ? 'data-val red' : 'data-val green';
if (isAlarm) {
els.txtStatus.textContent = '检测到2#区域跑浆风险,计算中...';
els.txtStatus.className = 'data-val red';
els.alert2.style.opacity = '1';
els.ledNormal.setAttribute('fill', '#334155');
els.ledWarn.setAttribute('fill', 'var(--accent-red)');
els.ledCalc.setAttribute('fill', 'var(--accent-cyan)');
els.signalLines.style.stroke = 'var(--accent-red)';
} else if (isOptimized) {
els.txtStatus.textContent = '流量重分配完成,全局最优平衡';
els.txtStatus.className = 'data-val green';
els.alert2.style.opacity = '0';
els.ledNormal.setAttribute('fill', 'var(--accent-cyan)');
els.ledWarn.setAttribute('fill', '#334155');
els.ledCalc.setAttribute('fill', '#334155');
els.signalLines.style.stroke = 'var(--accent-green)';
} else {
els.txtStatus.textContent = '正常均衡运行';
els.txtStatus.className = 'data-val cyan';
els.alert2.style.opacity = '0';
els.ledNormal.setAttribute('fill', 'var(--accent-cyan)');
els.ledWarn.setAttribute('fill', '#334155');
els.ledCalc.setAttribute('fill', '#334155');
els.signalLines.style.stroke = '#0ea5e9';
}
}
// --- Auto Play Storyline (IFR Implementation) ---
function runAutoSequence() {
if (mode !== 'auto') return;
// Phase 1: Normal (0-4s)
if (autoPhase === 0) {
updateSystem([33.3, 33.3, 33.4], false, false);
els.txtTime.textContent = '-- s (限值≤5s)';
setTimeout(() => { autoPhase = 1; runAutoSequence(); }, 3000);
}
// Phase 2: Overload on #2 (4-6s)
else if (autoPhase === 1) {
updateSystem([33.3, 50.0, 16.7], true, false); // Simulated sudden surge to #2
let ms = 0;
let counter = setInterval(()=>{
ms+=100;
if(ms <= 2400 && mode === 'auto') {
els.txtTime.textContent = (ms/1000).toFixed(1) + ' s (限值≤5s)';
}
}, 100);
setTimeout(() => {
clearInterval(counter);
autoPhase = 2;
runAutoSequence();
}, 2400); // PLC reaction time + Valve switch time 2.4s (<5s)
}
// Phase 3: Optimized / Adjusted (6-12s)
else if (autoPhase === 2) {
// PLC redistributes: limits #2 to 15%, pushes excess to #1 and #3
updateSystem([42.5, 15.0, 42.5], false, true);
setTimeout(() => { autoPhase = 0; runAutoSequence(); }, 6000); // Hold state then reset loop
}
}
// --- Manual Interaction Logic ---
function handleSliderChange(changedIdx, val) {
if(mode !== 'manual') return;
val = parseFloat(val);
let rem = 100 - val;
let newV = [0,0,0];
newV[changedIdx] = val;
// Auto balance the other two
let otherIndices = [0,1,2].filter(i => i !== changedIdx);
let ratio = valves[otherIndices[0]] / (valves[otherIndices[0]] + valves[otherIndices[1]]);
if (isNaN(ratio)) ratio = 0.5;
newV[otherIndices[0]] = rem * ratio;
newV[otherIndices[1]] = rem * (1 - ratio);
updateSystem(newV, false, true);
els.txtTime.textContent = 'Manual Override';
}
// --- Initialization & Listeners ---
document.addEventListener("DOMContentLoaded", () => {
// UI Listeners
document.getElementById('btn-auto').addEventListener('click', (e) => {
mode = 'auto';
e.target.classList.add('active');
document.getElementById('btn-manual').classList.remove('active');
document.getElementById('manual-controls').classList.remove('active');
autoPhase = 0;
runAutoSequence();
});
document.getElementById('btn-manual').addEventListener('click', (e) => {
mode = 'manual';
e.target.classList.add('active');
document.getElementById('btn-auto').classList.remove('active');
document.getElementById('manual-controls').classList.add('active');
els.txtStatus.textContent = '手动覆盖模式 (PLC辅助配平)';
els.txtStatus.className = 'data-val';
});
// Sliders
els.sliders.forEach((slider, idx) => {
slider.addEventListener('input', (e) => {
handleSliderChange(idx, e.target.value);
});
});
// Start animation loop automatically (Requirement 6 & 7)
runAutoSequence();
});
</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>
/* 基础页面设置 */
body, html {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
background-color: #020617; /* 极深蓝底色 */
display: flex;
justify-content: center;
align-items: center;
font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
overflow: hidden;
}
#animation-container {
width: 100%;
height: 100%;
max-width: 1200px;
max-height: 800px;
display: flex;
justify-content: center;
align-items: center;
}
svg {
width: 100%;
height: 100%;
box-shadow: 0 0 40px rgba(0, 0, 0, 0.5);
}
/* SVG 元素样式及过渡 */
.pipe {
stroke: #334155;
stroke-width: 16px;
fill: none;
stroke-linecap: round;
stroke-linejoin: round;
}
.fluid {
fill: none;
stroke-linecap: round;
stroke-linejoin: round;
transition: stroke-width 1s ease-in-out, stroke 1s ease-in-out;
}
/* 正常流体的流动动画 */
.dash-flow {
stroke-dasharray: 12, 16;
animation: flow 1.2s linear infinite;
}
@keyframes flow {
from { stroke-dashoffset: 28; }
to { stroke-dashoffset: 0; }
}
/* 信号线默认与激活状态 */
.signal-line {
stroke-linecap: round;
stroke-linejoin: round;
transition: opacity 0.5s ease, stroke 0.5s ease;
}
.signal-flow {
stroke-dasharray: 6, 8;
animation: flowSignal 0.6s linear infinite;
}
@keyframes flowSignal {
from { stroke-dashoffset: 14; }
to { stroke-dashoffset: 0; }
}
/* 发光滤镜增强效果 */
.glow-warn { filter: drop-shadow(0 0 10px rgba(239, 68, 68, 0.8)); }
.glow-active { filter: drop-shadow(0 0 12px rgba(16, 185, 129, 0.8)); }
/* 振动筛液位平滑过渡 */
.level-rect {
transition: height 1s ease-in-out, y 1s ease-in-out, fill 1s ease-in-out;
}
/* 机器振动效果 */
@keyframes vibrate {
0% { transform: translate(0, 0); }
25% { transform: translate(0, 1.5px); }
50% { transform: translate(0, -0.5px); }
75% { transform: translate(0, 1px); }
100% { transform: translate(0, 0); }
}
.vibrating {
animation: vibrate 0.08s linear infinite;
}
/* 交互按钮 */
#btn-trigger { cursor: pointer; transition: transform 0.1s; }
#btn-trigger:hover rect { fill: #2563eb; }
#btn-trigger:active { transform: scale(0.97); }
/* 文本过渡 */
.val-text { transition: fill 1s ease; }
.sieve-box { transition: stroke 1s ease; }
.plc-box { transition: stroke 0.5s ease; }
</style>
</head>
<body>
<div id="animation-container">
<svg viewBox="0 0 1000 650" preserveAspectRatio="xMidYMid meet">
<!-- 定义渐变、图案与复用元件 -->
<defs>
<pattern id="bg-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>
<g id="valve-icon">
<path d="M-15,-12 L15,12 L-15,12 L15,-12 Z" fill="#0f172a" stroke="#94a3b8" stroke-width="2" stroke-linejoin="round"/>
<rect x="-5" y="-15" width="10" height="6" fill="#94a3b8" rx="2"/>
<line x1="0" y1="-9" x2="0" y2="0" stroke="#94a3b8" stroke-width="2"/>
</g>
</defs>
<!-- 背景网格 -->
<rect width="100%" height="100%" fill="url(#bg-grid)" />
<!--================ 顶部面板区域 ================-->
<!-- 左侧:IFR 理念解说 -->
<g transform="translate(30, 30)">
<rect width="300" height="135" rx="8" fill="#0f172a" fill-opacity="0.85" stroke="#334155" stroke-width="2"/>
<text x="15" y="28" fill="#f8fafc" font-size="16" font-weight="bold">最终理想解 (IFR) 原理</text>
<line x1="15" y1="40" x2="285" y2="40" stroke="#1e293b" stroke-width="2"/>
<foreignObject x="15" y="48" width="270" height="85">
<div xmlns="http://www.w3.org/1999/xhtml" style="color:#94a3b8; font-size:13px; line-height:1.6; font-family:sans-serif;">
利用既有执行器与总控PLC组成系统级联锁。<br/>
单机(如2号)跑浆时,系统不增加冗余处理设备,而是<span style="color:#f59e0b; font-weight:bold;">主动自适应重分配</span>进料比例,用全局空间协同化解局部过载矛盾。
</div>
</foreignObject>
</g>
<!-- 右侧:关键参数监控面板 -->
<g transform="translate(730, 30)">
<rect width="240" height="135" rx="8" fill="#0f172a" fill-opacity="0.85" stroke="#334155" stroke-width="2"/>
<text x="15" y="28" fill="#f8fafc" font-size="16" font-weight="bold">系统联锁状态监控</text>
<line x1="15" y1="40" x2="225" y2="40" stroke="#1e293b" stroke-width="2"/>
<circle cx="20" cy="62" r="4" fill="#3b82f6"/>
<text x="32" y="66" fill="#cbd5e1" font-size="13">分配误差限制: <tspan fill="#10b981" font-weight="bold">≤±5%</tspan></text>
<circle cx="20" cy="87" r="4" fill="#3b82f6"/>
<text x="32" y="91" fill="#cbd5e1" font-size="13">设定切换时间: <tspan fill="#10b981" font-weight="bold">≤5.0 s</tspan></text>
<circle cx="20" cy="112" r="4" fill="#3b82f6"/>
<text x="32" y="116" fill="#cbd5e1" font-size="13">当前响应实测: <tspan id="actual-time" fill="#f59e0b" font-weight="bold" font-family="monospace">0.00 s</tspan></text>
</g>
<!-- 交互触发按钮 -->
<g transform="translate(415, 60)" id="btn-trigger">
<rect width="170" height="40" rx="20" fill="#3b82f6" stroke="#2563eb" stroke-width="2"/>
<text x="85" y="25" fill="#ffffff" font-size="14" font-weight="bold" text-anchor="middle" style="pointer-events: none;">⚡ 模拟 2号筛跑浆</text>
</g>
<!--================ 物理管线与设备 ================-->
<!-- 进料总管与底色管网 -->
<g id="pipe-network">
<!-- 总入料 -->
<path d="M500,-10 L500,200" class="pipe"/>
<!-- 顶部分配横梁管 -->
<path d="M220,200 L780,200" class="pipe"/>
<!-- 向下分支管 -->
<path d="M220,200 L220,480" class="pipe"/>
<path d="M500,200 L500,480" class="pipe"/>
<path d="M780,200 L780,480" class="pipe"/>
</g>
<!-- 信号控制网络 (背景层) -->
<g id="signals">
<path id="sig-3" class="signal-line" d="M850,220 L810,220 L810,165 L780,165 L780,200" stroke="#10b981" stroke-width="2" fill="none" opacity="0.2" />
<path id="sig-2" class="signal-line" d="M850,220 L810,220 L810,155 L500,155 L500,200" stroke="#10b981" stroke-width="2" fill="none" opacity="0.2" />
<path id="sig-1" class="signal-line" d="M850,220 L810,220 L810,145 L220,145 L220,200" stroke="#10b981" stroke-width="2" fill="none" opacity="0.2" />
</g>
<!-- 动态流体层 -->
<g id="fluid-network">
<path id="fluid-1" class="fluid dash-flow" d="M500,-10 L500,200 L220,200 L220,480" stroke="#06b6d4" style="stroke-width: 6px;"/>
<path id="fluid-2" class="fluid dash-flow" d="M500,-10 L500,480" stroke="#06b6d4" style="stroke-width: 6px;"/>
<path id="fluid-3" class="fluid dash-flow" d="M500,-10 L500,200 L780,200 L780,480" stroke="#06b6d4" style="stroke-width: 6px;"/>
</g>
<!-- 分配器与阀门节点 -->
<!-- 整体外壳罩住结合部 -->
<rect x="180" y="180" width="640" height="40" rx="8" fill="#1e293b" fill-opacity="0.9" stroke="#64748b" stroke-width="2"/>
<text x="500" y="193" fill="#64748b" font-size="10" font-weight="bold" text-anchor="middle" letter-spacing="4">多路电动分配器</text>
<!-- 阀门 1, 2, 3 -->
<use href="#valve-icon" x="220" y="205" />
<use href="#valve-icon" x="500" y="205" />
<use href="#valve-icon" x="780" y="205" />
<!-- 流量计与当前分配比例显示 -->
<g transform="translate(220, 240)">
<rect x="-30" y="-12" width="60" height="24" rx="4" fill="#020617" stroke="#3b82f6" stroke-width="1"/>
<text id="val-1" class="val-text" x="0" y="4" fill="#06b6d4" font-size="14" font-weight="bold" font-family="monospace" text-anchor="middle">33%</text>
</g>
<g transform="translate(500, 240)">
<rect x="-30" y="-12" width="60" height="24" rx="4" fill="#020617" stroke="#3b82f6" stroke-width="1"/>
<text id="val-2" class="val-text" x="0" y="4" fill="#06b6d4" font-size="14" font-weight="bold" font-family="monospace" text-anchor="middle">33%</text>
</g>
<g transform="translate(780, 240)">
<rect x="-30" y="-12" width="60" height="24" rx="4" fill="#020617" stroke="#3b82f6" stroke-width="1"/>
<text id="val-3" class="val-text" x="0" y="4" fill="#06b6d4" font-size="14" font-weight="bold" font-family="monospace" text-anchor="middle">33%</text>
</g>
<!--================ 振动筛机组 (1, 2, 3) ================-->
<!-- 振动筛 1 -->
<g transform="translate(220, 480)">
<g class="vibrating">
<!-- 机体外壳 -->
<rect id="sieve-box-1" class="sieve-box" x="-70" y="0" width="140" height="100" rx="8" fill="#1e293b" stroke="#475569" stroke-width="2"/>
<!-- 内部液池底槽 -->
<rect x="-66" y="4" width="132" height="92" rx="4" fill="#0f172a" />
<!-- 动态液位块,最大高度 92 -->
<rect id="liq-1" class="level-rect" x="-66" y="50" width="132" height="46" rx="2" fill="#06b6d4" opacity="0.75" />
<!-- 筛网层结构 -->
<path d="M-75,25 L75,25" stroke="#94a3b8" stroke-width="4" stroke-dasharray="6,4" />
<path d="M-75,38 L75,38" stroke="#94a3b8" stroke-width="2" stroke-dasharray="3,3" opacity="0.4"/>
<!-- 标识号 -->
<text x="0" y="85" fill="#f8fafc" font-size="16" font-weight="bold" text-anchor="middle" opacity="0.9">1# 振动筛</text>
</g>
</g>
<!-- 振动筛 2 -->
<g transform="translate(500, 480)">
<g class="vibrating">
<rect id="sieve-box-2" class="sieve-box" x="-70" y="0" width="140" height="100" rx="8" fill="#1e293b" stroke="#475569" stroke-width="2"/>
<rect x="-66" y="4" width="132" height="92" rx="4" fill="#0f172a" />
<rect id="liq-2" class="level-rect" x="-66" y="50" width="132" height="46" rx="2" fill="#06b6d4" opacity="0.75" />
<path d="M-75,25 L75,25" stroke="#94a3b8" stroke-width="4" stroke-dasharray="6,4" />
<path d="M-75,38 L75,38" stroke="#94a3b8" stroke-width="2" stroke-dasharray="3,3" opacity="0.4"/>
<text x="0" y="85" fill="#f8fafc" font-size="16" font-weight="bold" text-anchor="middle" opacity="0.9">2# 振动筛</text>
</g>
</g>
<!-- 振动筛 3 -->
<g transform="translate(780, 480)">
<g class="vibrating">
<rect id="sieve-box-3" class="sieve-box" x="-70" y="0" width="140" height="100" rx="8" fill="#1e293b" stroke="#475569" stroke-width="2"/>
<rect x="-66" y="4" width="132" height="92" rx="4" fill="#0f172a" />
<rect id="liq-3" class="level-rect" x="-66" y="50" width="132" height="46" rx="2" fill="#06b6d4" opacity="0.75" />
<path d="M-75,25 L75,25" stroke="#94a3b8" stroke-width="4" stroke-dasharray="6,4" />
<path d="M-75,38 L75,38" stroke="#94a3b8" stroke-width="2" stroke-dasharray="3,3" opacity="0.4"/>
<text x="0" y="85" fill="#f8fafc" font-size="16" font-weight="bold" text-anchor="middle" opacity="0.9">3# 振动筛</text>
</g>
</g>
<!--================ 总控 PLC 单元 ================-->
<g transform="translate(850, 220)">
<!-- 连线接口底座 -->
<rect x="-10" y="-15" width="20" height="30" fill="#334155"/>
<!-- 主机体 -->
<rect id="plc-box" class="plc-box" x="-60" y="-55" width="120" height="110" rx="6" fill="#0f172a" stroke="#10b981" stroke-width="2"/>
<!-- 屏幕区 -->
<rect x="-50" y="-45" width="100" height="40" rx="2" fill="#020617"/>
<text x="0" y="-30" fill="#10b981" font-size="12" font-weight="bold" text-anchor="middle" letter-spacing="2">SCADA/PLC</text>
<text id="plc-status" x="0" y="-15" fill="#e2e8f0" font-size="11" text-anchor="middle">常态均分监控</text>
<!-- 状态灯 -->
<circle id="plc-led" cx="0" cy="15" r="8" fill="#10b981" />
<circle cx="-25" cy="15" r="4" fill="#3b82f6" />
<circle cx="25" cy="15" r="4" fill="#3b82f6" />
<circle cx="-25" cy="35" r="4" fill="#475569" />
<circle cx="0" cy="35" r="4" fill="#475569" />
<circle cx="25" cy="35" r="4" fill="#475569" />
</g>
</svg>
</div>
<script>
document.addEventListener("DOMContentLoaded", () => {
// UI DOM 引用抓取
const UI = {
fluids: [document.getElementById('fluid-1'), document.getElementById('fluid-2'), document.getElementById('fluid-3')],
levels: [document.getElementById('liq-1'), document.getElementById('liq-2'), document.getElementById('liq-3')],
vals: [document.getElementById('val-1'), document.getElementById('val-2'), document.getElementById('val-3')],
sigs: [document.getElementById('sig-1'), document.getElementById('sig-2'), document.getElementById('sig-3')],
sieves: [document.getElementById('sieve-box-1'), document.getElementById('sieve-box-2'), document.getElementById('sieve-box-3')],
plcStatus: document.getElementById('plc-status'),
plcLed: document.getElementById('plc-led'),
plcBox: document.getElementById('plc-box'),
actualTime: document.getElementById('actual-time'),
btnTrigger: document.getElementById('btn-trigger')
};
const MAX_LIQ_HEIGHT = 92; // 容器内液位最大高度,y基准为4
/**
* 更新单台振动筛与管线状态
* @param {number} index - 索引 0, 1, 2
* @param {number} flowRate - 进料比例 0~100
* @param {number} levelPercent - 液位负载 0~100
* @param {string} state - 'normal', 'warn', 'active'
*/
function updateSieve(index, flowRate, levelPercent, state) {
// 1. 更新流动管径粗细模拟流量 (33%基准宽度为6px,系数约0.18)
let strokeW = Math.max(2, flowRate * 0.18);
UI.fluids[index].style.strokeWidth = strokeW + 'px';
UI.vals[index].textContent = flowRate + '%';
// 2. 更新液位高度与Y轴位置 (y基准为4, 底部为 4+92=96)
let h = MAX_LIQ_HEIGHT * (levelPercent / 100);
let y = 4 + (MAX_LIQ_HEIGHT - h);
UI.levels[index].setAttribute('height', h);
UI.levels[index].setAttribute('y', y);
// 3. 根据状态应用主题色系
if (state === 'warn') {
// 红色告警
UI.fluids[index].style.stroke = '#ef4444';
UI.levels[index].setAttribute('fill', '#ef4444');
UI.vals[index].style.fill = '#ef4444';
UI.sieves[index].style.stroke = '#ef4444';
UI.sieves[index].classList.add('glow-warn');
} else if (state === 'active') {
// 橙色增压负担
UI.fluids[index].style.stroke = '#f59e0b';
UI.levels[index].setAttribute('fill', '#f59e0b');
UI.vals[index].style.fill = '#f59e0b';
UI.sieves[index].style.stroke = '#64748b';
UI.sieves[index].classList.remove('glow-warn');
} else {
// 青蓝色正常
UI.fluids[index].style.stroke = '#06b6d4';
UI.levels[index].setAttribute('fill', '#06b6d4');
UI.vals[index].style.fill = '#06b6d4';
UI.sieves[index].style.stroke = '#475569';
UI.sieves[index].classList.remove('glow-warn');
}
}
/**
* 设置控制中枢视觉状态
*/
function setPlc(statusText, isAlert) {
UI.plcStatus.textContent = statusText;
if (isAlert) {
UI.plcLed.setAttribute('fill', '#ef4444');
UI.plcBox.style.stroke = '#ef4444';
UI.plcBox.classList.remove('glow-active');
UI.plcBox.classList.add('glow-warn');
UI.sigs.forEach(s => {
s.style.opacity = '1';
s.style.stroke = '#ef4444';
s.classList.add('signal-flow');
});
} else {
UI.plcLed.setAttribute('fill', '#10b981');
UI.plcBox.style.stroke = '#10b981';
UI.plcBox.classList.remove('glow-warn');
UI.plcBox.classList.add('glow-active');
UI.sigs.forEach(s => {
s.style.opacity = '0.2';
s.style.stroke = '#10b981';
s.classList.remove('signal-flow');
});
}
}
// 模拟切换计时器
let timeInterval;
function runTimer(targetTime) {
let t = 0;
clearInterval(timeInterval);
UI.actualTime.style.fill = "#f59e0b";
UI.actualTime.textContent = "0.00 s";
timeInterval = setInterval(() => {
t += 0.08;
if(t >= targetTime) {
t = targetTime;
clearInterval(timeInterval);
UI.actualTime.style.fill = "#10b981"; // 完成后变绿
}
UI.actualTime.textContent = t.toFixed(2) + " s";
}, 50);
}
// 主状态机控制循环
let cycleTimeout;
let currentPhase = 0;
function runPhase(phase) {
clearTimeout(cycleTimeout);
currentPhase = phase;
switch(phase) {
case 0:
// [正常状态] 3台机均分负荷
updateSieve(0, 33, 40, 'normal');
updateSieve(1, 34, 40, 'normal');
updateSieve(2, 33, 40, 'normal');
setPlc("常态均分监控", false);
UI.actualTime.style.fill = "#64748b";
// 维持 4 秒后进入异常
cycleTimeout = setTimeout(() => runPhase(1), 4000);
break;
case 1:
// [突发过载] 2号筛突发跑浆危机,流量来不及减少,液位暴涨
updateSieve(1, 34, 92, 'warn');
setPlc("警报: 2#液位超限", true);
// 维持 1.5 秒引起注意,PLC开始响应
cycleTimeout = setTimeout(() => runPhase(2), 1500);
break;
case 2:
// [IFR自适应重分配] 联锁介入,执行器调优流量
runTimer(2.14); // 模拟耗时小于5s要求
setPlc("联锁介入: 流量重分配", true);
// 减少2号进料化解矛盾,分摊给1号和3号
updateSieve(0, 45, 60, 'active');
updateSieve(1, 10, 40, 'normal'); // 负荷骤降,脱离危险
updateSieve(2, 45, 60, 'active');
// 维持 4.5 秒展示重分配成果
cycleTimeout = setTimeout(() => runPhase(3), 4500);
break;
case 3:
// [危险解除,准备恢复]
setPlc("状态平稳: 准备复位", false);
// 维持 1.5 秒后回正
cycleTimeout = setTimeout(() => runPhase(0), 1500);
break;
}
}
// 绑定手动干扰逻辑
UI.btnTrigger.addEventListener('click', () => {
if (currentPhase === 0 || currentPhase === 3) {
runPhase(1); // 强制跳跃至跑浆告警阶段
}
});
// 页面加载就绪,自动开始理想解展示循环
setTimeout(() => runPhase(0), 500);
});
</script>
</body>
</html>
积分规则:每轮对话扣10分
等待动画代码生成...
