<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>三体运动模型</title>
<style>
body {
margin: 0;
padding: 20px;
background-color: #000;
color: white;
font-family: Arial, sans-serif;
display: flex;
flex-direction: column;
align-items: center;
}
#canvas-container {
border: 2px solid #333;
margin: 20px 0;
}
canvas {
background-color: #111;
}
button {
padding: 15px 30px;
font-size: 18px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
margin: 10px;
}
button:hover {
background-color: #45a049;
}
button:disabled {
background-color: #666;
cursor: not-allowed;
}
.controls {
margin: 10px 0;
}
label {
margin: 0 10px;
}
</style>
</head>
<body>
<h1>三体运动模型</h1>
<div class="controls">
<button id="startBtn">开始模拟</button>
<button id="resetBtn">重置</button>
<label>
<input type="checkbox" id="trailCheckbox" checked> 显示轨迹
</label>
</div>
<div id="canvas-container">
<canvas id="simulationCanvas" width="800" height="600"></canvas>
</div>
<div id="info"></div>
<script>
// 获取canvas元素和上下文
const canvas = document.getElementById('simulationCanvas');
const ctx = canvas.getContext('2d');
const startBtn = document.getElementById('startBtn');
const resetBtn = document.getElementById('resetBtn');
const trailCheckbox = document.getElementById('trailCheckbox');
const infoDiv = document.getElementById('info');
// 物理常数
const G = 0.5; // 引力常数(调整以适应视觉效果)
const TIME_STEP = 0.1; // 时间步长
// 天体类
class Body {
constructor(x, y, vx, vy, mass, radius, color) {
this.x = x;
this.y = y;
this.vx = vx;
this.vy = vy;
this.mass = mass;
this.radius = radius;
this.color = color;
this.trail = []; // 轨迹点
this.maxTrailLength = 200; // 最大轨迹长度
}
// 计算与其他天体的引力
calculateForce(other) {
const dx = other.x - this.x;
const dy = other.y - this.y;
const distanceSq = dx * dx + dy * dy;
const distance = Math.sqrt(distanceSq);
// 避免距离过近导致的数值不稳定
if (distance < this.radius + other.radius) {
return { fx: 0, fy: 0 };
}
const force = G * this.mass * other.mass / distanceSq;
const forceX = force * dx / distance;
const forceY = force * dy / distance;
return { fx: forceX, fy: forceY };
}
// 更新位置和速度
update(forces) {
// 计算加速度
const ax = forces.fx / this.mass;
const ay = forces.fy / this.mass;
// 更新速度
this.vx += ax * TIME_STEP;
this.vy += ay * TIME_STEP;
// 更新位置
this.x += this.vx * TIME_STEP;
this.y += this.vy * TIME_STEP;
// 添加轨迹点
this.trail.push({ x: this.x, y: this.y });
if (this.trail.length > this.maxTrailLength) {
this.trail.shift();
}
}
// 绘制天体
draw() {
// 绘制轨迹
if (trailCheckbox.checked && this.trail.length > 1) {
ctx.beginPath();
ctx.moveTo(this.trail[0].x, this.trail[0].y);
for (let i = 1; i < this.trail.length; i++) {
ctx.lineTo(this.trail[i].x, this.trail[i].y);
}
ctx.strokeStyle = this.color + '40'; // 半透明颜色
ctx.lineWidth = 1;
ctx.stroke();
}
// 绘制天体
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
ctx.fillStyle = this.color;
ctx.fill();
// 添加高光效果
ctx.beginPath();
ctx.arc(this.x - this.radius/3, this.y - this.radius/3, this.radius/3, 0, Math.PI * 2);
ctx.fillStyle = 'rgba(255, 255, 255, 0.3)';
ctx.fill();
}
}
// 三体系统类
class ThreeBodySystem {
constructor() {
this.bodies = [];
this.isRunning = false;
this.animationId = null;
}
// 初始化三个天体
initialize() {
this.bodies = [];
// 创建三个随机天体
for (let i = 0; i < 3; i++) {
const radius = Math.random() * 15 + 10; // 10-25像素
const mass = radius * radius * 0.1; // 质量与半径平方成正比
// 随机位置(避免过于靠近边界)
const x = Math.random() * (canvas.width - 100) + 50;
const y = Math.random() * (canvas.height - 100) + 50;
// 随机初始速度
const vx = (Math.random() - 0.5) * 2;
const vy = (Math.random() - 0.5) * 2;
// 随机颜色
const colors = ['#FF6B6B', '#4ECDC4', '#45B7D1'];
this.bodies.push(new Body(x, y, vx, vy, mass, radius, colors[i]));
}
}
// 计算所有天体之间的引力
calculateForces() {
const forces = Array(this.bodies.length).fill().map(() => ({ fx: 0, fy: 0 }));
for (let i = 0; i < this.bodies.length; i++) {
for (let j = i + 1; j < this.bodies.length; j++) {
const force = this.bodies[i].calculateForce(this.bodies[j]);
forces[i].fx += force.fx;
forces[i].fy += force.fy;
forces[j].fx -= force.fx;
forces[j].fy -= force.fy;
}
}
return forces;
}
// 更新系统状态
update() {
const forces = this.calculateForces();
for (let i = 0; i < this.bodies.length; i++) {
this.bodies[i].update(forces[i]);
}
}
// 绘制系统
draw() {
// 清空画布(如果显示轨迹则使用半透明清空创建淡出效果)
if (trailCheckbox.checked) {
ctx.fillStyle = 'rgba(17, 17, 17, 0.1)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
} else {
ctx.clearRect(0, 0, canvas.width, canvas.height);
}
// 绘制所有天体
this.bodies.forEach(body => body.draw());
}
// 运行动画循环
animate() {
if (!this.isRunning) return;
this.update();
this.draw();
this.animationId = requestAnimationFrame(() => this.animate());
}
// 开始模拟
start() {
if (this.isRunning) return;
this.isRunning = true;
startBtn.disabled = true;
this.animate();
}
// 停止模拟
stop() {
this.isRunning = false;
startBtn.disabled = false;
if (this.animationId) {
cancelAnimationFrame(this.animationId);
}
}
// 重置系统
reset() {
this.stop();
this.initialize();
this.draw();
}
}
// 创建三体系统实例
const system = new ThreeBodySystem();
// 初始化画布
function init() {
system.initialize();
system.draw();
}
// 事件监听器
startBtn.addEventListener('click', () => {
system.start();
});
resetBtn.addEventListener('click', () => {
system.reset();
});
// 键盘控制
document.addEventListener('keydown', (event) => {
if (event.code === 'Space') {
event.preventDefault();
if (system.isRunning) {
system.stop();
} else {
system.start();
}
}
});
// 初始化
init();
// 显示操作说明
infoDiv.innerHTML = `
<p>操作说明:</p>
<ul>
<li>点击"开始模拟"按钮开始三体运动</li>
<li>点击"重置"按钮重新生成随机天体</li>
<li>按空格键可以暂停/继续模拟</li>
<li>勾选"显示轨迹"可以查看运动轨迹</li>
</ul>
<p>物理说明:基于牛顿万有引力定律的三体问题数值模拟</p>
`;
</script>
</body>
</html>
index.html
index.html