独立渲染引擎就绪引擎就绪
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>TRIZ IFR: 仿生锯齿文丘里管降噪系统</title>
<style>
:root {
--bg-base: #030610;
--grid-color: rgba(30, 58, 138, 0.2);
--cyan-glow: #00F0FF;
--magenta-glow: #FF007A;
--warning-red: #FF3333;
--text-main: #8892B0;
--text-light: #E2E8F0;
--panel-bg: rgba(5, 10, 20, 0.7);
--panel-border: rgba(0, 240, 255, 0.2);
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
background-color: var(--bg-base);
color: var(--text-main);
font-family: 'Inter', system-ui, -apple-system, sans-serif;
overflow: hidden;
width: 100vw;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
#animation-container {
position: relative;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
background: radial-gradient(circle at center, #0B1120 0%, #030610 100%);
}
/* SVG Stying */
svg {
width: 100%;
height: 100%;
max-width: 1600px;
max-height: 900px;
display: block;
}
.neon-cyan { stroke: var(--cyan-glow); filter: drop-shadow(0 0 4px var(--cyan-glow)); }
.neon-magenta { stroke: var(--magenta-glow); filter: drop-shadow(0 0 4px var(--magenta-glow)); }
.solid-body { fill: #0F172A; stroke: #1E293B; stroke-width: 2; }
.tech-line { stroke: rgba(0, 240, 255, 0.3); stroke-width: 1; stroke-dasharray: 4 4; }
text {
font-family: 'Courier New', monospace;
font-size: 12px;
fill: var(--text-main);
user-select: none;
letter-spacing: 1px;
}
.text-highlight { fill: var(--cyan-glow); font-weight: bold; }
.text-warning { fill: var(--warning-red); opacity: 0; transition: opacity 0.3s ease; }
/* UI Overlays (Kept to edges, small sizes) */
.ui-panel {
position: absolute;
background: var(--panel-bg);
border: 1px solid var(--panel-border);
border-radius: 8px;
padding: 16px;
backdrop-filter: blur(8px);
z-index: 10;
}
.top-left-panel { top: 24px; left: 24px; max-width: 300px; }
.bottom-left-panel { bottom: 24px; left: 24px; width: 280px; }
h1 {
font-size: 14px;
color: var(--text-light);
margin-bottom: 6px;
text-transform: uppercase;
letter-spacing: 1.5px;
display: flex;
align-items: center;
gap: 8px;
}
h1::before {
content: '';
display: inline-block;
width: 8px;
height: 8px;
background-color: var(--cyan-glow);
box-shadow: 0 0 8px var(--cyan-glow);
border-radius: 50%;
}
p.desc { font-size: 11px; line-height: 1.6; color: #64748B; }
/* Slider Controls */
.control-group { margin-top: 12px; }
.control-label {
display: flex;
justify-content: space-between;
font-size: 10px;
color: var(--cyan-glow);
margin-bottom: 6px;
}
input[type=range] {
-webkit-appearance: none;
width: 100%;
background: rgba(255,255,255,0.05);
height: 4px;
border-radius: 2px;
outline: none;
}
input[type=range]::-webkit-slider-thumb {
-webkit-appearance: none;
width: 12px;
height: 12px;
border-radius: 50%;
background: var(--cyan-glow);
box-shadow: 0 0 10px var(--cyan-glow);
cursor: pointer;
transition: transform 0.1s;
}
input[type=range]::-webkit-slider-thumb:hover { transform: scale(1.2); }
/* Status Indicator */
.status-dot {
display: inline-block;
width: 6px;
height: 6px;
border-radius: 50%;
margin-right: 4px;
background-color: var(--cyan-glow);
box-shadow: 0 0 6px var(--cyan-glow);
}
.status-dot.danger {
background-color: var(--warning-red);
box-shadow: 0 0 6px var(--warning-red);
}
</style>
</head>
<body>
<div id="animation-container">
<!-- UI Panels -->
<div class="ui-panel top-left-panel">
<h1>TRIZ IFR 最终理想解</h1>
<p class="desc">
方案聚焦:引入流线型文丘里收缩管与仿生猫头鹰翼缘锯齿。<br>
理想状态:消除台阶涡流,打散边界层剪切,实现高频白噪音转化。
</p>
</div>
<div class="ui-panel bottom-left-panel">
<div class="control-label">
<span><i class="status-dot" id="status-indicator"></i> 运行状态监测</span>
<span id="speed-value">标准流速</span>
</div>
<p class="desc" style="margin-bottom:8px;" id="status-desc">系统稳定运行,湍流碰撞完全消除。</p>
<div class="control-group">
<div class="control-label"><span>风机负载系数 (Mach)</span></div>
<input type="range" id="speed-slider" min="1" max="10" value="4" step="0.1">
</div>
</div>
<!-- Main SVG Animation -->
<svg viewBox="0 0 1400 800" preserveAspectRatio="xMidYMid meet" id="main-scene">
<defs>
<!-- Grid Pattern -->
<pattern id="hex-grid" width="40" height="40" patternUnits="userSpaceOnUse">
<path d="M 40 0 L 0 0 0 40" fill="none" stroke="var(--grid-color)" stroke-width="0.5"/>
<circle cx="0" cy="0" r="1" fill="var(--grid-color)"/>
</pattern>
<!-- Glow Filters -->
<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-magenta" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur stdDeviation="4" result="blur" />
<feComposite in="SourceGraphic" in2="blur" operator="over" />
</filter>
<!-- Metallic Body Gradient -->
<linearGradient id="body-grad" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#1E293B" stop-opacity="0.8"/>
<stop offset="50%" stop-color="#0B1120" stop-opacity="0.9"/>
<stop offset="100%" stop-color="#1E293B" stop-opacity="0.8"/>
</linearGradient>
<linearGradient id="edge-grad" x1="0" y1="0" x2="1" y2="0">
<stop offset="0%" stop-color="#0B1120"/>
<stop offset="100%" stop-color="#1E293B"/>
</linearGradient>
<!-- Clip Path for Magnifier -->
<clipPath id="magnify-clip">
<circle cx="1150" cy="250" r="140" />
</clipPath>
</defs>
<!-- Background -->
<rect width="100%" height="100%" fill="url(#hex-grid)" />
<!-- ============================== -->
<!-- MAIN CROSS SECTION VIEW -->
<!-- ============================== -->
<g id="main-duct" transform="translate(50, 0)">
<!-- Dimension Lines & Annotations -->
<g opacity="0.6">
<!-- Inlet -->
<line x1="50" y1="200" x2="50" y2="600" stroke="#475569" stroke-width="1" stroke-dasharray="2 2"/>
<text x="60" y="405" fill="#94A3B8">INTAKE / 广口进气段</text>
<!-- Transition Angle -->
<path d="M 450,200 A 100,100 0 0,1 550,230" fill="none" stroke="#475569" stroke-width="1" stroke-dasharray="2 2"/>
<text x="460" y="190" fill="#94A3B8">θ < 15° 平滑过渡控制</text>
<!-- Bernoulli Equation decorative -->
<text x="450" y="650" fill="#475569" font-style="italic">P₁ + ½ρv₁² = P₂ + ½ρv₂² (伯努利原理)</text>
</g>
<!-- Solid Wall Background / Hardware -->
<!-- Top Wall Profile: Inlet y=200, smooth curve down to y=320 at x=800 -->
<path d="M -50,150 L 400,150 L 400,200 C 550,200 650,320 800,320 L 800,150 Z" fill="url(#body-grad)"/>
<path d="M -50,200 L 400,200 C 550,200 650,320 800,320" fill="none" class="neon-cyan" stroke-width="2"/>
<!-- Bottom Wall Profile: Inlet y=600, smooth curve up to y=480 at x=800 -->
<path d="M -50,650 L 400,650 L 400,600 C 550,600 650,480 800,480 L 800,650 Z" fill="url(#body-grad)"/>
<path d="M -50,600 L 400,600 C 550,600 650,480 800,480" fill="none" class="neon-cyan" stroke-width="2"/>
<!-- Trailing Edge Hardware block (Right side before exit) -->
<rect x="800" y="320" width="20" height="160" fill="#334155" />
<line x1="820" y1="320" x2="820" y2="480" stroke="#00F0FF" stroke-width="2" stroke-dasharray="4 2"/>
<!-- Invisible Flow Paths for JS Particles -->
<g id="flow-paths" opacity="0">
<path class="flow-path" d="M -20,230 L 400,230 C 550,230 650,335 820,335" />
<path class="flow-path" d="M -20,280 L 400,280 C 550,280 650,360 820,360" />
<path class="flow-path" d="M -20,340 L 400,340 C 550,340 650,385 820,385" />
<path class="flow-path" d="M -20,400 L 820,400" /> <!-- Centerline -->
<path class="flow-path" d="M -20,460 L 400,460 C 550,460 650,415 820,415" />
<path class="flow-path" d="M -20,520 L 400,520 C 550,520 650,440 820,440" />
<path class="flow-path" d="M -20,570 L 400,570 C 550,570 650,465 820,465" />
</g>
<!-- Dynamic Particle Groups -->
<g id="particles-group" filter="url(#glow-cyan)"></g>
<g id="main-vortices-group" filter="url(#glow-magenta)"></g>
<!-- Annotations Pointing to Structure -->
<circle cx="600" cy="270" r="4" fill="#00F0FF" />
<line x1="600" y1="270" x2="520" y2="160" class="tech-line"/>
<rect x="360" y="130" width="160" height="24" fill="#050B14" stroke="#1E3A8A" stroke-width="1"/>
<text x="370" y="146" class="text-highlight">曲率连续 / 边界层防剥离</text>
<circle cx="820" cy="330" r="4" fill="#FF007A" />
<line x1="820" y1="330" x2="880" y2="280" class="tech-line"/>
<text x="840" y="270" fill="#E2E8F0">边缘激扰区</text>
</g>
<!-- ============================== -->
<!-- MAGNIFIER CALLOUT (TOP DOWN) -->
<!-- ============================== -->
<!-- Connection lines -->
<path d="M 870, 400 L 980, 250 L 1010, 250" fill="none" stroke="#475569" stroke-width="1" stroke-dasharray="4 4"/>
<path d="M 870, 320 L 980, 150 L 1010, 150" fill="none" stroke="#475569" stroke-width="1" stroke-dasharray="4 4"/>
<!-- Magnifier Frame -->
<circle cx="1150" cy="250" r="145" fill="none" stroke="var(--cyan-glow)" stroke-width="2" opacity="0.3"/>
<circle cx="1150" cy="250" r="140" fill="#050912" stroke="#1E293B" stroke-width="4"/>
<!-- Inside Magnifier -->
<g clip-path="url(#magnify-clip)">
<!-- Grid inside -->
<rect x="1000" y="100" width="300" height="300" fill="url(#hex-grid)" opacity="0.5"/>
<!-- Solid hardware (Trailing edge from top-down) -->
<path d="M 1000,100 L 1150,100
L 1150,130 L 1180,145 L 1150,160
L 1150,190 L 1180,205 L 1150,220
L 1150,250 L 1180,265 L 1150,280
L 1150,310 L 1180,325 L 1150,340
L 1150,370 L 1180,385 L 1150,400
L 1000,400 Z"
fill="url(#edge-grad)" stroke="#38BDF8" stroke-width="1.5"/>
<!-- JS Spawner area for Callout -->
<g id="callout-particles" filter="url(#glow-cyan)"></g>
<g id="callout-vortices" filter="url(#glow-magenta)"></g>
</g>
<!-- Magnifier UI Overlay Elements -->
<path d="M 1010,250 L 1030,250" stroke="#00F0FF" stroke-width="2"/>
<circle cx="1150" cy="250" r="140" fill="none" stroke="url(#body-grad)" stroke-width="8"/>
<circle cx="1150" cy="250" r="140" fill="none" stroke="#00F0FF" stroke-width="1" stroke-dasharray="2 6"/>
<!-- Callout Text -->
<rect x="1200" y="100" width="160" height="50" fill="rgba(5, 11, 20, 0.8)" rx="4"/>
<text x="1210" y="120" class="text-highlight">仿生微小锯齿构型</text>
<text x="1210" y="140" fill="#94A3B8">深度: 2-3mm / 切割大尺度涡</text>
<!-- Overspeed Warning Label -->
<g id="warning-label" class="text-warning" transform="translate(1100, 420)">
<rect x="0" y="0" width="180" height="24" fill="rgba(255, 51, 51, 0.1)" stroke="#FF3333" stroke-width="1" rx="2"/>
<text x="10" y="16" fill="#FF3333" font-weight="bold">⚠️ 边界突破:微结构哨音共振</text>
</g>
</svg>
</div>
<script>
document.addEventListener("DOMContentLoaded", () => {
// DOM Elements
const flowPaths = document.querySelectorAll('.flow-path');
const mainParticlesGroup = document.getElementById('particles-group');
const mainVorticesGroup = document.getElementById('main-vortices-group');
const calloutParticlesGroup = document.getElementById('callout-particles');
const calloutVorticesGroup = document.getElementById('callout-vortices');
const speedSlider = document.getElementById('speed-slider');
const statusIndicator = document.getElementById('status-indicator');
const speedValueText = document.getElementById('speed-value');
const statusDescText = document.getElementById('status-desc');
const warningLabel = document.getElementById('warning-label');
// Data Structures
const particles = [];
const vortices = [];
const calloutParticles = [];
const calloutVortices = [];
// Configuration
const OVERSPEED_THRESHOLD = 7.5;
const NUM_PARTICLES_PER_PATH = 12;
const BASE_SPEED_MULT = 0.0008;
// Helper: Create SVG Element
function createSvgEl(type, attrs, parent) {
const el = document.createElementNS('http://www.w3.org/2000/svg', type);
for (let key in attrs) el.setAttribute(key, attrs[key]);
parent.appendChild(el);
return el;
}
// Initialize Main Flow Particles
flowPaths.forEach(path => {
const len = path.getTotalLength();
for (let i = 0; i < NUM_PARTICLES_PER_PATH; i++) {
particles.push({
path: path,
len: len,
progress: i / NUM_PARTICLES_PER_PATH,
// Using lines for a "streaking" high-speed look instead of dots
el: createSvgEl('line', {
stroke: '#00F0FF',
'stroke-width': 1.5,
'stroke-linecap': 'round',
opacity: 0.8
}, mainParticlesGroup)
});
}
});
// Initialize Callout Flow Particles (Horizontal straight lines hitting the edge)
// The edge is at x=1150-1180, let's spawn them at x=1000
const calloutYPositions = [120, 150, 180, 210, 240, 270, 300, 330, 360, 390];
calloutYPositions.forEach(y => {
for(let i=0; i<3; i++) {
calloutParticles.push({
x: 1000 + i * 50,
y: y + (Math.random()*10 - 5),
el: createSvgEl('line', {
stroke: '#00F0FF',
'stroke-width': 1.5,
opacity: 0.6
}, calloutParticlesGroup)
});
}
});
// Animation Loop
function animate() {
const rawSpeed = parseFloat(speedSlider.value);
const baseSpeed = rawSpeed * BASE_SPEED_MULT;
const isOverspeed = rawSpeed >= OVERSPEED_THRESHOLD;
// Update UI state
if (isOverspeed) {
statusIndicator.classList.add('danger');
speedValueText.textContent = `流速异常 (Mach ${rawSpeed.toFixed(1)})`;
speedValueText.style.color = 'var(--warning-red)';
statusDescText.textContent = "流速过高,锯齿结构产生高频共振啸叫失效。";
warningLabel.style.opacity = '1';
} else {
statusIndicator.classList.remove('danger');
speedValueText.textContent = `最优工况 (Mach ${rawSpeed.toFixed(1)})`;
speedValueText.style.color = 'var(--cyan-glow)';
statusDescText.textContent = "文丘里效应平滑加速,锯齿边缘转化剪切涡流为高频白噪。";
warningLabel.style.opacity = '0';
}
const vortexColor = isOverspeed ? '#FF3333' : '#FF007A';
// 1. Process Main Duct Particles
particles.forEach(p => {
// Acceleration logic: Bernoulli principle in Venturi tube
// Throat is approximately progress 0.5 to 0.9. Speed increases significantly here.
let speedFactor = 1;
if (p.progress > 0.4) {
// Ramp up speed smoothly up to 3.5x in the throat
speedFactor = 1 + Math.sin((p.progress - 0.4) * Math.PI) * 2.5;
}
p.progress += baseSpeed * speedFactor;
if (p.progress >= 0.98) {
// Reach the exit trailing edge -> Shatter into micro-vortices
p.progress = 0;
spawnMainVortex(p.path.getPointAtLength(p.len), vortexColor, isOverspeed, rawSpeed);
}
const pt1 = p.path.getPointAtLength(p.progress * p.len);
// Trail calculation based on speed
const trailLen = 5 + (speedFactor * rawSpeed * 1.5);
const backProgress = Math.max(0, p.progress - (trailLen / p.len));
const pt2 = p.path.getPointAtLength(backProgress * p.len);
p.el.setAttribute('x1', pt1.x);
p.el.setAttribute('y1', pt1.y);
p.el.setAttribute('x2', pt2.x);
p.el.setAttribute('y2', pt2.y);
});
// 2. Process Main Vortices
updateVortices(vortices, isOverspeed);
// 3. Process Callout Particles
calloutParticles.forEach(p => {
const currentSpeed = (baseSpeed * 1000) * 1.5;
p.x += currentSpeed;
// Interaction with the zigzag edge (approx x=1160)
if (p.x > 1160) {
spawnCalloutVortex(p.x, p.y, vortexColor, isOverspeed);
p.x = 1000;
p.y += (Math.random()*20 - 10); // slight random shift
}
p.el.setAttribute('x1', p.x);
p.el.setAttribute('y1', p.y);
p.el.setAttribute('x2', p.x - (currentSpeed * 4));
p.el.setAttribute('y2', p.y);
});
// 4. Process Callout Vortices
updateVortices(calloutVortices, isOverspeed, true);
requestAnimationFrame(animate);
}
function spawnMainVortex(pt, color, isOverspeed, speedVal) {
// IFR behavior: large low-frequency vortices are broken into tiny high-freq noise
// Over-speed behavior: resonance creates larger, grouped distinct pulses
const count = isOverspeed ? 1 : 3;
const sizeBase = isOverspeed ? 3 : 1;
for (let i = 0; i < count; i++) {
const el = createSvgEl('circle', {
r: sizeBase + Math.random() * 1.5,
fill: color,
opacity: 0.9
}, mainVorticesGroup);
vortices.push({
el: el,
x: pt.x,
y: pt.y,
// Scatter logic: wider scatter for white noise, concentrated for resonance
vx: (isOverspeed ? 4 : 2) + Math.random() * speedVal * 0.5,
vy: (Math.random() - 0.5) * (isOverspeed ? 1 : 6),
life: 1,
decay: isOverspeed ? 0.015 : 0.04 // White noise dissipates rapidly
});
}
}
function spawnCalloutVortex(x, y, color, isOverspeed) {
const count = isOverspeed ? 1 : 2;
for(let i=0; i<count; i++) {
const el = createSvgEl('circle', {
r: (isOverspeed ? 4 : 1.5) + Math.random(),
fill: color,
opacity: 0.8
}, calloutVorticesGroup);
calloutVortices.push({
el: el,
x: x,
y: y,
vx: 1 + Math.random() * 2,
vy: (Math.random() - 0.5) * 4,
life: 1,
decay: 0.05
});
}
}
function updateVortices(vortexArray, isOverspeed, isCallout = false) {
for (let i = vortexArray.length - 1; i >= 0; i--) {
let v = vortexArray[i];
v.life -= v.decay;
v.x += v.vx;
v.y += v.vy;
// Add swirling motion for visual effect
if(!isOverspeed) {
v.vy += (Math.random() - 0.5) * 0.5;
} else {
// In overspeed, they oscillate creating a distinct frequency visual
v.y += Math.sin(v.x * 0.2) * 2;
}
if (v.life <= 0 || (isCallout && v.x > 1300)) {
v.el.remove();
vortexArray.splice(i, 1);
} else {
v.el.setAttribute('cx', v.x);
v.el.setAttribute('cy', v.y);
v.el.setAttribute('opacity', v.life);
if(isOverspeed) {
v.el.setAttribute('r', (isCallout?4:3) * v.life + 1); // pulse effect
}
}
}
}
// Auto-start
animate();
});
</script>
</body>
</html>
积分规则:第一轮对话扣减8分,后续每轮扣6分
等待动画代码生成...
