独立渲染引擎就绪引擎就绪
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>旋流沉砂分离结构 - IFR原理演示</title>
<style>
:root {
--bg-color: #07090f;
--grid-color: rgba(30, 58, 138, 0.2);
--text-color: #94a3b8;
--accent-cyan: #06b6d4;
--accent-orange: #f59e0b;
--metal-dark: #1e293b;
--metal-light: #475569;
--poly-lining: #84cc16;
}
body, html {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
background-color: var(--bg-color);
background-image:
linear-gradient(var(--grid-color) 1px, transparent 1px),
linear-gradient(90deg, var(--grid-color) 1px, transparent 1px);
background-size: 30px 30px;
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
}
.animation-container {
position: relative;
width: 100vw;
height: 100vh;
max-width: 1200px;
max-height: 900px;
display: flex;
justify-content: center;
align-items: center;
}
svg {
width: 100%;
height: 100%;
display: block;
}
/* 极简信息面板 - 严格限制尺寸和位置 */
.hud-panel {
position: absolute;
background: rgba(7, 9, 15, 0.85);
border: 1px solid rgba(6, 182, 212, 0.3);
border-radius: 6px;
padding: 12px 16px;
pointer-events: none;
box-shadow: 0 4px 20px rgba(0,0,0,0.5);
backdrop-filter: blur(4px);
}
.hud-top-left {
top: 30px;
left: 30px;
}
.hud-bottom-right {
bottom: 30px;
right: 30px;
}
.hud-title {
color: #f8fafc;
font-size: 14px;
font-weight: bold;
margin-bottom: 8px;
text-transform: uppercase;
letter-spacing: 1px;
border-bottom: 1px solid rgba(6, 182, 212, 0.3);
padding-bottom: 4px;
}
.hud-list {
margin: 0;
padding: 0;
list-style: none;
font-size: 11px;
color: var(--text-color);
line-height: 1.6;
}
.hud-list li {
display: flex;
align-items: center;
gap: 8px;
}
.legend-dot {
width: 6px;
height: 6px;
border-radius: 50%;
display: inline-block;
}
.dot-sand { background: var(--accent-orange); box-shadow: 0 0 5px var(--accent-orange); }
.dot-liquid { background: var(--accent-cyan); box-shadow: 0 0 5px var(--accent-cyan); }
.param-value {
color: var(--accent-cyan);
font-weight: bold;
}
/* SVG 内置动画辅助 */
.flow-arrow {
animation: dashMove 2s linear infinite;
}
@keyframes dashMove {
from { stroke-dashoffset: 20; }
to { stroke-dashoffset: 0; }
}
</style>
</head>
<body>
<div class="animation-container">
<!-- 主体动画 SVG -->
<svg viewBox="0 0 1000 800" preserveAspectRatio="xMidYMid meet" id="main-svg">
<defs>
<!-- 材质渐变定义 -->
<linearGradient id="metal-pipe" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" stop-color="#1e293b" />
<stop offset="30%" stop-color="#475569" />
<stop offset="70%" stop-color="#334155" />
<stop offset="100%" stop-color="#0f172a" />
</linearGradient>
<linearGradient id="metal-cone-back" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" stop-color="#0b0f19" />
<stop offset="50%" stop-color="#1a2235" />
<stop offset="100%" stop-color="#05070a" />
</linearGradient>
<linearGradient id="liquid-flow" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" stop-color="rgba(6, 182, 212, 0.8)" />
<stop offset="100%" stop-color="rgba(6, 182, 212, 0.1)" />
</linearGradient>
<!-- 发光滤镜 -->
<filter id="glow-cyan" x="-20%" y="-20%" width="140%" height="140%">
<feGaussianBlur stdDeviation="4" result="blur" />
<feComposite in="SourceGraphic" in2="blur" operator="over" />
</filter>
<filter id="glow-orange" x="-20%" y="-20%" width="140%" height="140%">
<feGaussianBlur stdDeviation="3" result="blur" />
<feComposite in="SourceGraphic" in2="blur" operator="over" />
</filter>
</defs>
<!-- 背景网格及标注线 -->
<g stroke="rgba(148, 163, 184, 0.1)" stroke-width="1" fill="none">
<line x1="500" y1="50" x2="500" y2="750" stroke-dasharray="5 5" />
<line x1="200" y1="400" x2="800" y2="400" stroke-dasharray="5 5" />
</g>
<!-- 静态结构:后半部分 (背板) -->
<g id="structure-back">
<!-- 缓冲罐底部 -->
<path d="M300,100 L700,100 L700,200 L300,200 Z" fill="url(#metal-pipe)" />
<text x="310" y="120" fill="#64748b" font-size="10">缓冲罐底部</text>
<!-- 进液管 -->
<path d="M150,220 L350,220 L350,280 L150,280 Z" fill="url(#metal-pipe)" />
<!-- 旋流锥体(内壁背侧) -->
<path d="M300,200 L700,200 L580,550 L420,550 Z" fill="url(#metal-cone-back)" stroke="#1e293b" stroke-width="2"/>
<!-- 聚氨酯衬里(背侧边缘) -->
<path d="M305,205 L695,205 L576,548 L424,548 Z" fill="none" stroke="var(--poly-lining)" stroke-width="3" opacity="0.3"/>
<!-- 沉砂收集包 -->
<path d="M420,550 L580,550 L580,680 L420,680 Z" fill="url(#metal-pipe)" />
<!-- 排砂阀体 -->
<path d="M450,700 L550,700 L520,740 L480,740 Z" fill="#334155" stroke="#1e293b"/>
<rect x="495" y="680" width="10" height="20" fill="#64748b" />
<circle cx="500" cy="720" r="15" fill="#f43f5e" /> <!-- 手阀手轮 -->
<!-- 溢流管(清液出口) 延伸至旋流器内部 -->
<path d="M460,50 L540,50 L540,320 L460,320 Z" fill="#0f172a" stroke="#334155" stroke-width="2"/>
<path d="M540,100 L850,100 L850,160 L540,160 Z" fill="url(#metal-pipe)" />
</g>
<!-- 粒子动画层容器 (由JS填充) -->
<g id="particle-layer"></g>
<!-- 静态结构:前半部分透视框 (剖面切割线) -->
<g id="structure-front" pointer-events="none">
<!-- 旋流器外壳剖面线 -->
<path d="M300,200 L700,200 L580,550 L420,550 Z" fill="none" stroke="#475569" stroke-width="4" />
<!-- 聚氨酯衬里剖面线 -->
<path d="M308,205 L692,205 L572,545 L428,545 Z" fill="none" stroke="var(--poly-lining)" stroke-width="4" opacity="0.8"/>
<!-- 溢流管前端虚线(表示在内部) -->
<path d="M460,200 L460,320 M540,200 L540,320" fill="none" stroke="#64748b" stroke-width="2" stroke-dasharray="6 4" />
<path d="M460,320 L540,320" fill="none" stroke="#64748b" stroke-width="2" stroke-dasharray="6 4" />
<!-- 结构指示标签 (极简字号) -->
<g font-size="10" fill="#94a3b8">
<line x1="280" y1="250" x2="350" y2="250" stroke="#94a3b8" stroke-width="1" />
<text x="210" y="245">含砂浆液入口</text>
<line x1="720" y1="130" x2="800" y2="130" stroke="#94a3b8" stroke-width="1" />
<text x="810" y="133">去放液管线(清液)</text>
<line x1="610" y1="350" x2="680" y2="350" stroke="var(--poly-lining)" stroke-width="1" />
<text x="690" y="353" fill="var(--poly-lining)">聚氨酯耐磨衬里</text>
<line x1="600" y1="620" x2="680" y2="620" stroke="#94a3b8" stroke-width="1" />
<text x="690" y="623">沉砂收集包</text>
</g>
</g>
</svg>
<!-- 左上角:原理与参数 -->
<div class="hud-panel hud-top-left">
<div class="hud-title">IFR设计: 旋流沉砂无源分离</div>
<ul class="hud-list">
<li><strong>核心矛盾破除:</strong>利用流体自身动能实现离心分离,无需动力部件。</li>
<li><strong>旋流压力:</strong><span class="param-value">0.1 - 0.3 MPa</span></li>
<li><strong>分离粒度:</strong><span class="param-value">0.1 ~ 2 mm</span></li>
<li style="margin-top: 8px; color: #64748b;">(注: 粗砂沉降,中心清液上溢,保护下游阀门)</li>
</ul>
</div>
<!-- 右下角:图例 -->
<div class="hud-panel hud-bottom-right">
<ul class="hud-list">
<li><span class="legend-dot dot-sand"></span> 粗砂粒 (比重大,沿壁下沉)</li>
<li><span class="legend-dot dot-liquid"></span> 澄清水 (比重小,中心溢流)</li>
</ul>
</div>
</div>
<script>
/**
* 高保真流体动力学粒子动画系统
* 利用 SVG <circle> 和 requestAnimationFrame 模拟三维旋流效果
*/
document.addEventListener("DOMContentLoaded", () => {
const svgNS = "http://www.w3.org/2000/svg";
const particleLayer = document.getElementById("particle-layer");
// 动画参数配置
const config = {
sandCount: 250, // 砂粒数量
liquidCount: 300, // 清液粒子数量
centerX: 500, // 旋流中心X
coneTopY: 200, // 锥体顶部Y
coneBottomY: 550, // 锥体底部Y
coneTopR: 190, // 锥体顶部半径 (内壁)
coneBottomR: 70, // 锥体底部半径
pocketBottomY: 670, // 收集包底部Y
overflowBottomY: 320,// 溢流管底部Y
overflowR: 35, // 溢流管半径
inletY: 250, // 入口Y基准
inletX: 150 // 入口X基准
};
const particles = [];
class Particle {
constructor(type) {
this.type = type; // 'sand' | 'liquid'
this.element = document.createElementNS(svgNS, "circle");
if (this.type === 'sand') {
this.radius = Math.random() * 2 + 1.5;
this.element.setAttribute("fill", "var(--accent-orange)");
this.element.setAttribute("filter", "url(#glow-orange)");
} else {
this.radius = Math.random() * 1.5 + 1;
this.element.setAttribute("fill", "var(--accent-cyan)");
this.element.setAttribute("opacity", Math.random() * 0.5 + 0.3);
}
this.element.setAttribute("r", this.radius);
particleLayer.appendChild(this.element);
this.reset();
// 初始时随机分布在生命周期中,避免一起出现
this.progress = Math.random();
this.updatePosition();
}
reset() {
this.state = 'inlet'; // inlet -> cyclone -> (pocket | overflow)
this.progress = 0;
// 入口参数
this.x = config.inletX + Math.random() * 100;
this.y = config.inletY - 20 + Math.random() * 40;
this.speed = Math.random() * 0.005 + 0.005;
// 旋流参数
this.angle = Math.random() * Math.PI * 2;
this.angularVelocity = Math.random() * 0.1 + 0.15; // 角速度
// 在收集包中的沉积位置
if (this.type === 'sand') {
this.settleX = config.centerX + (Math.random() - 0.5) * (config.coneBottomR * 2 - 10);
this.settleY = config.pocketBottomY - Math.random() * 30;
}
}
updatePosition() {
this.progress += this.speed;
if (this.state === 'inlet') {
// 入口阶段:向右直线移动
this.x += (config.centerX - config.inletX) * this.speed * 2;
if (this.x > config.centerX - config.coneTopR + 20) {
this.state = 'cyclone';
this.progress = 0;
// 转换到极坐标
this.angle = Math.PI; // 从左侧进入
this.currentY = this.y;
}
this.element.setAttribute("cx", this.x);
this.element.setAttribute("cy", this.y);
this.element.setAttribute("opacity", 1);
}
else if (this.state === 'cyclone') {
this.angle += this.angularVelocity;
if (this.type === 'sand') {
// 砂粒:沿外壁向下螺旋
this.currentY += 2; // 下沉速度
// 计算当前高度下的圆锥半径
let ratio = (this.currentY - config.coneTopY) / (config.coneBottomY - config.coneTopY);
ratio = Math.max(0, Math.min(1, ratio));
let currentR = config.coneTopR - ratio * (config.coneTopR - config.coneBottomR) - this.radius;
if (this.currentY >= config.coneBottomY) {
this.state = 'pocket';
this.progress = 0;
}
// 3D透视投影 (Y轴压缩)
let px = config.centerX + currentR * Math.cos(this.angle);
let py = this.currentY + currentR * 0.15 * Math.sin(this.angle);
// 简单的深度排序(背面变暗变小)
if (Math.sin(this.angle) < 0) {
this.element.setAttribute("opacity", 0.3);
} else {
this.element.setAttribute("opacity", 1);
}
this.element.setAttribute("cx", px);
this.element.setAttribute("cy", py);
} else {
// 清液:先外旋下沉,再内旋上升
// 假设下沉到一定深度后被中心负压抽离
let depthThreshold = config.coneTopY + 200 + Math.random() * 100;
if (this.currentY < depthThreshold && !this.rising) {
this.currentY += 1.5;
let ratio = (this.currentY - config.coneTopY) / (config.coneBottomY - config.coneTopY);
let currentR = (config.coneTopR - ratio * (config.coneTopR - config.coneBottomR)) * 0.8; // 在主涡流内层
let px = config.centerX + currentR * Math.cos(this.angle);
let py = this.currentY + currentR * 0.15 * Math.sin(this.angle);
this.element.setAttribute("cx", px);
this.element.setAttribute("cy", py);
} else {
this.rising = true;
this.currentY -= 3; // 上升速度更快
// 逐渐收缩到中心溢流管半径
let currentR = config.overflowR * 0.8;
let px = config.centerX + currentR * Math.cos(this.angle * 2); // 内部转速更快
let py = this.currentY + currentR * 0.15 * Math.sin(this.angle * 2);
this.element.setAttribute("cx", px);
this.element.setAttribute("cy", py);
if (this.currentY < 130) {
this.state = 'overflow';
this.x = px;
this.y = py;
}
}
}
}
else if (this.state === 'pocket') {
// 砂粒落入收集包
if (this.currentY < this.settleY) {
this.currentY += 2;
this.element.setAttribute("cy", this.currentY);
// 稍微随机水平抖动模拟沉积
this.element.setAttribute("cx", config.centerX + (Math.random()-0.5)*5 + (this.settleX - config.centerX)*0.5);
} else {
// 停留一段时间后重置(模拟定期排砂排走)
this.progress += 0.005;
if (this.progress > 1) {
this.reset();
}
}
}
else if (this.state === 'overflow') {
// 清液向右流出
this.x += 4;
this.element.setAttribute("cx", this.x);
this.element.setAttribute("cy", this.y);
if (this.x > 850) {
this.reset();
this.rising = false;
}
}
}
}
// 初始化粒子池
for (let i = 0; i < config.sandCount; i++) {
particles.push(new Particle('sand'));
}
for (let i = 0; i < config.liquidCount; i++) {
particles.push(new Particle('liquid'));
}
// 核心渲染循环 - 自动播放无需交互
function animate() {
for (let i = 0; i < particles.length; i++) {
particles[i].updatePosition();
}
requestAnimationFrame(animate);
}
// 启动动画
animate();
});
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>旋流沉砂分离结构 - TRIZ 最终理想解</title>
<style>
:root {
--bg-color: #050814;
--grid-color: rgba(0, 255, 255, 0.05);
--cyan-glow: #00f0ff;
--orange-glow: #ff5500;
--green-pu: #39ff14;
--panel-bg: rgba(5, 8, 20, 0.7);
--border-tech: 1px solid rgba(0, 240, 255, 0.3);
--text-main: #e0f7fa;
--font-tech: 'Consolas', 'Courier New', monospace;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
background-color: var(--bg-color);
background-image:
linear-gradient(var(--grid-color) 1px, transparent 1px),
linear-gradient(90deg, var(--grid-color) 1px, transparent 1px);
background-size: 30px 30px;
color: var(--text-main);
font-family: var(--font-tech);
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
}
/* 扫描线特效 */
body::after {
content: "";
position: absolute;
top: 0; left: 0; width: 100%; height: 100%;
background: linear-gradient(rgba(18, 16, 16, 0) 50%, rgba(0, 0, 0, 0.25) 50%), linear-gradient(90deg, rgba(255, 0, 0, 0.06), rgba(0, 255, 0, 0.02), rgba(0, 0, 255, 0.06));
background-size: 100% 4px, 3px 100%;
pointer-events: none;
z-index: 100;
}
.dashboard {
position: relative;
width: 100vw;
height: 100vh;
display: flex;
justify-content: space-between;
}
.svg-container {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1;
display: flex;
justify-content: center;
align-items: center;
}
svg {
width: 100%;
height: 100%;
max-width: 1400px;
}
.hud-panel {
position: relative;
z-index: 10;
width: 350px;
margin: 30px;
padding: 25px;
background: var(--panel-bg);
border: var(--border-tech);
backdrop-filter: blur(8px);
display: flex;
flex-direction: column;
gap: 20px;
box-shadow: 0 0 20px rgba(0, 240, 255, 0.1) inset;
}
.hud-panel::before {
content: '';
position: absolute;
top: -2px; left: -2px; width: 20px; height: 20px;
border-top: 2px solid var(--cyan-glow);
border-left: 2px solid var(--cyan-glow);
}
.hud-panel::after {
content: '';
position: absolute;
bottom: -2px; right: -2px; width: 20px; height: 20px;
border-bottom: 2px solid var(--cyan-glow);
border-right: 2px solid var(--cyan-glow);
}
h1 {
font-size: 1.4rem;
color: var(--cyan-glow);
text-shadow: 0 0 10px rgba(0, 240, 255, 0.5);
text-transform: uppercase;
letter-spacing: 2px;
border-bottom: 1px solid var(--cyan-glow);
padding-bottom: 10px;
}
h2 {
font-size: 1rem;
color: var(--orange-glow);
margin-bottom: 5px;
}
.triz-box {
background: rgba(255, 85, 0, 0.1);
border-left: 3px solid var(--orange-glow);
padding: 15px;
font-size: 0.9rem;
line-height: 1.6;
}
.highlight {
color: var(--green-pu);
font-weight: bold;
}
.controls {
margin-top: auto;
background: rgba(0, 240, 255, 0.05);
padding: 15px;
border: 1px solid rgba(0, 240, 255, 0.2);
}
.control-group {
display: flex;
flex-direction: column;
gap: 10px;
}
.slider-header {
display: flex;
justify-content: space-between;
font-size: 0.9rem;
color: var(--cyan-glow);
}
input[type=range] {
-webkit-appearance: none;
width: 100%;
background: transparent;
}
input[type=range]::-webkit-slider-thumb {
-webkit-appearance: none;
height: 16px;
width: 16px;
background: var(--cyan-glow);
cursor: pointer;
box-shadow: 0 0 10px var(--cyan-glow);
border-radius: 50%;
margin-top: -6px;
}
input[type=range]::-webkit-slider-runnable-track {
width: 100%;
height: 4px;
cursor: pointer;
background: rgba(0, 240, 255, 0.3);
border-radius: 2px;
}
.param-list {
list-style: none;
font-size: 0.85rem;
line-height: 2;
}
.param-list li {
display: flex;
justify-content: space-between;
border-bottom: 1px dashed rgba(255,255,255,0.1);
}
.param-value {
color: var(--cyan-glow);
}
/* SVG Styles */
.tank-shell {
fill: rgba(0, 50, 100, 0.2);
stroke: var(--cyan-glow);
stroke-width: 2;
filter: drop-shadow(0 0 5px rgba(0, 240, 255, 0.5));
}
.pu-lining {
fill: none;
stroke: var(--green-pu);
stroke-width: 4;
stroke-dasharray: 8 4;
filter: drop-shadow(0 0 6px var(--green-pu));
animation: dash 20s linear infinite;
}
.pipe {
fill: rgba(0, 50, 100, 0.4);
stroke: var(--cyan-glow);
stroke-width: 2;
}
.valve {
fill: #334455;
stroke: var(--cyan-glow);
stroke-width: 2;
transition: all 0.3s;
}
.sand-bag {
fill: rgba(255, 85, 0, 0.1);
stroke: var(--orange-glow);
stroke-width: 2;
}
.sand-pile {
fill: var(--orange-glow);
opacity: 0.8;
filter: drop-shadow(0 0 10px var(--orange-glow));
animation: pulse-sand 4s infinite alternate;
}
.annotation-line {
stroke: rgba(255, 255, 255, 0.5);
stroke-width: 1.5;
stroke-dasharray: 4 2;
fill: none;
}
.annotation-text {
fill: #fff;
font-family: var(--font-tech);
font-size: 14px;
letter-spacing: 1px;
}
.annotation-point {
fill: var(--cyan-glow);
}
@keyframes dash {
to { stroke-dashoffset: 200; }
}
@keyframes pulse-sand {
0% { opacity: 0.5; }
100% { opacity: 0.9; }
}
</style>
</head>
<body>
<div class="dashboard">
<!-- 左侧面板:理论与信息 -->
<aside class="hud-panel" style="margin-right: auto;">
<h1>系统重构工程图</h1>
<div class="triz-box">
<h2>最终理想解 (IFR)</h2>
<p>流体<span class="highlight">自行完成分离</span>,有害作用(粗砂磨损)自我消除,彻底保护下游蝶阀与管线。</p>
</div>
<div class="triz-box">
<h2>资源利用率 (Resources)</h2>
<p>巧妙利用含砂浆液自身的压力转化为<span class="highlight">旋流离心动能</span>,零额外动力部件,系统复杂度极低。</p>
</div>
<div class="triz-box" style="border-left-color: var(--cyan-glow); background: rgba(0,240,255,0.05);">
<h2>工作机理</h2>
<p>外侧粗砂受离心力沿锥斗壁面(聚氨酯耐磨层)螺旋下沉至收集包;中心清液形成上旋涡流,安全进入放液管线。</p>
</div>
</aside>
<!-- 中央 SVG 动画区 -->
<div class="svg-container">
<svg viewBox="0 0 1200 800" id="main-svg">
<defs>
<filter id="glow-cyan" x="-20%" y="-20%" width="140%" height="140%">
<feGaussianBlur stdDeviation="3" result="blur" />
<feComposite in="SourceGraphic" in2="blur" operator="over" />
</filter>
<filter id="glow-orange" x="-20%" y="-20%" width="140%" height="140%">
<feGaussianBlur stdDeviation="4" result="blur" />
<feComposite in="SourceGraphic" in2="blur" operator="over" />
</filter>
<linearGradient id="sand-grad" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#ff5500" stop-opacity="0.2"/>
<stop offset="100%" stop-color="#ff5500" stop-opacity="0.8"/>
</linearGradient>
</defs>
<g id="engineering-drawing">
<!-- 顶流出液管 (Overflow) -->
<rect x="560" y="80" width="80" height="220" class="pipe" />
<!-- 进液管 (Inlet) -->
<rect x="250" y="220" width="230" height="80" class="pipe" />
<!-- 旋流器主体 (Cyclone Body - Cylinder + Cone) -->
<polygon points="480,200 720,200 720,380 640,650 560,650 480,380" class="tank-shell" />
<!-- 聚氨酯耐磨衬里 (PU Lining) -->
<polygon points="488,208 712,208 712,376 632,642 568,642 488,376" class="pu-lining" />
<!-- 沉砂收集包 (Sand Bag) -->
<path d="M 540,710 L 660,710 L 640,780 L 560,780 Z" class="sand-bag" />
<!-- 堆积的粗砂 -->
<path d="M 545,740 L 655,740 L 640,780 L 560,780 Z" class="sand-pile" fill="url(#sand-grad)" />
<!-- 排砂手阀 (Manual Valve) -->
<g id="valve-group">
<rect x="560" y="650" width="80" height="60" class="pipe" />
<!-- 蝶阀抽象图形 -->
<polygon points="560,660 640,700 560,700 640,660" class="valve" />
<line x1="600" y1="680" x2="600" y2="640" stroke="var(--cyan-glow)" stroke-width="3" />
<rect x="580" y="635" width="40" height="10" fill="#ffaa00" />
</g>
</g>
<!-- 粒子动画层 -->
<g id="particle-layer"></g>
<!-- 标注系统 -->
<g id="annotations">
<!-- 入口标注 -->
<polyline points="300,260 300,180 200,180" class="annotation-line" />
<circle cx="300" cy="260" r="3" class="annotation-point" />
<text x="190" y="175" class="annotation-text" text-anchor="end">含砂浆液入口 (0.1-0.3MPa)</text>
<!-- 清液标注 -->
<polyline points="600,120 750,120 780,90" class="annotation-line" />
<circle cx="600" cy="120" r="3" class="annotation-point" />
<text x="785" y="85" class="annotation-text" fill="var(--cyan-glow)">顶流:中心清液上溢</text>
<!-- PU衬里标注 -->
<polyline points="700,300 800,300 830,270" class="annotation-line" />
<circle cx="700" cy="300" r="3" class="annotation-point" fill="var(--green-pu)" />
<text x="835" y="265" class="annotation-text" fill="var(--green-pu)">聚氨酯耐磨衬里层</text>
<!-- 旋流分离标注 -->
<polyline points="650,450 780,450 810,480" class="annotation-line" />
<text x="815" y="485" class="annotation-text">旋流分离区 (粗砂沿壁下沉)</text>
<!-- 阀门标注 -->
<polyline points="640,680 750,680 780,650" class="annotation-line" />
<circle cx="640" cy="680" r="3" class="annotation-point" />
<text x="785" y="645" class="annotation-text">排砂手阀 (免受持续磨损)</text>
<!-- 收集包标注 -->
<polyline points="650,750 750,750 780,780" class="annotation-line" />
<circle cx="650" cy="750" r="3" class="annotation-point" fill="var(--orange-glow)" />
<text x="785" y="785" class="annotation-text" fill="var(--orange-glow)">沉砂收集包</text>
</g>
</svg>
</div>
<!-- 右侧面板:参数与控制 -->
<aside class="hud-panel" style="margin-left: auto;">
<h2>关键参数监控</h2>
<ul class="param-list">
<li><span>分离粒度范围</span> <span class="param-value">0.1 ~ 2mm</span></li>
<li><span>衬里材质</span> <span class="param-value">Polyurethane (PU)</span></li>
<li><span>适用工况</span> <span class="param-value">中等含砂量</span></li>
<li><span>下游保护率</span> <span class="param-value" style="color: #39ff14;">> 98.5%</span></li>
</ul>
<div class="controls">
<div class="control-group">
<div class="slider-header">
<span>旋流压力控制 (MPa)</span>
<span id="pressureValue">0.20</span>
</div>
<input type="range" id="pressureSlider" min="0.1" max="0.3" step="0.01" value="0.2">
<p style="font-size: 0.75rem; color: rgba(255,255,255,0.5); margin-top: 5px;">
* 调整压力观察分离效率与流速变化
</p>
</div>
</div>
</aside>
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
const particleLayer = document.getElementById('particle-layer');
const pressureSlider = document.getElementById('pressureSlider');
const pressureValueDisplay = document.getElementById('pressureValue');
// 基础配置
const NUM_SAND = 90;
const NUM_WATER = 90;
const BASE_SPEED = 6; // 基础动画时长(秒)
// 预生成路径数据
function generateSpiralPath(startX, startY, endY, maxR, minR, loops, isInner = false) {
let path = `M ${startX},${startY} `;
// 如果是外围含砂流体,先有一段水平进入阶段
if (!isInner) {
path = `M 250,260 L 480,260 `;
}
const steps = loops * 40;
// 中心点 X 始终为 600
const centerX = 600;
for(let i=0; i<=steps; i++) {
let t = i/steps;
let y = isInner ? startY - (startY - endY) * t : startY + (endY - startY) * t;
let r = maxR - (maxR - minR) * t;
// 利用余弦计算 X 坐标形成 2D 投影螺旋
// 入口后平滑过渡相位
let angleOffset = isInner ? 0 : Math.PI;
let angle = angleOffset + t * Math.PI * 2 * loops * (isInner ? -1 : 1);
let x = centerX + Math.cos(angle) * r;
path += `L ${x},${y} `;
}
return path;
}
// 生成外侧下降的含砂涡流路径 (Y: 260 -> 640)
const outerPathStr = generateSpiralPath(480, 260, 640, 110, 30, 7, false);
// 生成内侧上升的清液涡流路径 (Y: 600 -> 100)
const innerPathStr = generateSpiralPath(600, 600, 100, 20, 20, 9, true);
const allAnimations = [];
function createParticle(type, pathStr, delay, duration) {
const isSand = type === 'sand';
const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
// 属性设定
circle.setAttribute('r', isSand ? (Math.random() * 2 + 1.5) : (Math.random() * 1.5 + 1));
circle.setAttribute('fill', isSand ? (Math.random() > 0.5 ? '#ffaa00' : '#ff5500') : '#00f0ff');
circle.setAttribute('filter', isSand ? 'url(#glow-orange)' : 'url(#glow-cyan)');
circle.setAttribute('opacity', isSand ? '0.9' : '0.7');
const animate = document.createElementNS('http://www.w3.org/2000/svg', 'animateMotion');
animate.setAttribute('path', pathStr);
animate.setAttribute('dur', `${duration}s`);
animate.setAttribute('begin', `-${delay}s`);
animate.setAttribute('repeatCount', 'indefinite');
// 模拟 3D 深度淡入淡出(简单的透明度动画与运动同步)
// 真实 SVG 中用 animateMotion 的同时很难精准同步透明度,这里靠密集度制造视觉错觉
circle.appendChild(animate);
particleLayer.appendChild(circle);
allAnimations.push(animate);
}
// 初始化粒子系统
function initParticles() {
// 外层混合流/粗砂粒子
for(let i=0; i<NUM_SAND; i++) {
const delay = Math.random() * BASE_SPEED;
// 外层速度稍慢
createParticle('sand', outerPathStr, delay, BASE_SPEED);
}
// 内层纯净液粒子
for(let i=0; i<NUM_WATER; i++) {
const delay = Math.random() * (BASE_SPEED * 0.7);
// 内部清液上升速度较快
createParticle('water', innerPathStr, delay, BASE_SPEED * 0.7);
}
}
// 交互逻辑:调节压力改变流速
pressureSlider.addEventListener('input', (e) => {
const val = parseFloat(e.target.value);
pressureValueDisplay.textContent = val.toFixed(2);
// 压力越大 (0.3),动画越快 (duration越小)
// 基础压力 0.2 对应 multiplier 1
const speedMultiplier = 0.2 / val;
allAnimations.forEach((anim) => {
// 获取初始比例
const isWater = anim.parentElement.getAttribute('fill') === '#00f0ff';
const baseDur = isWater ? BASE_SPEED * 0.7 : BASE_SPEED;
const newDur = baseDur * speedMultiplier;
anim.setAttribute('dur', `${newDur}s`);
});
});
// 启动动画
initParticles();
});
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>TRIZ 最终理想解 - 旋流沉砂分离结构原理动画</title>
<style>
:root {
--bg-base: #020617;
--bg-panel: rgba(15, 23, 42, 0.85);
--border-color: #1e293b;
--text-main: #f8fafc;
--text-muted: #94a3b8;
--accent-cyan: #22d3ee;
--accent-orange: #f59e0b;
--accent-green: #a3e635;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
background-color: var(--bg-base);
color: var(--text-main);
font-family: system-ui, -apple-system, sans-serif;
display: flex;
height: 100vh;
overflow: hidden;
}
/* 侧边控制面板 */
.sidebar {
width: 380px;
background: var(--bg-panel);
border-right: 1px solid var(--border-color);
padding: 30px 24px;
display: flex;
flex-direction: column;
gap: 28px;
backdrop-filter: blur(12px);
z-index: 10;
box-shadow: 4px 0 24px rgba(0,0,0,0.5);
}
.header h1 {
font-size: 22px;
font-weight: 600;
margin-bottom: 8px;
background: linear-gradient(90deg, #fff, #94a3b8);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.header p {
font-size: 13px;
color: var(--accent-green);
line-height: 1.5;
}
.panel-section {
background: rgba(255, 255, 255, 0.03);
border: 1px solid rgba(255, 255, 255, 0.05);
border-radius: 8px;
padding: 16px;
}
.panel-section h3 {
font-size: 14px;
text-transform: uppercase;
letter-spacing: 1px;
color: var(--text-muted);
margin-bottom: 16px;
display: flex;
align-items: center;
gap: 8px;
}
.panel-section h3::before {
content: '';
display: block;
width: 4px;
height: 14px;
background: var(--accent-cyan);
border-radius: 2px;
}
/* 状态指示器 */
.status-indicator {
display: flex;
align-items: center;
gap: 10px;
font-size: 14px;
font-family: monospace;
background: rgba(0,0,0,0.3);
padding: 10px;
border-radius: 6px;
}
.status-dot {
width: 10px;
height: 10px;
background: var(--accent-green);
border-radius: 50%;
box-shadow: 0 0 10px var(--accent-green);
animation: pulse 2s infinite;
}
@keyframes pulse {
0% { transform: scale(0.95); opacity: 0.8; }
50% { transform: scale(1.1); opacity: 1; }
100% { transform: scale(0.95); opacity: 0.8; }
}
/* 交互滑块 */
.control-group {
display: flex;
flex-direction: column;
gap: 12px;
}
.control-header {
display: flex;
justify-content: space-between;
font-size: 14px;
}
.val-display {
color: var(--accent-cyan);
font-family: monospace;
font-weight: bold;
font-size: 16px;
}
input[type=range] {
-webkit-appearance: none;
width: 100%;
background: #1e293b;
height: 6px;
border-radius: 3px;
outline: none;
}
input[type=range]::-webkit-slider-thumb {
-webkit-appearance: none;
width: 18px;
height: 18px;
border-radius: 50%;
background: var(--accent-cyan);
cursor: pointer;
box-shadow: 0 0 12px var(--accent-cyan);
transition: transform 0.1s;
}
input[type=range]::-webkit-slider-thumb:hover {
transform: scale(1.2);
}
.helper-text {
font-size: 12px;
color: var(--text-muted);
line-height: 1.5;
}
/* TRIZ 列表 */
.triz-list {
list-style: none;
display: flex;
flex-direction: column;
gap: 12px;
}
.triz-list li {
font-size: 13px;
line-height: 1.6;
color: #cbd5e1;
padding-left: 16px;
position: relative;
}
.triz-list li::before {
content: '▹';
position: absolute;
left: 0;
color: var(--accent-orange);
}
.triz-list strong {
color: #fff;
}
/* 主视图区 */
.main-view {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
position: relative;
background: radial-gradient(circle at center, #0f172a 0%, #020617 100%);
}
/* 科技感背景网格 */
.grid-bg {
position: absolute;
inset: 0;
background-image:
linear-gradient(rgba(255,255,255,0.03) 1px, transparent 1px),
linear-gradient(90deg, rgba(255,255,255,0.03) 1px, transparent 1px);
background-size: 40px 40px;
z-index: 1;
pointer-events: none;
}
.svg-container {
position: relative;
z-index: 2;
width: 100%;
height: 100%;
max-width: 1100px;
max-height: 900px;
display: flex;
justify-content: center;
align-items: center;
}
svg {
width: 100%;
height: 100%;
filter: drop-shadow(0 20px 30px rgba(0,0,0,0.5));
}
.legend {
position: absolute;
bottom: 30px;
right: 40px;
background: rgba(15, 23, 42, 0.7);
border: 1px solid var(--border-color);
padding: 12px 16px;
border-radius: 8px;
display: flex;
gap: 20px;
z-index: 10;
}
.legend-item {
display: flex;
align-items: center;
gap: 8px;
font-size: 13px;
}
.legend-color {
width: 12px;
height: 12px;
border-radius: 50%;
}
.legend-color.sand { background: var(--accent-orange); box-shadow: 0 0 8px var(--accent-orange); }
.legend-color.liquid { background: var(--accent-cyan); box-shadow: 0 0 8px var(--accent-cyan); }
</style>
</head>
<body>
<aside class="sidebar">
<header class="header">
<h1>旋流沉砂分离系统</h1>
<p>TRIZ IFR 最终理想解动态演示</p>
</header>
<div class="panel-section">
<h3>运行状态</h3>
<div class="status-indicator">
<div class="status-dot"></div>
<span>自动分离中 (流体驱动)</span>
</div>
</div>
<div class="panel-section">
<h3>核心参数控制 (交互)</h3>
<div class="control-group">
<div class="control-header">
<span>旋流进料压力</span>
<span class="val-display" id="pressure-val">0.20 MPa</span>
</div>
<input type="range" id="pressure-slider" min="0.1" max="0.3" step="0.01" value="0.2">
<div class="helper-text">
* 手动调节滑动条。压力越大,系统动能转化越快,沉砂分离的离心转速越高,分离越彻底。
</div>
<div class="helper-text" style="color:var(--text-muted); margin-top:4px;">
* 适用分离粒度:0.1~2mm
</div>
</div>
</div>
<div class="panel-section">
<h3>TRIZ 原理解析</h3>
<ul class="triz-list">
<li><strong>最终理想解 (IFR):</strong> 不增加外部动力过滤设备,通过巧妙的结构构型让混合流体“自己”完成分离,根除下游阀门磨损矛盾。</li>
<li><strong>有效利用资源:</strong> 直接利用原进料流体自带的压能(0.1-0.3MPa)转化为分离所需的强大离心力。</li>
<li><strong>局部质量原则:</strong> 仅在最高磨损区(旋流锥斗内壁)增加聚氨酯衬里,实现低成本高可靠性防护。</li>
</ul>
</div>
</aside>
<main class="main-view">
<div class="grid-bg"></div>
<div class="legend">
<div class="legend-item">
<div class="legend-color sand"></div>
<span>粗砂及杂质 (下沉)</span>
</div>
<div class="legend-item">
<div class="legend-color liquid"></div>
<span>上溢清液 (去放液管线)</span>
</div>
</div>
<div class="svg-container">
<!-- 视口设置足够大以容纳主体及标注,居中对齐 -->
<svg id="cyclone-svg" viewBox="0 0 1000 800" preserveAspectRatio="xMidYMid meet">
<defs>
<linearGradient id="metalGrad" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" stop-color="#334155" />
<stop offset="50%" stop-color="#64748b" />
<stop offset="100%" stop-color="#334155" />
</linearGradient>
<linearGradient id="coneGrad" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" stop-color="rgba(30, 41, 59, 0.8)" />
<stop offset="50%" stop-color="rgba(71, 85, 105, 0.4)" />
<stop offset="100%" stop-color="rgba(30, 41, 59, 0.8)" />
</linearGradient>
<marker id="arrow-green" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="6" markerHeight="6" orient="auto">
<path d="M 0 0 L 10 5 L 0 10 z" fill="#a3e635" />
</marker>
<!-- 辉光滤镜,用于静态标注的高亮 -->
<filter id="glow-text">
<feGaussianBlur stdDeviation="2" result="blur" />
<feMerge>
<feMergeNode in="blur"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
</defs>
<!-- 1. 背景环境:现有缓冲罐轮廓 (虚线,表示不增加系统复杂度的原有资源) -->
<g class="environment" opacity="0.4">
<path d="M 150 100 L 150 500 L 500 850 L 850 500 L 850 100" fill="none" stroke="#475569" stroke-width="2" stroke-dasharray="10 6" />
<text x="170" y="130" fill="#94a3b8" font-size="16" letter-spacing="2">缓冲罐主体边界 (现有空间)</text>
</g>
<!-- 2. 管道与外部结构 (静态底图层) -->
<g class="infrastructure">
<!-- 进料管 -->
<path d="M 80 400 L 400 400 L 400 450 L 80 450 Z" fill="url(#metalGrad)" stroke="#1e293b" stroke-width="2"/>
<!-- 顶部溢流清液管线 (延伸至右侧放液管线) -->
<path d="M 470 450 L 530 450 L 530 250 L 900 250 L 900 310 L 470 310 Z" fill="url(#metalGrad)" stroke="#1e293b" stroke-width="2"/>
</g>
<!-- 3. 旋流沉砂结构主体 (后半部分或透明外壳) -->
<g class="cyclone-body">
<!-- 圆柱段 -->
<path d="M 400 380 L 600 380 L 600 480 L 400 480 Z" fill="url(#coneGrad)" stroke="#64748b" stroke-width="2"/>
<!-- 锥形段 -->
<path d="M 400 480 L 600 480 L 525 640 L 475 640 Z" fill="url(#coneGrad)" stroke="#64748b" stroke-width="2"/>
<!-- 聚氨酯耐磨衬里 (内部局部强化高亮) -->
<path d="M 406 480 L 594 480 L 521 636 L 479 636 Z" fill="none" stroke="#f59e0b" stroke-width="4" stroke-linejoin="round"/>
</g>
<!-- 4. 底部沉砂收集与排砂系统 -->
<g class="bottom-system">
<!-- 法兰接口 -->
<rect x="465" y="640" width="70" height="15" fill="#475569" rx="2"/>
<!-- 排砂手阀 -->
<!-- 阀体蝴蝶结形状 -->
<path d="M 460 655 L 510 690 M 510 655 L 460 690" fill="none" stroke="#94a3b8" stroke-width="6"/>
<circle cx="485" cy="672.5" r="5" fill="#cbd5e1"/>
<!-- 阀杆与手轮 -->
<line x1="485" y1="672.5" x2="540" y2="672.5" stroke="#cbd5e1" stroke-width="4"/>
<rect x="535" y="652.5" width="8" height="40" fill="#ef4444" rx="4"/>
<!-- 沉砂收集包 -->
<path d="M 450 690 L 520 690 L 535 780 L 435 780 Z" fill="rgba(15, 23, 42, 0.95)" stroke="#64748b" stroke-width="3" stroke-linejoin="round"/>
</g>
<!-- 5. 动态粒子层 (由 JS 动态生成控制,展示流体与砂粒轨迹) -->
<g id="particle-layer"></g>
<!-- 6. 视觉引导标注层 (位于最上层,明确解释 TRIZ 特征) -->
<g class="annotations">
<!-- 进料动能标注 -->
<path d="M 200 370 L 250 310 L 380 310" fill="none" stroke="#a3e635" stroke-width="1.5" stroke-dasharray="4 4"/>
<circle cx="200" cy="370" r="4" fill="#a3e635"/>
<text x="390" y="315" fill="#a3e635" font-size="15" font-weight="bold" filter="url(#glow-text)">含砂浆液进料 (利用自带压能)</text>
<path d="M 120 425 L 180 425" fill="none" stroke="#a3e635" stroke-width="3" marker-end="url(#arrow-green)"/>
<!-- IFR 离心分离标注 -->
<path d="M 610 430 L 680 430" fill="none" stroke="#f8fafc" stroke-width="1.5"/>
<circle cx="610" cy="430" r="4" fill="#f8fafc"/>
<text x="690" y="435" fill="#f8fafc" font-size="15" font-weight="bold">IFR 状态:流体动能转化为离心力</text>
<text x="690" y="455" fill="#94a3b8" font-size="13">粗砂被甩向外壁,避免引入复杂设备</text>
<!-- 聚氨酯衬里标注 -->
<path d="M 570 550 L 660 550 L 680 580" fill="none" stroke="#f59e0b" stroke-width="1.5"/>
<circle cx="570" cy="550" r="4" fill="#f59e0b"/>
<text x="690" y="585" fill="#f59e0b" font-size="15" font-weight="bold">聚氨酯衬里 (橙色边界)</text>
<text x="690" y="605" fill="#94a3b8" font-size="13">局部强化原则:抵御高浓度粗砂磨损</text>
<!-- 收集包标注 -->
<path d="M 545 740 L 630 740" fill="none" stroke="#cbd5e1" stroke-width="1.5"/>
<circle cx="535" cy="740" r="4" fill="#cbd5e1"/>
<text x="640" y="735" fill="#f8fafc" font-size="15" font-weight="bold">沉砂收集包 & 排砂手阀</text>
<text x="640" y="755" fill="#94a3b8" font-size="13">集中收集,定期排空</text>
<!-- 清液上溢标注 -->
<path d="M 720 250 L 720 200 L 750 200" fill="none" stroke="#22d3ee" stroke-width="1.5"/>
<circle cx="720" cy="250" r="4" fill="#22d3ee"/>
<text x="760" y="205" fill="#22d3ee" font-size="15" font-weight="bold" filter="url(#glow-text)">上溢清液至放液管线</text>
<text x="760" y="225" fill="#94a3b8" font-size="13">彻底消除后端蝶阀与管道的堵塞磨损</text>
</g>
</svg>
</div>
</main>
<script>
document.addEventListener('DOMContentLoaded', () => {
const particleLayer = document.getElementById('particle-layer');
const slider = document.getElementById('pressure-slider');
const valDisplay = document.getElementById('pressure-val');
let particles = [];
// 为保证动画流畅并充分展示效果,设置合理的粒子密度
const MAX_PARTICLES = 250;
const CENTER_X = 500;
// 创建单个粒子并初始化其状态
function spawnParticle() {
// 30%的概率生成粗砂(橙色),70%生成清液(青色)
const isSand = Math.random() > 0.7;
const el = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
const p = {
el,
type: isSand ? 'sand' : 'liquid',
phase: 'inlet', // 生命周期阶段:inlet -> cyclone-down -> (sand: bag) / (liquid: cyclone-up -> overflow)
x: 60 + Math.random() * 40,
y: 405 + Math.random() * 40,
angle: Math.PI,
life: 1,
radius: isSand ? 3.5 : 2.5
};
el.setAttribute('r', p.radius);
el.setAttribute('fill', isSand ? '#f59e0b' : '#22d3ee');
// 为了性能,仅给少量粗砂加阴影,其余靠纯色对比
if (isSand && Math.random() > 0.5) {
el.style.filter = 'drop-shadow(0 0 3px #f59e0b)';
}
particleLayer.appendChild(el);
particles.push(p);
}
// 核心动画循环
function animate() {
// 交互性:获取滑块压力值,计算动画速度倍率
const pressure = parseFloat(slider.value);
valDisplay.innerText = pressure.toFixed(2) + ' MPa';
// 0.1MPa 对应 1x 速度, 0.3MPa 对应 2.5x 速度,体现压力对动能转换的影响
const speed = 1 + (pressure - 0.1) * 7.5;
particles.forEach(p => {
// 1. 直管进料阶段
if (p.phase === 'inlet') {
p.x += 4 * speed;
if (p.x >= 420) {
p.phase = 'cyclone-down';
p.x = 420; // 锁定进入旋流器的切入点
}
p.el.setAttribute('cx', p.x);
p.el.setAttribute('cy', p.y);
}
// 2. 旋流下降阶段 (沿壁贴切下旋)
else if (p.phase === 'cyclone-down') {
// 旋流角速度和下降速度
p.angle -= 0.12 * speed;
p.y += 1.2 * speed;
// 计算当前高度对应的锥斗半径 (实现漏斗形下旋)
let currentRadius = 90; // 上部圆柱区半径
if (p.y > 480) {
// 锥形区半径递减,高度480~640, 半径从90降到25
let progress = (p.y - 480) / 160;
currentRadius = 90 - (65 * progress);
}
// 将 3D 螺旋投影到 2D 视角,加上垂直倾角产生立体感
let vx = CENTER_X + Math.cos(p.angle) * currentRadius;
let vy = p.y + Math.sin(p.angle) * currentRadius * 0.25;
// 根据螺旋角度调整透明度,模拟粒子在筒体前后的遮挡效果 (IFR视觉暗示)
let isFront = Math.sin(p.angle) > 0;
p.el.setAttribute('opacity', isFront ? '1' : '0.2');
p.el.setAttribute('cx', vx);
p.el.setAttribute('cy', vy);
// 粗砂落入收集包
if (p.type === 'sand' && p.y > 640) {
p.phase = 'bag';
}
// 清液在下旋一段距离后转向中心上升
if (p.type === 'liquid' && p.y > 520 + Math.random() * 80) {
p.phase = 'cyclone-up';
}
}
// 3. 中心清液上升阶段 (内旋流)
else if (p.phase === 'cyclone-up') {
p.angle -= 0.25 * speed; // 内旋流转速更快
p.y -= 2 * speed;
// 中心涡流半径较小
let currentRadius = 15 + Math.random() * 10;
let vx = CENTER_X + Math.cos(p.angle) * currentRadius;
let vy = p.y + Math.sin(p.angle) * currentRadius * 0.25;
p.el.setAttribute('opacity', '0.9'); // 中心液流无遮挡
p.el.setAttribute('cx', vx);
p.el.setAttribute('cy', vy);
// 到达顶部溢流口
if (p.y < 300 + Math.random() * 20) {
p.phase = 'overflow';
}
}
// 4. 清液溢出阶段 (去管线)
else if (p.phase === 'overflow') {
if (!p.outX) p.outX = CENTER_X;
p.outX += 4 * speed;
p.el.setAttribute('cx', p.outX);
p.el.setAttribute('cy', p.y);
// 飞出屏幕右侧后销毁
if (p.outX > 950) p.life = 0;
}
// 5. 沉砂收集阶段 (沉降到底部收集包)
else if (p.phase === 'bag') {
if (!p.bagY) p.bagY = 640;
p.bagY += 1.5 * speed;
// 在收集包内随机散落
p.el.setAttribute('cx', CENTER_X + (Math.random() - 0.5) * 60);
p.el.setAttribute('cy', p.bagY);
p.el.setAttribute('opacity', p.life);
// 堆积并逐渐淡出 (防止 DOM 节点无限增加)
if (p.bagY > 730 + Math.random() * 30) {
p.life -= 0.02 * speed;
}
}
});
// 垃圾回收:清理生命周期结束的粒子
for (let i = particles.length - 1; i >= 0; i--) {
if (particles[i].life <= 0) {
particleLayer.removeChild(particles[i].el);
particles.splice(i, 1);
}
}
// 根据压力动态调整粒子生成密度
const targetCount = Math.floor(MAX_PARTICLES * (speed / 1.5));
if (particles.length < targetCount) {
// 每帧补充粒子
let spawnCount = Math.ceil(1.5 * speed);
for (let i = 0; i < spawnCount; i++) {
spawnParticle();
}
}
// 持续请求下一帧,实现自动播放与流畅动画
requestAnimationFrame(animate);
}
// 启动动画 (满足重开即播、自动播放要求)
animate();
});
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>最终理想解 (IFR) - 旋流沉砂分离结构</title>
<style>
:root {
--bg-dark: #0f172a;
--panel-bg: #1e293b;
--border-color: #334155;
--text-main: #f8fafc;
--text-muted: #94a3b8;
--accent-blue: #38bdf8;
--accent-sand: #fcd34d;
--success: #10b981;
--danger: #ef4444;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
background-color: var(--bg-dark);
color: var(--text-main);
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
width: 100vw;
}
.container {
display: flex;
width: 100%;
height: 100%;
max-width: 1400px;
}
/* Sidebar Control Panel */
.sidebar {
width: 360px;
background: var(--panel-bg);
border-right: 1px solid var(--border-color);
padding: 2rem;
display: flex;
flex-direction: column;
gap: 2rem;
box-shadow: 4px 0 20px rgba(0, 0, 0, 0.4);
z-index: 10;
overflow-y: auto;
}
.header h2 {
font-size: 1.5rem;
font-weight: 700;
margin-bottom: 0.75rem;
letter-spacing: 0.5px;
}
.badge-ifr {
display: inline-block;
background: rgba(16, 185, 129, 0.15);
color: var(--success);
padding: 0.3rem 0.75rem;
border-radius: 6px;
font-size: 0.85rem;
font-weight: 600;
border: 1px solid rgba(16, 185, 129, 0.3);
}
.info-card {
background: rgba(15, 23, 42, 0.6);
padding: 1.25rem;
border-radius: 8px;
border-left: 4px solid var(--accent-blue);
font-size: 0.9rem;
line-height: 1.6;
color: var(--text-muted);
box-shadow: inset 0 2px 10px rgba(0,0,0,0.1);
}
.info-card strong {
color: var(--text-main);
}
.info-card p {
margin-bottom: 0.75rem;
}
.info-card p:last-child {
margin-bottom: 0;
}
/* Controls */
.controls-section {
display: flex;
flex-direction: column;
gap: 1.5rem;
}
.control-group {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.control-header {
display: flex;
justify-content: space-between;
font-size: 0.9rem;
color: var(--text-muted);
font-weight: 500;
}
.val-display {
color: var(--accent-blue);
font-family: monospace;
font-size: 1rem;
}
input[type=range] {
-webkit-appearance: none;
width: 100%;
background: transparent;
}
input[type=range]::-webkit-slider-thumb {
-webkit-appearance: none;
height: 16px;
width: 16px;
border-radius: 50%;
background: var(--accent-blue);
cursor: pointer;
margin-top: -6px;
box-shadow: 0 0 10px rgba(56, 189, 248, 0.5);
transition: transform 0.1s;
}
input[type=range]::-webkit-slider-thumb:hover {
transform: scale(1.2);
}
input[type=range]::-webkit-slider-runnable-track {
width: 100%;
height: 4px;
cursor: pointer;
background: var(--border-color);
border-radius: 2px;
}
.btn {
background: transparent;
color: var(--text-main);
border: 1px solid var(--border-color);
padding: 0.85rem;
border-radius: 6px;
font-size: 0.95rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
text-align: center;
}
.btn:hover {
background: rgba(51, 65, 85, 0.5);
}
.btn.active {
background: rgba(239, 68, 68, 0.15);
color: var(--danger);
border-color: rgba(239, 68, 68, 0.4);
box-shadow: inset 0 0 15px rgba(239, 68, 68, 0.1);
}
/* Legend */
.legend {
margin-top: auto;
display: flex;
flex-direction: column;
gap: 0.75rem;
padding-top: 1.5rem;
border-top: 1px solid var(--border-color);
}
.legend-item {
display: flex;
align-items: center;
gap: 0.75rem;
font-size: 0.85rem;
color: var(--text-muted);
}
.dot {
width: 12px;
height: 12px;
border-radius: 50%;
display: inline-block;
}
.dot.sand { background: var(--accent-sand); box-shadow: 0 0 8px var(--accent-sand); }
.dot.liquid { background: var(--accent-blue); box-shadow: 0 0 8px var(--accent-blue); }
/* Main SVG Area */
.main-view {
flex: 1;
position: relative;
display: flex;
align-items: center;
justify-content: center;
background: radial-gradient(circle at center, #162032 0%, var(--bg-dark) 100%);
}
svg {
width: 100%;
height: 100%;
max-width: 900px;
max-height: 900px;
}
/* Responsive */
@media (max-width: 900px) {
.container { flex-direction: column; }
.sidebar { width: 100%; height: auto; max-height: 40vh; border-right: none; border-bottom: 1px solid var(--border-color); }
}
</style>
</head>
<body>
<div class="container">
<aside class="sidebar">
<div class="header">
<h2>旋流沉砂分离结构</h2>
<div class="badge-ifr">TRIZ 最终理想解 (IFR)</div>
</div>
<div class="info-card">
<p><strong>核心机理:</strong>引入水力旋流原理,利用流体自身动能使其发生离心分离。粗砂沿壁下沉,中心清液上溢。</p>
<p><strong>化解矛盾:</strong>无需增加复杂的机械过滤部件(极高资源利用率),彻底阻断了粗砂对下游放液管线与蝶阀的磨损与堵塞。</p>
</div>
<div class="controls-section">
<div class="control-group">
<div class="control-header">
<span>系统旋流压力</span>
<span class="val-display" id="pressureVal">0.20 MPa</span>
</div>
<input type="range" id="pressureSlider" min="0.1" max="0.3" step="0.01" value="0.2">
</div>
<div class="control-group">
<div class="control-header">
<span>浆液含砂比例模拟</span>
<span class="val-display" id="sandVal">中等</span>
</div>
<input type="range" id="sandSlider" min="0.1" max="0.8" step="0.1" value="0.4">
</div>
<button id="dischargeBtn" class="btn">手动开启排砂手阀</button>
</div>
<div class="legend">
<div class="legend-item"><span class="dot sand"></span> 分离沉砂 (粒度 0.1~2mm)</div>
<div class="legend-item"><span class="dot liquid"></span> 中心溢流清液 (无磨损)</div>
</div>
</aside>
<main class="main-view">
<svg viewBox="0 0 800 800" preserveAspectRatio="xMidYMid meet" xmlns="http://www.w3.org/2000/svg">
<defs>
<!-- Background Grid -->
<pattern id="grid" width="40" height="40" patternUnits="userSpaceOnUse">
<path d="M 40 0 L 0 0 0 40" fill="none" stroke="#1e293b" stroke-width="1"/>
</pattern>
<!-- Gradients for structural pipes -->
<linearGradient id="pipeH" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" stop-color="#1e293b" />
<stop offset="50%" stop-color="#334155" />
<stop offset="100%" stop-color="#1e293b" />
</linearGradient>
<linearGradient id="pipeV" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" stop-color="#1e293b" />
<stop offset="50%" stop-color="#334155" />
<stop offset="100%" stop-color="#1e293b" />
</linearGradient>
</defs>
<!-- Background -->
<rect width="100%" height="100%" fill="url(#grid)" />
<!-- Highlight Banner -->
<g transform="translate(480, 40)">
<rect x="0" y="0" width="280" height="46" rx="6" fill="rgba(16, 185, 129, 0.1)" stroke="#10b981" stroke-width="1.5"/>
<text x="140" y="28" fill="#10b981" font-size="15" font-weight="bold" text-anchor="middle">无源分离 · 消除系统磨损矛盾</text>
</g>
<!-- Particles Layer (Behind foreground glass but in front of background) -->
<g id="particles-layer"></g>
<!-- ================= STRUCTURAL COMPONENTS ================= -->
<!-- Inlet Pipe (Left) -->
<path d="M100 210 L300 210 L300 270 L100 270 Z" fill="url(#pipeH)" stroke="#475569" stroke-width="2"/>
<path d="M100 150 C100 180 120 210 200 210" fill="none" stroke="#475569" stroke-width="2" stroke-dasharray="6,4"/>
<!-- Inner Vortex Finder (Clear Liquid Exit) -->
<path d="M380 270 L380 150 L680 150 L680 110 L420 110 L420 270 Z" fill="rgba(15, 23, 42, 0.8)" stroke="#475569" stroke-width="2"/>
<!-- Main Cyclone Outer Shell (Semi-transparent Glassy Look) -->
<path d="M300 200 L500 200 L500 300 L430 550 L430 650 L370 650 L370 550 L300 300 Z" fill="rgba(30, 41, 59, 0.5)" stroke="#64748b" stroke-width="3" stroke-linejoin="round"/>
<!-- Polyurethane Lining Indicator (Inner edge) -->
<path d="M304 302 L372 548 M496 302 L428 548" stroke="#f59e0b" stroke-width="4" fill="none" opacity="0.85" stroke-linecap="round"/>
<!-- Sand Discharge Pipe (Bottom) -->
<path d="M390 650 L410 650 L410 760 L390 760 Z" fill="url(#pipeV)" stroke="#475569" stroke-width="2"/>
<!-- ================= VALVES ================= -->
<!-- Manual Sand Discharge Valve (Bottom) -->
<g transform="translate(400, 690)">
<path d="M-20 -15 L20 10 L-20 10 L20 -15 Z" fill="#475569" stroke="#94a3b8" stroke-width="2" stroke-linejoin="round"/>
<line x1="20" y1="-2.5" x2="45" y2="-2.5" stroke="#94a3b8" stroke-width="4" stroke-linecap="round"/>
<!-- Animated Handwheel -->
<g id="valveWheel" style="transform-origin: 45px -2.5px; transition: transform 0.6s cubic-bezier(0.4, 0, 0.2, 1);">
<ellipse cx="45" cy="-2.5" rx="5" ry="22" fill="#ef4444" stroke="#b91c1c" stroke-width="2" />
<line x1="45" y1="-22" x2="45" y2="17" stroke="#b91c1c" stroke-width="2"/>
</g>
</g>
<!-- Protected Butterfly Valve (Right Clear Liquid Line) -->
<g transform="translate(560, 130)">
<!-- Flanges -->
<rect x="-22" y="-28" width="6" height="56" fill="#334155" stroke="#94a3b8" stroke-width="1.5" rx="1"/>
<rect x="16" y="-28" width="6" height="56" fill="#334155" stroke="#94a3b8" stroke-width="1.5" rx="1"/>
<!-- Valve Body -->
<rect x="-16" y="-22" width="32" height="44" fill="#1e293b" stroke="#94a3b8" stroke-width="1.5" />
<!-- Stem & Actuator -->
<rect x="-4" y="-45" width="8" height="23" fill="#94a3b8" />
<rect x="-18" y="-55" width="36" height="10" fill="#f59e0b" stroke="#b45309" stroke-width="1.5" rx="2"/>
<!-- Internal Disc (Open State) -->
<ellipse cx="0" cy="0" rx="3" ry="18" fill="#f8fafc" transform="rotate(-25)" />
</g>
<!-- ================= LABELS & POINTERS ================= -->
<g class="diagram-labels">
<!-- Inlet Label -->
<polyline points="150,180 180,140 250,140" fill="none" stroke="#cbd5e1" stroke-width="1.5" stroke-dasharray="3,3"/>
<text x="255" y="145" fill="#f8fafc" font-size="14" font-weight="bold">含砂浆液入口</text>
<!-- Lining Label -->
<polyline points="325,380 270,430 180,430" fill="none" stroke="#f59e0b" stroke-width="1.5" stroke-dasharray="3,3"/>
<text x="175" y="425" fill="#f59e0b" font-size="14" font-weight="bold" text-anchor="end">聚氨酯衬里</text>
<text x="175" y="445" fill="#94a3b8" font-size="12" text-anchor="end">耐磨核心工作区</text>
<!-- Liquid Outlet Label -->
<polyline points="470,130 500,80 580,80" fill="none" stroke="#38bdf8" stroke-width="1.5" stroke-dasharray="3,3"/>
<text x="585" y="75" fill="#38bdf8" font-size="14" font-weight="bold">中心清液溢流</text>
<!-- Butterfly Valve Label -->
<polyline points="560,170 530,220 610,220" fill="none" stroke="#10b981" stroke-width="1.5" stroke-dasharray="3,3"/>
<text x="615" y="215" fill="#f8fafc" font-size="14" font-weight="bold">目标保护:放液管线与蝶阀</text>
<text x="615" y="235" fill="#10b981" font-size="12">✔ 磨损源已彻底隔离</text>
<!-- Sand Collection Label -->
<polyline points="430,600 480,600" fill="none" stroke="#fcd34d" stroke-width="1.5" stroke-dasharray="3,3"/>
<text x="490" y="595" fill="#fcd34d" font-size="14" font-weight="bold">沉砂收集包</text>
<text x="490" y="615" fill="#94a3b8" font-size="12">分离粒度: 0.1~2mm</text>
<!-- Manual Valve Label -->
<polyline points="450,690 490,690" fill="none" stroke="#cbd5e1" stroke-width="1.5" stroke-dasharray="3,3"/>
<text x="500" y="695" fill="#f8fafc" font-size="14" font-weight="bold">排砂手阀</text>
</g>
</svg>
</main>
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
// UI Elements
const pressureSlider = document.getElementById('pressureSlider');
const pressureVal = document.getElementById('pressureVal');
const sandSlider = document.getElementById('sandSlider');
const sandVal = document.getElementById('sandVal');
const dischargeBtn = document.getElementById('dischargeBtn');
const valveWheel = document.getElementById('valveWheel');
const canvas = document.getElementById('particles-layer');
// State Variables
let isValveOpen = false;
let pressureMultiplier = 1.0;
let sandRatio = 0.4;
let particles = [];
const MAX_PARTICLES = 500;
// Map pressure (0.1-0.3) to multiplier (0.5 - 1.5 for speed)
const updatePressure = () => {
let val = parseFloat(pressureSlider.value);
pressureVal.textContent = val.toFixed(2) + ' MPa';
pressureMultiplier = (val - 0.1) * 5 + 0.5; // Maps 0.1->0.5, 0.3->1.5
};
const updateSand = () => {
sandRatio = parseFloat(sandSlider.value);
if(sandRatio < 0.3) sandVal.textContent = '低含砂';
else if(sandRatio < 0.6) sandVal.textContent = '中等含砂';
else sandVal.textContent = '高含砂';
};
pressureSlider.addEventListener('input', updatePressure);
sandSlider.addEventListener('input', updateSand);
dischargeBtn.addEventListener('click', () => {
isValveOpen = !isValveOpen;
if (isValveOpen) {
dischargeBtn.textContent = '关闭排砂手阀';
dischargeBtn.classList.add('active');
valveWheel.style.transform = 'rotate(90deg)';
} else {
dischargeBtn.textContent = '手动开启排砂手阀';
dischargeBtn.classList.remove('active');
valveWheel.style.transform = 'rotate(0deg)';
}
});
// Particle Generation
function createParticle() {
if (particles.length > MAX_PARTICLES) return;
const isSand = Math.random() < sandRatio;
const p = {
id: Math.random(),
type: isSand ? 'sand' : 'liquid',
el: document.createElementNS("http://www.w3.org/2000/svg", "circle"),
phase: 1,
x: 100, // Starts at left inlet
y_pos: 240 + (Math.random() * 20 - 10), // Spread vertically in pipe
angle: 0,
r: 0, // Swirl radius
r_ratio: isSand ? (0.85 + Math.random()*0.15) : (0.1 + Math.random()*0.6), // Sand stays outside, liquid inside
turn_y: 360 + Math.random() * 140, // Where liquid starts moving up
randOffset: Math.random() * 6 // For organic settling
};
p.el.setAttribute("r", isSand ? 3.5 : 2.5);
p.el.setAttribute("fill", isSand ? "#fcd34d" : "#38bdf8");
if (!isSand) p.el.setAttribute("opacity", "0.7");
canvas.appendChild(p.el);
particles.push(p);
}
// Main Animation Loop
function update() {
// Generate particles based on flow rate (pressure)
let genChance = 0.6 * pressureMultiplier;
if (Math.random() < genChance) createParticle();
if (Math.random() < genChance - 0.5) createParticle(); // extra chance at high pressure
for (let i = particles.length - 1; i >= 0; i--) {
let p = particles[i];
// Phase 1: Entry Pipe
if (p.phase === 1) {
p.x += 3.5 * pressureMultiplier;
p.el.setAttribute("cx", p.x);
p.el.setAttribute("cy", p.y_pos);
// Reach cyclone body
if (p.x >= 330) {
p.phase = 2;
p.angle = Math.PI; // Enter tangentially from left
}
}
// Phase 2: Downward Swirl (Primary Separation)
else if (p.phase === 2) {
p.angle -= 0.16 * pressureMultiplier;
p.y_pos += 1.8 * pressureMultiplier;
// Calculate max bounds based on cone shape
let max_r = 95;
if (p.y_pos > 300) {
max_r = 95 - 65 * ((p.y_pos - 300) / 250); // Taper down to radius 30 at y=550
}
p.r = max_r * p.r_ratio;
// 2D projection of 3D spiral
let cx = 400 + p.r * Math.cos(p.angle);
let cy = p.y_pos + p.r * Math.sin(p.angle) * 0.15; // 0.15 tilt factor
p.el.setAttribute("cx", cx);
p.el.setAttribute("cy", cy);
// Pseudo-3D Depth opacity
if (Math.sin(p.angle) < 0) {
p.el.style.opacity = p.type === 'sand' ? '0.4' : '0.2';
} else {
p.el.style.opacity = p.type === 'sand' ? '1' : '0.7';
}
// Phase transitions
if (p.type === 'liquid' && p.y_pos > p.turn_y) {
p.phase = 3;
p.target_r = 5 + Math.random() * 12; // Shrink to inner vortex
}
if (p.type === 'sand' && p.y_pos > 550) {
p.phase = 5; // Enter collection bag
}
}
// Phase 3: Upward Swirl (Clear Liquid)
else if (p.phase === 3) {
p.angle -= 0.3 * pressureMultiplier; // Inner vortex spins faster
p.y_pos -= 3.5 * pressureMultiplier; // Moves up
p.r += (p.target_r - p.r) * 0.1; // Smoothly constrain radius
let cx = 400 + p.r * Math.cos(p.angle);
let cy = p.y_pos + p.r * Math.sin(p.angle) * 0.15;
p.el.setAttribute("cx", cx);
p.el.setAttribute("cy", cy);
p.el.style.opacity = "0.8"; // Make clearly visible in inner tube
if (p.y_pos < 130) {
p.phase = 4;
p.exit_x = cx;
p.exit_y = cy;
}
}
// Phase 4: Liquid Exit Pipe
else if (p.phase === 4) {
p.exit_x += 4.5 * pressureMultiplier;
p.el.setAttribute("cx", p.exit_x);
p.el.setAttribute("cy", p.exit_y);
if (p.exit_x > 750) {
p.el.remove();
particles.splice(i, 1);
}
}
// Phase 5: Sand Accumulation
else if (p.phase === 5) {
if (isValveOpen) {
p.phase = 6;
} else {
// Dynamic pile calculation based on settled sand
let settledCount = particles.filter(pt => pt.phase === 5 && pt.y_pos >= 560).length;
let fillHeight = Math.min(85, settledCount * 0.4);
let actualBottom = 645 - fillHeight + p.randOffset;
if (p.y_pos < actualBottom) {
// Still falling in bag
p.angle -= 0.04 * pressureMultiplier;
p.y_pos += 2 * pressureMultiplier;
} else if (p.y_pos > actualBottom + 3) {
// Slump down if space created
p.y_pos += 1;
}
p.r = 24 * p.r_ratio;
let cx = 400 + p.r * Math.cos(p.angle);
let cy = p.y_pos + p.r * Math.sin(p.angle) * 0.15;
p.el.setAttribute("cx", cx);
p.el.setAttribute("cy", cy);
// Manage excess particles to prevent DOM bloat
if (settledCount > 250 && Math.random() < 0.05 && p.y_pos >= 640) {
p.el.remove();
particles.splice(i, 1);
}
}
}
// Phase 6: Sand Discharge
else if (p.phase === 6) {
p.y_pos += 5 * pressureMultiplier;
let cx = parseFloat(p.el.getAttribute("cx"));
cx += (400 - cx) * 0.15; // Funnel into discharge pipe center
p.el.setAttribute("cx", cx);
p.el.setAttribute("cy", p.y_pos);
if (p.y_pos > 780) {
p.el.remove();
particles.splice(i, 1);
}
}
}
requestAnimationFrame(update);
}
// Initialize state & start loop
updatePressure();
updateSand();
requestAnimationFrame(update);
});
</script>
</body>
</html>
积分规则:每轮对话扣10分
等待动画代码生成...
