| class ParticleSimulator { |
| constructor(canvasId) { |
| this.canvas = document.getElementById(canvasId); |
| this.particles = []; |
| this.isRunning = false; |
| this.animationId = null; |
| |
| this.init(); |
| } |
|
|
| init() { |
| this.createParticles(25); |
| this.start(); |
| |
| |
| window.addEventListener('resize', () => { |
| this.handleResize(); |
| }); |
| } |
|
|
| createParticles(count) { |
| this.particles = []; |
| const colors = [ |
| 'rgba(14, 165, 233, 0.8)', |
| 'rgba(100, 116, 139, 0.8)', |
| 'rgba(2, 132, 199, 0.8)', |
| 'rgba(71, 85, 105, 0.8)' |
| ]; |
|
|
| for (let i = 0; i < count; i++) { |
| const size = Math.random() * 8 + 4; |
| const particle = { |
| x: Math.random() * this.canvas.offsetWidth, |
| y: Math.random() * this.canvas.offsetHeight, |
| size: size, |
| speedX: (Math.random() - 0.5) * 3, |
| speedY: (Math.random() - 0.5) * 3, |
| color: colors[Math.floor(Math.random() * colors.length)], |
| trail: [] |
| }; |
| this.particles.push(particle); |
| } |
| } |
|
|
| start() { |
| this.isRunning = true; |
| this.animate(); |
| } |
|
|
| stop() { |
| this.isRunning = false; |
| if (this.animationId) { |
| cancelAnimationFrame(this.animationId); |
| } |
| } |
|
|
| reset() { |
| this.stop(); |
| this.particles = []; |
| this.createParticles(25); |
| this.start(); |
| } |
|
|
| animate() { |
| if (!this.isRunning) return; |
|
|
| |
| this.canvas.innerHTML = ''; |
|
|
| |
| this.particles.forEach(particle => { |
| |
| particle.x += particle.speedX; |
| particle.y += particle.speedY; |
|
|
| |
| if (particle.x <= 0 || particle.x >= this.canvas.offsetWidth) { |
| particle.speedX *= -1; |
| } |
| if (particle.y <= 0 || particle.y >= this.canvas.offsetHeight) { |
| particle.speedY *= -1; |
| } |
|
|
| |
| particle.trail.push({ x: particle.x, y: particle.y }); |
| if (particle.trail.length > 10) { |
| particle.trail.shift(); |
| } |
|
|
| |
| particle.trail.forEach((trailPos, index) => { |
| const trailElement = document.createElement('div'); |
| trailElement.className = 'particle-trail'; |
| trailElement.style.width = `${particle.size * (index / particle.trail.length)}px`; |
| trailElement.style.height = `${particle.size * (index / particle.trail.length)}px`; |
| trailElement.style.left = `${trailPos.x}px`; |
| trailElement.style.top = `${trailPos.y}px`; |
| trailElement.style.backgroundColor = particle.color.replace('0.8', `${0.3 * (index / particle.trail.length)}`); |
| this.canvas.appendChild(trailElement); |
| }); |
|
|
| |
| const particleElement = document.createElement('div'); |
| particleElement.className = 'particle'; |
| particleElement.style.width = `${particle.size}px`; |
| particleElement.style.height = `${particle.size}px`; |
| particleElement.style.left = `${particle.x}px`; |
| particleElement.style.top = `${particle.y}px`; |
| particleElement.style.backgroundColor = particle.color; |
| particleElement.style.boxShadow = `0 0 ${particle.size * 2}px ${particle.color}`; |
| this.canvas.appendChild(particleElement); |
| }); |
|
|
| this.animationId = requestAnimationFrame(() => this.animate()); |
| } |
|
|
| handleResize() { |
| this.stop(); |
| this.start(); |
| } |
|
|
| updateParticleCount(count) { |
| this.stop(); |
| this.createParticles(count); |
| this.start(); |
| } |
| } |
|
|
| |
| document.addEventListener('DOMContentLoaded', () => { |
| const simulator = new ParticleSimulator('simulationCanvas'); |
|
|
| |
| document.querySelector('button:nth-child(1)').addEventListener('click', () => { |
| simulator.start(); |
| }); |
|
|
| document.querySelector('button:nth-child(2)').addEventListener('click', () => { |
| simulator.stop(); |
| }); |
|
|
| document.querySelector('button:nth-child(3)').addEventListener('click', () => { |
| simulator.reset(); |
| }); |
|
|
| |
| const sliders = document.querySelectorAll('input[type="range"]'); |
| sliders[0].addEventListener('input', (e) => { |
| simulator.updateParticleCount(parseInt(e.target.value)); |
| }); |
| }); |