Skip to content

Instantly share code, notes, and snippets.

@chrisvogt
Last active July 4, 2024 03:15
Show Gist options
  • Save chrisvogt/7581d8f6c9343ddec5dd9dfc23d0435f to your computer and use it in GitHub Desktop.
Save chrisvogt/7581d8f6c9343ddec5dd9dfc23d0435f to your computer and use it in GitHub Desktop.
import React, { useEffect, useRef } from 'react';
const AnimatedBackground = () => {
const canvasRef = useRef(null);
useEffect(() => {
const canvas = canvasRef.current;
const ctx = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
// Predefined complex gradient colors
const gradients = [
[
{ position: 0, color: 'rgba(255, 183, 248, 1)' },
{ position: 0, color: 'rgba(218, 214, 237, 1)' },
{ position: 0, color: 'rgba(169, 255, 224, 1)' },
{ position: 0, color: 'rgba(88, 197, 210, 0.86)' },
{ position: 0.1, color: 'rgba(47, 201, 192, 1)' },
{ position: 0.75, color: 'rgba(192, 187, 255, 0.51)' },
{ position: 0.99, color: 'rgba(255, 255, 255, 0)' },
{ position: 0.99, color: 'rgba(153, 224, 255, 0)' }
],
[
{ position: 0.06, color: 'rgba(255, 204, 169, 1)' },
{ position: 0.13, color: 'rgba(255, 127, 255, 0)' },
{ position: 0.37, color: 'rgba(192, 187, 255, 0.37)' },
{ position: 0.82, color: 'rgba(255, 204, 169, 1)' },
{ position: 0.99, color: 'rgba(255, 255, 255, 1)' }
]
];
class Circle {
constructor(x, y, radius, gradientStops) {
this.x = x;
this.y = y;
this.radius = radius;
this.gradientStops = gradientStops;
this.dx = (Math.random() - 0.5) * 0.715; // Increase speed by another 30%
this.dy = (Math.random() - 0.5) * 0.715; // Increase speed by another 30%
// console.log(`Circle created at (${this.x}, ${this.y}) with radius=${this.radius}, dx=${this.dx}, dy=${this.dy}`);
}
draw() {
ctx.globalAlpha = 0.75; // Set transparency to 75%
const gradient = ctx.createRadialGradient(this.x, this.y, 0, this.x, this.y, this.radius);
this.gradientStops.forEach(stop => {
gradient.addColorStop(stop.position, stop.color);
});
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
ctx.fillStyle = gradient;
ctx.fill();
ctx.closePath();
ctx.globalAlpha = 1.0; // Reset transparency to 100%
}
update(circles) {
// console.log(`Circle at (${this.x}, ${this.y}) with dx=${this.dx}, dy=${this.dy}`);
this.x += this.dx;
this.y += this.dy;
if (this.x + this.radius > canvas.width || this.x - this.radius < 0) {
// console.log(`Circle hit horizontal boundary at (${this.x}, ${this.y})`);
this.dx = -this.dx;
}
if (this.y + this.radius > canvas.height || this.y - this.radius < 0) {
// console.log(`Circle hit vertical boundary at (${this.y}, ${this.y})`);
this.dy = -this.dy;
}
for (let i = 0; i < circles.length; i++) {
if (this === circles[i]) continue;
const dx = this.x - circles[i].x;
const dy = this.y - circles[i].y;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance < this.radius + circles[i].radius) {
// Resolve overlap
const overlap = this.radius + circles[i].radius - distance;
const angle = Math.atan2(dy, dx);
const moveX = overlap * Math.cos(angle) / 2;
const moveY = overlap * Math.sin(angle) / 2;
this.x += moveX;
this.y += moveY;
circles[i].x -= moveX;
circles[i].y -= moveY;
// Swap velocities
this.dx = -this.dx;
this.dy = -this.dy;
circles[i].dx = -circles[i].dx;
circles[i].dy = -circles[i].dy;
// console.log(`Circle collision resolved at (${this.x}, ${this.y})`);
}
}
this.draw();
}
}
const circles = [];
// One large circle
const largeRadius = Math.random() * 200 + 250; // Up to 450
const largeX = Math.random() * (canvas.width - largeRadius * 2) + largeRadius;
const largeY = Math.random() * (canvas.height - largeRadius * 2) + largeRadius;
const largeGradient = gradients[0]; // Use the first gradient for the large circle
const largeCircle = new Circle(largeX, largeY, largeRadius, largeGradient);
circles.push(largeCircle);
// Smaller circles
for (let i = 0; i < 3; i++) {
const radius = Math.random() * 70 + 80; // Smaller circles
const x = Math.random() * (canvas.width - radius * 2) + radius;
const y = Math.random() * (canvas.height - radius * 2) + radius;
const gradient = gradients[(i + 1) % 2]; // Alternate gradients between the two sets
const smallCircle = new Circle(x, y, radius, gradient);
circles.push(smallCircle);
}
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
circles.forEach(circle => circle.update(circles));
requestAnimationFrame(animate);
}
animate();
// Handle resizing of the window
const handleResize = () => {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
};
window.addEventListener('resize', handleResize);
return () => {
window.removeEventListener('resize', handleResize);
};
}, []);
return (
<div style={{ position: 'absolute', top: 0, left: 0, right: 0, bottom: 0 }}>
<canvas ref={canvasRef} style={{ display: 'block', position: 'absolute', top: 0, left: 0, width: '100%', height: '100%' }}></canvas>
<div style={{
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
backgroundColor: 'rgba(34, 34, 41, 0.02)',
backdropFilter: 'blur(100px)',
pointerEvents: 'none' // Ensure the overlay doesn't block interactions
}}></div>
</div>
);
};
export default AnimatedBackground;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment