Spaces:
Running
Running
| document.addEventListener('DOMContentLoaded', () => { | |
| const canvas = document.createElement('canvas'); | |
| const background = document.getElementById('animated-background'); | |
| background.appendChild(canvas); | |
| canvas.style.position = 'absolute'; | |
| canvas.style.top = '0'; | |
| canvas.style.left = '0'; | |
| canvas.style.width = '100%'; | |
| canvas.style.height = '100%'; | |
| // Resize canvas to full window | |
| function resizeCanvas() { | |
| canvas.width = window.innerWidth; | |
| canvas.height = window.innerHeight; | |
| } | |
| window.addEventListener('resize', resizeCanvas); | |
| resizeCanvas(); | |
| const ctx = canvas.getContext('2d'); | |
| // Particle system | |
| let particles = []; | |
| let particleCount = 150; | |
| let speed = 5; | |
| let size = 5; | |
| let effectType = 'cosmic'; | |
| // Create particles | |
| function createParticles() { | |
| particles = []; | |
| for (let i = 0; i < particleCount; i++) { | |
| particles.push({ | |
| x: Math.random() * canvas.width, | |
| y: Math.random() * canvas.height, | |
| size: Math.random() * size + 1, | |
| speedX: (Math.random() - 0.5) * speed, | |
| speedY: (Math.random() - 0.5) * speed, | |
| color: `hsl(${Math.random() * 360}, 70%, 60%)`, | |
| angle: Math.random() * Math.PI * 2, | |
| angleSpeed: (Math.random() - 0.5) * 0.02 | |
| }); | |
| } | |
| } | |
| // Draw particles | |
| function drawParticles() { | |
| ctx.clearRect(0, 0, canvas.width, canvas.height); | |
| particles.forEach(p => { | |
| // Update position | |
| p.x += p.speedX; | |
| p.y += p.speedY; | |
| p.angle += p.angleSpeed; | |
| // Wrap around edges | |
| if (p.x < 0) p.x = canvas.width; | |
| if (p.x > canvas.width) p.x = 0; | |
| if (p.y < 0) p.y = canvas.height; | |
| if (p.y > canvas.height) p.y = 0; | |
| // Draw based on effect type | |
| if (effectType === 'cosmic') { | |
| drawCosmicParticle(p); | |
| } else if (effectType === 'bubbles') { | |
| drawBubbleParticle(p); | |
| } else if (effectType === 'stars') { | |
| drawStarParticle(p); | |
| } | |
| }); | |
| requestAnimationFrame(drawParticles); | |
| } | |
| function drawCosmicParticle(p) { | |
| ctx.save(); | |
| ctx.translate(p.x, p.y); | |
| ctx.rotate(p.angle); | |
| // Glow effect | |
| const gradient = ctx.createRadialGradient(0, 0, 0, 0, 0, p.size * 2); | |
| gradient.addColorStop(0, p.color); | |
| gradient.addColorStop(1, 'transparent'); | |
| ctx.fillStyle = gradient; | |
| ctx.beginPath(); | |
| ctx.arc(0, 0, p.size * 2, 0, Math.PI * 2); | |
| ctx.fill(); | |
| // Core | |
| ctx.fillStyle = p.color; | |
| ctx.beginPath(); | |
| ctx.arc(0, 0, p.size / 2, 0, Math.PI * 2); | |
| ctx.fill(); | |
| ctx.restore(); | |
| } | |
| function drawBubbleParticle(p) { | |
| ctx.fillStyle = `rgba(255, 255, 255, ${p.size / 20})`; | |
| ctx.beginPath(); | |
| ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2); | |
| ctx.fill(); | |
| ctx.strokeStyle = `rgba(255, 255, 255, 0.3)`; | |
| ctx.stroke(); | |
| } | |
| function drawStarParticle(p) { | |
| const spikes = 5; | |
| const outerRadius = p.size; | |
| const innerRadius = p.size / 2; | |
| ctx.save(); | |
| ctx.translate(p.x, p.y); | |
| ctx.rotate(p.angle); | |
| ctx.fillStyle = p.color; | |
| ctx.beginPath(); | |
| for (let i = 0; i < spikes * 2; i++) { | |
| const radius = i % 2 === 0 ? outerRadius : innerRadius; | |
| const angle = (i * Math.PI) / spikes; | |
| ctx.lineTo(Math.cos(angle) * radius, Math.sin(angle) * radius); | |
| } | |
| ctx.closePath(); | |
| ctx.fill(); | |
| // Glow | |
| ctx.shadowColor = p.color; | |
| ctx.shadowBlur = 10; | |
| ctx.fill(); | |
| ctx.restore(); | |
| } | |
| // Change effect type | |
| document.getElementById('change-effect').addEventListener('click', () => { | |
| const effects = ['cosmic', 'bubbles', 'stars']; | |
| const currentIndex = effects.indexOf(effectType); | |
| effectType = effects[(currentIndex + 1) % effects.length]; | |
| createParticles(); | |
| }); | |
| // Toggle controls panel | |
| const controlsPanel = document.getElementById('controls-panel'); | |
| document.getElementById('toggle-controls').addEventListener('click', () => { | |
| controlsPanel.style.transform = controlsPanel.style.transform === 'translateY(0px)' ? | |
| 'translateY(100%)' : 'translateY(0)'; | |
| }); | |
| // Update settings from controls | |
| document.getElementById('particle-count').addEventListener('input', (e) => { | |
| particleCount = parseInt(e.target.value); | |
| createParticles(); | |
| }); | |
| document.getElementById('speed').addEventListener('input', (e) => { | |
| speed = parseInt(e.target.value); | |
| particles.forEach(p => { | |
| p.speedX = (Math.random() - 0.5) * speed; | |
| p.speedY = (Math.random() - 0.5) * speed; | |
| }); | |
| }); | |
| document.getElementById('size').addEventListener('input', (e) => { | |
| size = parseInt(e.target.value); | |
| particles.forEach(p => { | |
| p.size = Math.random() * size + 1; | |
| }); | |
| }); | |
| // Initialize | |
| createParticles(); | |
| drawParticles(); | |
| }); |