以下是一篇适合优快云的技术推广文档,包含Markdown格式和详细的技术说明:
基于HTML5 Canvas的科技感动态物理模拟模型
🌌 项目简介
本系统是一款基于现代Web技术打造的动态物理模拟器,融合了经典力学、计算机图形学和交互设计,为开发者、教育工作者和科技爱好者提供了一个高度可配置的物理实验沙盒。系统采用纯HTML/CSS/JavaScript实现,无需任何第三方库,单文件即可运行!
🚀 核心功能亮点
1. 多维度控制系统
<div class="controls">
边数(N): <input type="number" id="n" value="5" min="3">
粒子数(M): <input type="number" id="m" value="15" min="1">
转速: <input type="number" id="speed" value="0.005" step="0.001">
<select id="dir">...</select>
</div>
- 实时调节容器几何形态(3-N边形)
- 动态增减粒子数量(1-100+)
- 无级变速控制(0.001-0.1 rad/frame)
- 双向旋转模式(顺/逆时针)
2. 高级粒子系统
class EnergyBall {
constructor() {
this.radius = random(8, 20); // 随机尺寸
this.color = `hsl(${random(360)}, 80%, 60%)`; // HSV色彩空间
this.mass = this.radius * 0.5; // 质量关联
}
}
- 自发光的霓虹粒子效果
- 质量-尺寸关联物理模型
- 粒子属性随机生成机制
- 速度限制保护系统(防溢出)
3. 真实物理引擎
// 动量守恒碰撞公式
const impulse = (2 * (dv.x*nx + dv.y*ny)) / (m1+m2) * energyBoost;
v1.x -= impulse * m2 * nx;
v2.x += impulse * m1 * nx;
- 完全弹性碰撞算法
- 重力加速度模拟(可调参数)
- 动态能量衰减系统
- 多层碰撞检测架构
🔧 技术架构解析
物理引擎核心模块
关键技术实现
1. 高效碰撞检测
// 边界碰撞向量计算
const edge = {x: p2.x-p1.x, y: p2.y-p1.y};
const normal = normalize({x: -edge.y, y: edge.x});
const distance = dot(vecToCenter, normal);
if(distance < radius) {
// 碰撞响应...
}
- 使用向量投影进行快速检测
- 预计算法向量提升性能
- 分层检测策略(O(n)复杂度)
2. 动态能量控制
// 速度钳制系统
updateVelocity() {
this.vx = clamp(this.vx, -maxSpeed, maxSpeed);
this.vy = clamp(this.vy, -maxSpeed, maxSpeed);
}
// 自适应能量增益
const boostFactor = energyBoost * (1 - velocity/maxSpeed);
- 三重保护机制防止数值溢出
- 基于系统能量的动态衰减
- 非线性增益曲线设计
3. 渲染优化策略
ctx.shadowColor = particleColor;
ctx.shadowBlur = 15; // 发光效果
ctx.fillStyle = particleColor;
// 残影效果
ctx.fillRect(0, 0, W, H, 'rgba(0,0,0,0.1)');
- 利用Canvas阴影API实现发光
- 半透明覆盖制造运动残影
- 批量渲染优化绘制性能
🎮 应用场景
教育领域
- 经典力学可视化教学
- 动量守恒定律演示
- 离散碰撞检测算法实例
游戏开发
- 物理引擎原型设计
- 粒子系统效果测试
- 特殊技能效果模拟
创意编程
- 生成艺术(Generative Art)
- 交互式数据可视化
- 科技感动态壁纸
💻 快速入门
环境要求
- 现代浏览器(Chrome 85+/Firefox 80+)
- 支持Canvas 2D上下文
- 推荐分辨率:1920×1080
源码
<!DOCTYPE html>
<html>
<head>
<title>科技感动态物理模拟</title>
<style>
body {
background: #000;
color: #0ff;
font-family: 'Courier New', monospace;
display: flex;
flex-direction: column;
align-items: center;
}
canvas {
border: 2px solid #0ff;
box-shadow: 0 0 20px #0ff;
border-radius: 5px;
}
.controls {
margin: 20px 0;
padding: 15px;
background: rgba(0, 50, 50, 0.3);
border: 1px solid #0ff;
border-radius: 5px;
}
input, select, button {
background: #002;
color: #0ff;
border: 1px solid #0ff;
padding: 5px;
margin: 0 10px;
border-radius: 3px;
}
button:hover {
background: #0ff;
color: #002;
cursor: pointer;
box-shadow: 0 0 10px #0ff;
}
.manual {
max-width: 800px;
margin: 20px;
padding: 20px;
background: rgba(0, 30, 30, 0.8);
border: 1px solid #0ff;
border-radius: 5px;
}
</style>
</head>
<body>
<div class="controls">
边数(N): <input type="number" id="n" value="5" min="3">
小球数(M): <input type="number" id="m" value="15" min="1">
速度: <input type="number" id="speed" value="0.005" step="0.001" min="0.001" max="0.1">
方向:
<select id="dir">
<option value="1">顺时针</option>
<option value="-1">逆时针</option>
</select>
<button onclick="initGame()">初始化系统</button>
</div>
<canvas id="gameCanvas"></canvas>
<script>
// 初始化画布
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
canvas.width = 800;
canvas.height = 600;
// 系统参数
const config = {
center: { x: canvas.width/2, y: canvas.height/2 },
radius: 250,
maxBallSize: 20,
minBallSize: 8,
energyBoost: 1.0 // 碰撞能量增强系数
};
// 物理实体存储
let polygons = [];
let balls = [];
/**
* 多边形容器类
*/
class Polygon {
constructor(n) {
this.vertices = [];
this.rotation = 0;
this.update(n);
}
update(n) {
this.vertices = [];
const angleStep = (Math.PI * 2) / n;
for(let i = 0; i < n; i++) {
const angle = angleStep * i + this.rotation;
this.vertices.push({
x: config.center.x + Math.cos(angle) * config.radius,
y: config.center.y + Math.sin(angle) * config.radius
});
}
}
animate() {
const direction = parseInt(document.getElementById('dir').value);
const speed = parseFloat(document.getElementById('speed').value) || 0.005;
this.rotation += direction * speed;
this.update(this.vertices.length);
}
render() {
ctx.beginPath();
ctx.moveTo(this.vertices[0].x, this.vertices[0].y);
this.vertices.forEach(v => ctx.lineTo(v.x, v.y));
ctx.closePath();
ctx.strokeStyle = '#0ff';
ctx.lineWidth = 2;
ctx.stroke();
}
}
/**
* 动态粒子类
*/
class EnergyBall {
constructor() {
// 随机属性
this.radius = Math.random() * (config.maxBallSize - config.minBallSize) + config.minBallSize;
this.color = `hsl(${Math.random() * 360}, 80%, 60%)`;
// 物理属性
this.mass = this.radius * 0.5;
this.position = {
x: config.center.x + (Math.random() - 0.5) * config.radius * 0.8,
y: config.center.y + (Math.random() - 0.5) * config.radius * 0.8
};
this.velocity = {
x: (Math.random() - 0.5) * 4,
y: (Math.random() - 0.5) * 4
};
// 环境参数
this.gravity = 0.15;
this.damping = 0.9;
}
applyPhysics(container) {
// 应用重力
this.velocity.y += this.gravity;
// 更新位置
this.position.x += this.velocity.x;
this.position.y += this.velocity.y;
// 边界碰撞
this.handleContainerCollision(container);
// 粒子交互
this.handleParticleCollision();
}
handleContainerCollision(container) {
container.vertices.forEach((v, i) => {
const next = container.vertices[(i + 1) % container.vertices.length];
// 计算边向量和法线
const edge = { x: next.x - v.x, y: next.y - v.y };
const normal = { x: -edge.y, y: edge.x };
const length = Math.hypot(normal.x, normal.y);
normal.x /= length;
normal.y /= length;
// 距离检测
const vecToPoint = {
x: this.position.x - v.x,
y: this.position.y - v.y
};
const distance = vecToPoint.x * normal.x + vecToPoint.y * normal.y;
if (distance < this.radius) {
// 位置修正
this.position.x -= normal.x * (distance - this.radius);
this.position.y -= normal.y * (distance - this.radius);
// 速度反射(增加能量)
const velocityProjection = this.velocity.x * normal.x + this.velocity.y * normal.y;
this.velocity.x -= (1 + config.energyBoost) * velocityProjection * normal.x;
this.velocity.y -= (1 + config.energyBoost) * velocityProjection * normal.y;
}
});
}
handleParticleCollision() {
balls.forEach(other => {
if (other === this) return;
const dx = other.position.x - this.position.x;
const dy = other.position.y - this.position.y;
const distance = Math.hypot(dx, dy);
const minDistance = this.radius + other.radius;
if (distance < minDistance) {
// 碰撞法线
const nx = dx / distance;
const ny = dy / distance;
// 动量交换(能量增强)
const velocityDiff = {
x: this.velocity.x - other.velocity.x,
y: this.velocity.y - other.velocity.y
};
const impulse = (2 * (velocityDiff.x * nx + velocityDiff.y * ny)) /
(this.mass + other.mass) * config.energyBoost;
// 更新速度
this.velocity.x -= impulse * other.mass * nx;
this.velocity.y -= impulse * other.mass * ny;
other.velocity.x += impulse * this.mass * nx;
other.velocity.y += impulse * this.mass * ny;
// 位置分离
const overlap = (minDistance - distance) / 2;
this.position.x -= overlap * nx;
this.position.y -= overlap * ny;
other.position.x += overlap * nx;
other.position.y += overlap * ny;
}
});
}
render() {
ctx.beginPath();
ctx.arc(this.position.x, this.position.y, this.radius, 0, Math.PI * 2);
ctx.fillStyle = this.color;
// 添加发光效果
ctx.shadowColor = this.color;
ctx.shadowBlur = 15;
ctx.fill();
ctx.shadowBlur = 0; // 重置阴影
}
}
// 初始化系统
function initGame() {
const n = Math.max(3, parseInt(document.getElementById('n').value));
const m = Math.max(1, parseInt(document.getElementById('m').value));
polygons = [new Polygon(n)];
balls = Array.from({ length: m }, () => new EnergyBall());
}
// 主循环
function runSimulation() {
ctx.fillStyle = 'rgba(0, 0, 0, 0.1)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
polygons[0].animate();
polygons[0].render();
balls.forEach(ball => {
ball.applyPhysics(polygons[0]);
ball.render();
});
requestAnimationFrame(runSimulation);
}
// 启动系统
initGame();
runSimulation();
</script>
<!-- 用户手册 -->
<div class="manual">
<h2 style="color: #0ff">系统操作手册</h2>
<h3>▶ 核心功能</h3>
<ul>
<li>🔵 多边形容器动态旋转系统</li>
<li>⚛️ 高能粒子物理模拟引擎</li>
<li>🎨 自发光粒子效果渲染</li>
<li>⚡ 碰撞能量增强机制</li>
</ul>
<h3>▶ 控制面板说明</h3>
<table style="width:100%">
<tr>
<th style="width:25%">控件</th>
<th style="width:50%">功能</th>
<th style="width:25%">参数范围</th>
</tr>
<tr>
<td>边数(N)</td>
<td>设置容器几何结构(3边以上)</td>
<td>≥3</td>
</tr>
<tr>
<td>小球数(M)</td>
<td>控制活跃粒子数量</td>
<td>≥1</td>
</tr>
<tr>
<td>速度</td>
<td>调节容器旋转角速度</td>
<td>0.001-0.1</td>
</tr>
<tr>
<td>方向</td>
<td>切换旋转方向</td>
<td>顺/逆时针</td>
</tr>
</table>
<h3>▶ 物理特性</h3>
<div style="display: grid; grid-template-columns: repeat(2, 1fr); gap: 15px">
<div style="border: 1px solid #0ff; padding: 10px">
<h4>粒子属性</h4>
<p>• 随机尺寸:8-20像素<br>
• 自发光颜色<br>
• 质量与尺寸正比</p>
</div>
<div style="border: 1px solid #0ff; padding: 10px">
<h4>环境参数</h4>
<p>• 重力加速度:0.15px/f²<br>
• 能量增益:1.2x<br>
• 运动阻尼:0.9x</p>
</div>
</div>
<h3>▶ 操作指南</h3>
<ol>
<li>通过数字输入框调整参数</li>
<li>点击"初始化系统"重置场景</li>
<li>实时调整速度/方向参数</li>
<li>观察粒子动态交互效果</li>
</ol>
<h3 style="color: #f0f">⚠️ 注意事项</h3>
<p>• 建议粒子数 ≤ 50(性能优化)<br>
• 高能量系数可能导致速度溢出<br>
• 长时间运行可能出现能量累积</p>
</div>
</body>
</html>
使用指南
- 下载HTML文件
- 用浏览器打开
- 调整控制参数
- 点击"初始化系统"
- 观察粒子运动
参数调优建议
// config.js
const config = {
gravity: 0.15, // 重力加速度
energyBoost: 1.2, // 碰撞能量增益
maxSpeed: 15, // 最大速度限制
dampening: 0.9 // 速度衰减系数
};
🚨 常见问题解答
Q:如何实现更多粒子?
A:修改<input id="m">
值后重新初始化,建议≤100保证性能
Q:碰撞有时不精确?
A:这是离散检测的固有特性,可尝试:
- 减小时间步长
- 增加位置修正系数
- 开启连续碰撞检测
Q:如何导出动画?
A:推荐使用浏览器录屏工具,或添加以下代码:
// 视频采集代码示例
const stream = canvas.captureStream(60);
const recorder = new MediaRecorder(stream);
相关技术
- Matter.js物理引擎
- Three.js三维渲染
- Box2D物理库