164 lines
4.2 KiB
TypeScript
164 lines
4.2 KiB
TypeScript
import { useEffect, useState } from 'react';
|
|
import { motion } from 'framer-motion';
|
|
import { InteractiveBubbles } from './InteractiveBubbles';
|
|
|
|
interface Particle {
|
|
id: number;
|
|
x: number;
|
|
y: number;
|
|
size: number;
|
|
speed: number;
|
|
delay: number;
|
|
}
|
|
|
|
export const AnimatedBackground = () => {
|
|
const [particles, setParticles] = useState<Particle[]>([]);
|
|
|
|
useEffect(() => {
|
|
const createParticles = () => {
|
|
const newParticles: Particle[] = [];
|
|
for (let i = 0; i < 50; i++) {
|
|
newParticles.push({
|
|
id: i,
|
|
x: Math.random() * window.innerWidth,
|
|
y: Math.random() * window.innerHeight,
|
|
size: Math.random() * 4 + 2,
|
|
speed: Math.random() * 0.5 + 0.1,
|
|
delay: Math.random() * 5,
|
|
});
|
|
}
|
|
setParticles(newParticles);
|
|
};
|
|
|
|
createParticles();
|
|
|
|
const handleResize = () => {
|
|
createParticles();
|
|
};
|
|
|
|
window.addEventListener('resize', handleResize);
|
|
return () => window.removeEventListener('resize', handleResize);
|
|
}, []);
|
|
|
|
return (
|
|
<div className="fixed inset-0 pointer-events-none overflow-hidden">
|
|
{/* Large liquid light cylinders from top */}
|
|
<div className="light-cylinder light-cylinder-1"></div>
|
|
<div className="light-cylinder light-cylinder-2"></div>
|
|
<div className="light-cylinder light-cylinder-3"></div>
|
|
|
|
{/* Fluid wave bubbles */}
|
|
<div className="wave-bubble"></div>
|
|
<div className="wave-bubble"></div>
|
|
<div className="wave-bubble"></div>
|
|
<div className="wave-bubble"></div>
|
|
<div className="wave-bubble"></div>
|
|
|
|
{/* Animated particles */}
|
|
{particles.map((particle) => (
|
|
<motion.div
|
|
key={particle.id}
|
|
className="absolute rounded-full bg-gradient-to-br from-purple-500/20 via-blue-500/20 to-pink-500/20"
|
|
style={{
|
|
width: particle.size,
|
|
height: particle.size,
|
|
left: particle.x,
|
|
top: particle.y,
|
|
}}
|
|
animate={{
|
|
y: [particle.y, particle.y - 100],
|
|
opacity: [0, 1, 0],
|
|
scale: [0, 1, 0],
|
|
}}
|
|
transition={{
|
|
duration: 10 + particle.speed * 10,
|
|
delay: particle.delay,
|
|
repeat: Infinity,
|
|
ease: "linear",
|
|
}}
|
|
/>
|
|
))}
|
|
|
|
|
|
{/* Interactive bubbles */}
|
|
<InteractiveBubbles />
|
|
|
|
{/* Floating musical notes */}
|
|
<motion.div
|
|
className="absolute top-20 left-10 text-2xl text-spotify-green/30"
|
|
animate={{
|
|
y: [0, -20, 0],
|
|
rotate: [0, 10, -10, 0],
|
|
}}
|
|
transition={{
|
|
duration: 4,
|
|
repeat: Infinity,
|
|
ease: "easeInOut",
|
|
}}
|
|
>
|
|
♪
|
|
</motion.div>
|
|
|
|
<motion.div
|
|
className="absolute top-40 right-20 text-3xl text-spotify-green/20"
|
|
animate={{
|
|
y: [0, -30, 0],
|
|
rotate: [0, -15, 15, 0],
|
|
}}
|
|
transition={{
|
|
duration: 6,
|
|
repeat: Infinity,
|
|
ease: "easeInOut",
|
|
delay: 1,
|
|
}}
|
|
>
|
|
♫
|
|
</motion.div>
|
|
|
|
<motion.div
|
|
className="absolute bottom-32 left-1/4 text-xl text-spotify-green/25"
|
|
animate={{
|
|
y: [0, -25, 0],
|
|
rotate: [0, 20, -20, 0],
|
|
}}
|
|
transition={{
|
|
duration: 5,
|
|
repeat: Infinity,
|
|
ease: "easeInOut",
|
|
delay: 2,
|
|
}}
|
|
>
|
|
♪
|
|
</motion.div>
|
|
|
|
<motion.div
|
|
className="absolute bottom-20 right-1/3 text-2xl text-spotify-green/30"
|
|
animate={{
|
|
y: [0, -35, 0],
|
|
rotate: [0, -25, 25, 0],
|
|
}}
|
|
transition={{
|
|
duration: 7,
|
|
repeat: Infinity,
|
|
ease: "easeInOut",
|
|
delay: 3,
|
|
}}
|
|
>
|
|
♫
|
|
</motion.div>
|
|
|
|
{/* Subtle grid pattern */}
|
|
<div
|
|
className="absolute inset-0 opacity-10"
|
|
style={{
|
|
backgroundImage: `
|
|
linear-gradient(rgba(29, 185, 84, 0.1) 1px, transparent 1px),
|
|
linear-gradient(90deg, rgba(29, 185, 84, 0.1) 1px, transparent 1px)
|
|
`,
|
|
backgroundSize: '50px 50px',
|
|
}}
|
|
/>
|
|
</div>
|
|
);
|
|
};
|