小球引力模拟edit icon

作者:
邓朝元
Fork(复制)
下载
嵌入
BUG反馈
index.html
现在支持上传本地图片了!
index.html
            
            <!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>
        
编辑器加载中
预览
控制台