Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Particle Collider</title> | |
| <style> | |
| body { | |
| margin: 0; | |
| overflow: hidden; | |
| background: #000; | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| height: 100vh; | |
| } | |
| canvas { | |
| border: 1px solid #333; | |
| } | |
| .controls { | |
| position: fixed; | |
| top: 10px; | |
| left: 10px; | |
| color: white; | |
| } | |
| button { | |
| padding: 5px 10px; | |
| margin: 5px; | |
| cursor: pointer; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="controls"> | |
| <button onclick="addParticles(10)">Add 10 Particles</button> | |
| <button onclick="toggleGravity()">Toggle Gravity</button> | |
| <button onclick="clearParticles()">Clear</button> | |
| </div> | |
| <canvas id="canvas"></canvas> | |
| <script> | |
| const canvas = document.getElementById('canvas'); | |
| const ctx = canvas.getContext('2d'); | |
| // Set canvas size | |
| canvas.width = 800; | |
| canvas.height = 600; | |
| let particles = []; | |
| let gravity = false; | |
| class Particle { | |
| constructor(x, y) { | |
| this.x = x; | |
| this.y = y; | |
| this.radius = Math.random() * 10 + 5; | |
| this.mass = this.radius; | |
| this.vx = (Math.random() - 0.5) * 10; | |
| this.vy = (Math.random() - 0.5) * 10; | |
| this.color = `hsl(${Math.random() * 360}, 50%, 50%)`; | |
| } | |
| draw() { | |
| ctx.beginPath(); | |
| ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2); | |
| ctx.fillStyle = this.color; | |
| ctx.fill(); | |
| ctx.strokeStyle = 'rgba(255, 255, 255, 0.5)'; | |
| ctx.stroke(); | |
| } | |
| update() { | |
| if (gravity) { | |
| this.vy += 0.2; // Gravity effect | |
| } | |
| this.x += this.vx; | |
| this.y += this.vy; | |
| // Bounce off walls | |
| if (this.x - this.radius < 0 || this.x + this.radius > canvas.width) { | |
| this.vx *= -0.9; | |
| this.x = Math.max(this.radius, Math.min(canvas.width - this.radius, this.x)); | |
| } | |
| if (this.y - this.radius < 0 || this.y + this.radius > canvas.height) { | |
| this.vy *= -0.9; | |
| this.y = Math.max(this.radius, Math.min(canvas.height - this.radius, this.y)); | |
| } | |
| } | |
| } | |
| function checkCollision(p1, p2) { | |
| const dx = p2.x - p1.x; | |
| const dy = p2.y - p1.y; | |
| const distance = Math.sqrt(dx * dx + dy * dy); | |
| if (distance < p1.radius + p2.radius) { | |
| // Collision detected - Calculate collision response | |
| const angle = Math.atan2(dy, dx); | |
| const sin = Math.sin(angle); | |
| const cos = Math.cos(angle); | |
| // Rotate velocities | |
| const vx1 = p1.vx * cos + p1.vy * sin; | |
| const vy1 = p1.vy * cos - p1.vx * sin; | |
| const vx2 = p2.vx * cos + p2.vy * sin; | |
| const vy2 = p2.vy * cos - p2.vx * sin; | |
| // Collision elastic equations | |
| const m1 = p1.mass; | |
| const m2 = p2.mass; | |
| const u1 = vx1 * (m1 - m2) / (m1 + m2) + vx2 * 2 * m2 / (m1 + m2); | |
| const u2 = vx2 * (m2 - m1) / (m1 + m2) + vx1 * 2 * m1 / (m1 + m2); | |
| // Rotate velocities back | |
| p1.vx = u1 * cos - vy1 * sin; | |
| p1.vy = vy1 * cos + u1 * sin; | |
| p2.vx = u2 * cos - vy2 * sin; | |
| p2.vy = vy2 * cos + u2 * sin; | |
| // Move particles apart to prevent sticking | |
| const overlap = (p1.radius + p2.radius - distance) / 2; | |
| p1.x -= overlap * cos; | |
| p1.y -= overlap * sin; | |
| p2.x += overlap * cos; | |
| p2.y += overlap * sin; | |
| } | |
| } | |
| function addParticles(count) { | |
| for (let i = 0; i < count; i++) { | |
| particles.push(new Particle( | |
| Math.random() * canvas.width, | |
| Math.random() * canvas.height | |
| )); | |
| } | |
| } | |
| function toggleGravity() { | |
| gravity = !gravity; | |
| } | |
| function clearParticles() { | |
| particles = []; | |
| } | |
| function animate() { | |
| ctx.fillStyle = 'rgba(0, 0, 0, 0.1)'; | |
| ctx.fillRect(0, 0, canvas.width, canvas.height); | |
| // Update and draw particles | |
| particles.forEach(particle => { | |
| particle.update(); | |
| particle.draw(); | |
| }); | |
| // Check collisions | |
| for (let i = 0; i < particles.length; i++) { | |
| for (let j = i + 1; j < particles.length; j++) { | |
| checkCollision(particles[i], particles[j]); | |
| } | |
| } | |
| requestAnimationFrame(animate); | |
| } | |
| // Add initial particles and start animation | |
| addParticles(20); | |
| animate(); | |
| // Mouse interaction | |
| let isMouseDown = false; | |
| canvas.addEventListener('mousedown', () => isMouseDown = true); | |
| canvas.addEventListener('mouseup', () => isMouseDown = false); | |
| canvas.addEventListener('mousemove', (e) => { | |
| if (isMouseDown) { | |
| const rect = canvas.getBoundingClientRect(); | |
| const x = e.clientX - rect.left; | |
| const y = e.clientY - rect.top; | |
| particles.push(new Particle(x, y)); | |
| } | |
| }); | |
| </script> | |
| </body> | |
| </html> |