独立渲染引擎就绪引擎就绪
<!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: #080c16;
--grid-color: rgba(0, 240, 255, 0.1);
--text-main: #00f0ff;
--text-muted: #6b8299;
--accent-orange: #ffaa00;
--accent-blue: #0088ff;
--accent-green: #00ffaa;
--panel-bg: rgba(8, 12, 22, 0.85);
--border-color: rgba(0, 240, 255, 0.3);
}
body {
margin: 0;
padding: 0;
background-color: var(--bg-color);
color: var(--text-main);
font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
overflow: hidden;
width: 100vw;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
/* 绝对定位的UI面板,避开中心动画区域 */
.info-panel {
position: absolute;
background: var(--panel-bg);
border: 1px solid var(--border-color);
border-radius: 6px;
padding: 12px 16px;
backdrop-filter: blur(4px);
z-index: 10;
max-width: 300px;
pointer-events: none; /* 让信息面板不阻挡鼠标事件 */
}
.top-left { top: 20px; left: 20px; }
.bottom-left { bottom: 20px; left: 20px; }
.top-right { top: 20px; right: 20px; pointer-events: auto; /* 控件面板需要交互 */ }
.title {
font-size: 14px;
font-weight: 600;
margin: 0 0 8px 0;
letter-spacing: 1px;
color: #fff;
text-transform: uppercase;
}
.desc {
font-size: 12px;
color: var(--text-muted);
line-height: 1.5;
margin: 0;
}
.legend-item {
display: flex;
align-items: center;
font-size: 11px;
margin-top: 6px;
color: var(--text-muted);
}
.legend-color {
width: 10px;
height: 10px;
border-radius: 2px;
margin-right: 8px;
}
/* 交互控件样式 */
.control-group {
margin-top: 10px;
display: flex;
flex-direction: column;
gap: 6px;
}
.control-header {
display: flex;
justify-content: space-between;
font-size: 12px;
color: #fff;
}
.value-display {
color: var(--accent-orange);
font-family: 'Courier New', monospace;
font-weight: bold;
}
input[type=range] {
-webkit-appearance: none;
width: 100%;
background: transparent;
cursor: pointer;
}
input[type=range]::-webkit-slider-runnable-track {
width: 100%;
height: 4px;
background: rgba(0, 240, 255, 0.2);
border-radius: 2px;
}
input[type=range]::-webkit-slider-thumb {
-webkit-appearance: none;
height: 16px;
width: 16px;
border-radius: 50%;
background: var(--accent-orange);
margin-top: -6px;
box-shadow: 0 0 8px var(--accent-orange);
transition: transform 0.1s;
}
input[type=range]::-webkit-slider-thumb:hover {
transform: scale(1.2);
}
/* SVG 容器 */
#animation-container {
width: 100%;
height: 100%;
max-width: 1200px;
max-height: 800px;
}
svg {
width: 100%;
height: 100%;
display: block;
}
</style>
</head>
<body>
<!-- 顶部左侧信息栏 -->
<div class="info-panel top-left">
<h1 class="title">变向角激振系统 | IFR 模型</h1>
<p class="desc">
理想最终解:无需改变系统整体架构,仅通过调节偏心块相位差,即可动态重构合成激振力方向。实现排浆速度与岩屑抛射效果的自适应平衡。
</p>
</div>
<!-- 底部左侧图例 -->
<div class="info-panel bottom-left">
<h2 class="title" style="font-size: 12px;">图例说明</h2>
<div class="legend-item">
<div class="legend-color" style="background: var(--accent-blue);"></div>
<span>单轴激振力向量</span>
</div>
<div class="legend-item">
<div class="legend-color" style="background: var(--accent-orange); box-shadow: 0 0 5px var(--accent-orange);"></div>
<span>合成激振力 (方向角可调)</span>
</div>
<div class="legend-item">
<div class="legend-color" style="background: var(--accent-green);"></div>
<span>物料抛射轨迹</span>
</div>
</div>
<!-- 顶部右侧控制台 -->
<div class="info-panel top-right">
<h2 class="title">动态参数控制</h2>
<p class="desc" style="margin-bottom: 12px;">通过调节相位改变宏观系统属性</p>
<div class="control-group">
<div class="control-header">
<span>振动方向角 (α)</span>
<span class="value-display" id="angle-val">45°</span>
</div>
<input type="range" id="angle-slider" min="45" max="60" value="45" step="1">
</div>
<div class="control-group" style="margin-top: 16px;">
<div class="control-header">
<span>激振频率</span>
<span class="value-display" style="color: var(--text-main);">1000 r/min</span>
</div>
<!-- 视觉演示,频率固定,不提供滑块以保持交互焦点 -->
</div>
</div>
<!-- 核心 SVG 动画区域 -->
<div id="animation-container">
<svg viewBox="0 0 1000 600" preserveAspectRatio="xMidYMid meet">
<defs>
<!-- 发光滤镜 -->
<filter id="glow" x="-20%" y="-20%" width="140%" height="140%">
<feGaussianBlur stdDeviation="4" result="blur" />
<feComposite in="SourceGraphic" in2="blur" operator="over" />
</filter>
<filter id="glow-strong" x="-30%" y="-30%" width="160%" height="160%">
<feGaussianBlur stdDeviation="8" result="blur" />
<feMerge>
<feMergeNode in="blur"/>
<feMergeNode in="blur"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
<!-- 箭头标记 -->
<marker id="arrow-blue" 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="var(--accent-blue)" />
</marker>
<marker id="arrow-orange" 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="var(--accent-orange)" />
</marker>
<!-- 网格背景图案 -->
<pattern id="grid" width="40" height="40" patternUnits="userSpaceOnUse">
<path d="M 40 0 L 0 0 0 40" fill="none" stroke="var(--grid-color)" stroke-width="0.5" />
<circle cx="0" cy="0" r="1" fill="var(--grid-color)" />
</pattern>
<!-- 筛网纹理 -->
<pattern id="screen-mesh" width="10" height="10" patternUnits="userSpaceOnUse" patternTransform="rotate(15)">
<path d="M 10 0 L 0 0 0 10" fill="none" stroke="rgba(0, 240, 255, 0.3)" stroke-width="1" />
</pattern>
</defs>
<!-- 1. 背景层 -->
<rect width="100%" height="100%" fill="url(#grid)" />
<!-- 坐标系参考线 -->
<g stroke="rgba(0, 240, 255, 0.2)" stroke-width="1" stroke-dasharray="4 4">
<line x1="100" y1="350" x2="900" y2="350" />
<line x1="500" y1="100" x2="500" y2="500" />
</g>
<!-- 2. 筛机结构层 -->
<!-- 筛网床面 (倾斜展示排料) -->
<g transform="translate(0, 0)">
<path d="M 150 480 L 850 450 L 850 465 L 150 495 Z" fill="rgba(0, 100, 255, 0.1)" stroke="var(--text-main)" stroke-width="2" />
<path d="M 150 480 L 850 450 L 850 465 L 150 495 Z" fill="url(#screen-mesh)" />
<line x1="150" y1="480" x2="850" y2="450" stroke="var(--text-main)" stroke-width="3" filter="url(#glow)" />
</g>
<!-- 3. 物料/流体动力学层 (通过JS动态生成) -->
<g id="particles-group" filter="url(#glow)"></g>
<g id="trails-group" opacity="0.6"></g>
<!-- 4. 激振器核心机构层 -->
<g id="exciter-assembly" transform="translate(500, 350)">
<!-- 机构外壳轮廓 -->
<rect x="-120" y="-60" width="240" height="120" rx="15" fill="rgba(8, 12, 22, 0.9)" stroke="var(--text-muted)" stroke-width="2" />
<path d="M -100 -40 L -100 40 M 100 -40 L 100 40" stroke="var(--border-color)" stroke-width="1" stroke-dasharray="2 2" />
<!-- 左轴 (顺时针旋转) -->
<g id="shaft-left" transform="translate(-50, 0)">
<circle r="35" fill="none" stroke="var(--border-color)" stroke-width="1" stroke-dasharray="4 2" />
<!-- 偏心块 -->
<path id="mass-left" d="M 0 -30 A 30 30 0 0 1 0 30 L 0 0 Z" fill="var(--text-muted)" opacity="0.7" />
<circle r="6" fill="var(--text-main)" filter="url(#glow)" />
<!-- 单轴力向量 -->
<line id="vec-left" x1="0" y1="0" x2="0" y2="-60" stroke="var(--accent-blue)" stroke-width="2" marker-end="url(#arrow-blue)" opacity="0.6" />
</g>
<!-- 右轴 (逆时针旋转) -->
<g id="shaft-right" transform="translate(50, 0)">
<circle r="35" fill="none" stroke="var(--border-color)" stroke-width="1" stroke-dasharray="4 2" />
<!-- 偏心块 -->
<path id="mass-right" d="M 0 -30 A 30 30 0 0 0 0 30 L 0 0 Z" fill="var(--text-muted)" opacity="0.7" />
<circle r="6" fill="var(--text-main)" filter="url(#glow)" />
<!-- 单轴力向量 -->
<line id="vec-right" x1="0" y1="0" x2="0" y2="-60" stroke="var(--accent-blue)" stroke-width="2" marker-end="url(#arrow-blue)" opacity="0.6" />
</g>
<!-- 合成力向量基准线 (展示当前设定的方向角) -->
<line id="angle-guideline" x1="-150" y1="150" x2="150" y2="-150" stroke="rgba(255, 170, 0, 0.3)" stroke-width="1" stroke-dasharray="5 5" />
<!-- 合成力向量 (动态伸缩) -->
<line id="vec-resultant" x1="0" y1="0" x2="120" y2="-120" stroke="var(--accent-orange)" stroke-width="4" marker-end="url(#arrow-orange)" filter="url(#glow-strong)" />
<!-- 中心参考点 -->
<circle cx="0" cy="0" r="3" fill="var(--accent-orange)" />
</g>
<!-- 角度标注弧线 -->
<g transform="translate(500, 350)">
<path id="angle-arc" d="M 80 0 A 80 80 0 0 0 56.5 -56.5" fill="none" stroke="var(--accent-orange)" stroke-width="1.5" opacity="0.8" />
<text id="angle-text-svg" x="90" y="-30" fill="var(--accent-orange)" font-size="12" font-family="monospace">45°</text>
</g>
</svg>
</div>
<script>
/**
* 物理与渲染引擎
*/
const slider = document.getElementById('angle-slider');
const angleValDisplay = document.getElementById('angle-val');
// SVG 元素引用
const massLeft = document.getElementById('mass-left');
const massRight = document.getElementById('mass-right');
const vecLeft = document.getElementById('vec-left');
const vecRight = document.getElementById('vec-right');
const vecResultant = document.getElementById('vec-resultant');
const angleGuideline = document.getElementById('angle-guideline');
const angleArc = document.getElementById('angle-arc');
const angleTextSvg = document.getElementById('angle-text-svg');
const particlesGroup = document.getElementById('particles-group');
const trailsGroup = document.getElementById('trails-group');
// 系统状态
let targetAngleDeg = parseFloat(slider.value); // 目标角度 (45-60)
let currentAngleDeg = targetAngleDeg; // 当前平滑过渡的角度
let time = 0; // 时间变量,驱动旋转
const omega = 0.15; // 角速度 (模拟频率)
// 几何常量
const R = 30; // 偏心块等效半径 (用于渲染)
const F_max = 65; // 单轴力向量渲染最大长度
const deckY = (x) => 480 - ((480 - 450) / (850 - 150)) * (x - 150); // 筛面方程 y = f(x)
const deckAngle = Math.atan2(450 - 480, 850 - 150); // 筛面倾角 (-0.04 rad)
// 粒子系统
const particles = [];
const MAX_PARTICLES = 35;
let frameCount = 0;
class Particle {
constructor() {
this.reset(true);
}
reset(initial = false) {
// 起点在筛面左侧随机位置
this.x = 160 + Math.random() * 50;
this.y = deckY(this.x);
// 速度由当前的合成力方向(抛射角)决定
// 角度越高(60°),Y分量越大,X分量越小
const throwForce = 8 + Math.random() * 2;
const rad = currentAngleDeg * Math.PI / 180;
this.vx = throwForce * Math.cos(rad);
this.vy = -throwForce * Math.sin(rad); // SVG Y轴向下
this.history = [];
this.active = !initial || Math.random() > 0.5; // 错开初始发射时间
this.color = Math.random() > 0.3 ? 'var(--accent-green)' : 'rgba(255, 255, 255, 0.7)';
this.size = 1.5 + Math.random() * 2;
if(!this.element) {
this.element = document.createElementNS("http://www.w3.org/2000/svg", "circle");
this.element.setAttribute("r", this.size);
this.element.setAttribute("fill", this.color);
particlesGroup.appendChild(this.element);
this.trail = document.createElementNS("http://www.w3.org/2000/svg", "polyline");
this.trail.setAttribute("fill", "none");
this.trail.setAttribute("stroke", this.color);
this.trail.setAttribute("stroke-width", "1");
this.trail.setAttribute("opacity", "0.5");
trailsGroup.appendChild(this.trail);
}
}
update() {
if (!this.active) return;
this.history.push({x: this.x, y: this.y});
if (this.history.length > 15) this.history.shift();
// 物理运动: 抛物线
this.x += this.vx;
this.y += this.vy;
this.vy += 0.35; // 重力加速度
const currentDeckY = deckY(this.x);
// 碰撞检测 (落回筛面)
if (this.y >= currentDeckY) {
this.y = currentDeckY;
// 模拟在筛面上受到新的激振力抛射
// 如果超过右边界,重置
if (this.x > 820) {
this.reset();
} else {
// 继续向前弹跳,弹跳角度跟随当前设定角
const throwForce = 7 + Math.random() * 2;
const rad = currentAngleDeg * Math.PI / 180;
this.vx = throwForce * Math.cos(rad);
this.vy = -throwForce * Math.sin(rad);
}
}
this.draw();
}
draw() {
this.element.setAttribute("cx", this.x);
this.element.setAttribute("cy", this.y);
if (this.history.length > 1) {
const points = this.history.map(p => `${p.x},${p.y}`).join(" ");
this.trail.setAttribute("points", points);
}
}
}
// 初始化粒子
for (let i = 0; i < MAX_PARTICLES; i++) {
particles.push(new Particle());
}
// 事件监听
slider.addEventListener('input', (e) => {
targetAngleDeg = parseFloat(e.target.value);
angleValDisplay.textContent = targetAngleDeg + '°';
});
/**
* 核心动画循环
*/
function animate() {
time += omega;
// 1. 平滑过渡角度 (视觉缓冲效果)
currentAngleDeg += (targetAngleDeg - currentAngleDeg) * 0.1;
const alpha = currentAngleDeg * Math.PI / 180; // 目标方向角弧度
// 2. IFR 核心机制学计算:
// 为了使合力方向恒定为 alpha,两偏心块需满足特定相位关系。
// 左轴(顺时针): theta1 = time + alpha
// 右轴(逆时针): theta2 = -time + alpha
// 这样在 time=0, pi, 2pi... 时,两者角度均为 alpha,合力最大且指向 alpha。
const theta1 = time + alpha;
const theta2 = -time + alpha;
// 更新偏心块旋转
const deg1 = theta1 * 180 / Math.PI;
const deg2 = theta2 * 180 / Math.PI;
massLeft.setAttribute('transform', `rotate(${deg1})`);
massRight.setAttribute('transform', `rotate(${deg2})`);
// 3. 计算并绘制单轴力向量 (力方向与偏心块质心方向一致)
const f1_x = F_max * Math.cos(theta1);
const f1_y = -F_max * Math.sin(theta1); // SVG Y反向
vecLeft.setAttribute('x2', f1_x);
vecLeft.setAttribute('y2', f1_y);
const f2_x = F_max * Math.cos(theta2);
const f2_y = -F_max * Math.sin(theta2);
vecRight.setAttribute('x2', f2_x);
vecRight.setAttribute('y2', f2_y);
// 4. 计算并绘制合成力向量 (两单轴力之和)
const res_x = f1_x + f2_x;
const res_y = f1_y + f2_y;
vecResultant.setAttribute('x2', res_x);
vecResultant.setAttribute('y2', res_y);
// 5. 更新辅助线和角度标注
// 辅助线长度固定,方向为当前 alpha
const guide_L = 150;
angleGuideline.setAttribute('x1', -guide_L * Math.cos(alpha));
angleGuideline.setAttribute('y1', guide_L * Math.sin(alpha));
angleGuideline.setAttribute('x2', guide_L * Math.cos(alpha));
angleGuideline.setAttribute('y2', -guide_L * Math.sin(alpha));
// 绘制角度圆弧 (从0度到alpha度)
const arcR = 80;
const arcX = arcR * Math.cos(alpha);
const arcY = -arcR * Math.sin(alpha);
angleArc.setAttribute('d', `M ${arcR} 0 A ${arcR} ${arcR} 0 0 0 ${arcX} ${arcY}`);
angleTextSvg.textContent = Math.round(currentAngleDeg) + '°';
angleTextSvg.setAttribute('x', arcX + 10);
angleTextSvg.setAttribute('y', arcY - 10);
// 6. 更新粒子系统 (物料轨迹)
frameCount++;
if (frameCount % 5 === 0) {
// 周期性激活休眠的粒子,形成连续流
const inactive = particles.find(p => !p.active);
if (inactive) inactive.active = true;
}
particles.forEach(p => p.update());
requestAnimationFrame(animate);
}
// 启动动画 (DOMContentLoaded/Load后自动启动,符合要求)
requestAnimationFrame(animate);
</script>
</body>
</html>
积分规则:每轮对话扣10分
等待动画代码生成...
