animation = {
const context = canvas.getContext("2d");
let frame = 0;
const particles = Array.from({length: numParticles}, () => {
const angle = Math.random() * Math.PI * 2;
const speed = Math.random() * 5;
return {
x: Math.random() * canvas.width,
y: Math.random() * canvas.height,
vx: Math.cos(angle) * speed,
vy: Math.sin(angle) * speed
};
});
yield* Generators.observe(function(notify) {
const centerX = canvas.width / 2;
const centerY = canvas.height / 2;
const update = () => {
particles.forEach(p => {
p.x += p.vx * particleSpeed;
p.y += p.vy * particleSpeed;
const dx = centerX - p.x;
const dy = centerY - p.y;
const distance = Math.sqrt(dx * dx + dy * dy);
const attraction = distance ? attractionForce / distance : 0;
p.vx += dx * attraction;
p.vy += dy * attraction;
p.vx += (Math.random() - 0.5) * noiseInfluence;
p.vy += (Math.random() - 0.5) * noiseInfluence;
p.x += p.vx;
p.y += p.vy;
if (p.x > canvas.width) p.x = 0;
if (p.y > canvas.height) p.y = 0;
if (p.x < 0) p.x = canvas.width;
if (p.y < 0) p.y = canvas.height;
});
context.clearRect(0, 0, canvas.width, canvas.height);
particles.forEach(p => {
context.beginPath();
context.arc(p.x, p.y, 2, 0, 2 * Math.PI);
context.fill();
});
notify(canvas);
};
(function animate() {
update();
frame = requestAnimationFrame(animate);
})();
return () => cancelAnimationFrame(frame);
});
}