ab-group-llc-landing-page/app/components/AnimatedBackground.tsx

123 lines
3.1 KiB
TypeScript

"use client";
import { useEffect, useRef } from "react";
interface Orb {
x: number;
y: number;
size: number;
speedX: number;
speedY: number;
opacity: number;
}
interface AnimatedBackgroundProps {
variant?: "dark" | "light" | "red";
orbCount?: number;
className?: string;
}
export default function AnimatedBackground({
variant = "dark",
orbCount = 5,
className = "",
}: AnimatedBackgroundProps) {
const canvasRef = useRef<HTMLCanvasElement>(null);
const orbsRef = useRef<Orb[]>([]);
const animationRef = useRef<number | undefined>(undefined);
useEffect(() => {
const canvas = canvasRef.current;
if (!canvas) return;
const ctx = canvas.getContext("2d");
if (!ctx) return;
const resizeCanvas = () => {
const parent = canvas.parentElement;
if (parent) {
canvas.width = parent.offsetWidth;
canvas.height = parent.offsetHeight;
}
};
resizeCanvas();
window.addEventListener("resize", resizeCanvas);
// Initialize orbs
orbsRef.current = Array.from({ length: orbCount }, () => ({
x: Math.random() * canvas.width,
y: Math.random() * canvas.height,
size: Math.random() * 200 + 100,
speedX: (Math.random() - 0.5) * 0.5,
speedY: (Math.random() - 0.5) * 0.5,
opacity: Math.random() * 0.3 + 0.1,
}));
const getOrbColor = (opacity: number) => {
switch (variant) {
case "dark":
return `rgba(220, 38, 38, ${opacity})`;
case "light":
return `rgba(220, 38, 38, ${opacity * 0.5})`;
case "red":
return `rgba(255, 255, 255, ${opacity * 0.3})`;
default:
return `rgba(220, 38, 38, ${opacity})`;
}
};
const animate = () => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
orbsRef.current.forEach((orb) => {
// Update position
orb.x += orb.speedX;
orb.y += orb.speedY;
// Bounce off edges
if (orb.x < -orb.size) orb.x = canvas.width + orb.size;
if (orb.x > canvas.width + orb.size) orb.x = -orb.size;
if (orb.y < -orb.size) orb.y = canvas.height + orb.size;
if (orb.y > canvas.height + orb.size) orb.y = -orb.size;
// Draw orb with gradient
const gradient = ctx.createRadialGradient(
orb.x,
orb.y,
0,
orb.x,
orb.y,
orb.size
);
gradient.addColorStop(0, getOrbColor(orb.opacity));
gradient.addColorStop(1, getOrbColor(0));
ctx.beginPath();
ctx.arc(orb.x, orb.y, orb.size, 0, Math.PI * 2);
ctx.fillStyle = gradient;
ctx.fill();
});
animationRef.current = requestAnimationFrame(animate);
};
animate();
return () => {
window.removeEventListener("resize", resizeCanvas);
if (animationRef.current) {
cancelAnimationFrame(animationRef.current);
}
};
}, [variant, orbCount]);
return (
<canvas
ref={canvasRef}
className={`absolute inset-0 pointer-events-none ${className}`}
style={{ filter: "blur(60px)" }}
/>
);
}