This commit is contained in:
HugeFrog24
2025-01-15 03:40:11 +01:00
parent 99cc7218a7
commit 52ddcd3db9
10 changed files with 394 additions and 75 deletions

View File

@@ -2,6 +2,7 @@
import { useState, useEffect } from 'react';
import { HeartIcon } from '@heroicons/react/24/solid';
import { shakeConfig } from '../config/shake';
interface Heart {
id: number;
@@ -9,6 +10,7 @@ interface Heart {
speed: number;
startPosition: { x: number; y: number };
scale: number;
createdAt: number;
}
interface FloatingHeartsProps {
@@ -18,56 +20,66 @@ interface FloatingHeartsProps {
export function FloatingHearts({ intensity }: FloatingHeartsProps) {
const [hearts, setHearts] = useState<Heart[]>([]);
// Cleanup interval to remove old hearts
useEffect(() => {
const cleanupInterval = setInterval(() => {
const now = Date.now();
setHearts(prev => prev.filter(heart =>
now - heart.createdAt < shakeConfig.animations.heartFloat
));
}, shakeConfig.hearts.cleanupInterval);
return () => clearInterval(cleanupInterval);
}, []);
useEffect(() => {
if (intensity <= 0) return;
// Number of hearts based on intensity
const numHearts = Math.min(Math.floor(intensity * 2), 50);
const numHearts = Math.min(Math.floor(intensity * 2), shakeConfig.hearts.maxPerShake);
// Create waves of hearts
const waves = 4; // Number of waves
const heartsPerWave = Math.ceil(numHearts / waves);
const waveDelay = 200; // Delay between waves in ms
const heartsPerWave = Math.ceil(numHearts / shakeConfig.hearts.waves);
const timers: NodeJS.Timeout[] = [];
// Generate hearts in waves
for (let wave = 0; wave < waves; wave++) {
for (let wave = 0; wave < shakeConfig.hearts.waves; wave++) {
const timer = setTimeout(() => {
const newHearts = Array.from({ length: heartsPerWave }, (_, i) => {
const totalIndex = wave * heartsPerWave + i;
return {
id: Date.now() + totalIndex,
// Distribute angles evenly within each wave
angle: Math.random() * 360, // Random angle for full radial distribution
speed: 0.8 + Math.random() * 0.4,
startPosition: {
x: Math.random() * 40 - 20,
y: Math.random() * 40 - 20,
},
scale: 0.8 + Math.random() * 0.4,
};
});
const now = Date.now();
const newHearts = Array.from({ length: heartsPerWave }, (_, i) => ({
id: now + i,
angle: Math.random() * 360,
speed: shakeConfig.hearts.minSpeed +
Math.random() * (shakeConfig.hearts.maxSpeed - shakeConfig.hearts.minSpeed),
startPosition: {
x: Math.random() * (shakeConfig.hearts.spreadX * 2) - shakeConfig.hearts.spreadX,
y: Math.random() * (shakeConfig.hearts.spreadY * 2) - shakeConfig.hearts.spreadY,
},
scale: shakeConfig.hearts.minScale +
Math.random() * (shakeConfig.hearts.maxScale - shakeConfig.hearts.minScale),
createdAt: now,
}));
setHearts(prev => [...prev, ...newHearts]);
}, wave * waveDelay);
// Remove this wave's hearts after animation
const cleanupTimer = setTimeout(() => {
setHearts(prev => prev.filter(heart => heart.createdAt !== now));
}, shakeConfig.animations.heartFloat);
timers.push(cleanupTimer);
}, wave * shakeConfig.hearts.waveDelay);
timers.push(timer);
}
// Remove hearts after animation completes
const cleanupTimer = setTimeout(() => {
setHearts(prev => prev.filter(heart => heart.id > Date.now() - 3500));
}, waves * waveDelay + 3500);
timers.push(cleanupTimer);
return () => {
timers.forEach(timer => clearTimeout(timer));
};
}, [intensity]);
return (
<div className="absolute inset-0 pointer-events-none -z-10">
<div className="absolute inset-0 pointer-events-none -z-10 overflow-hidden">
{hearts.map((heart) => {
const style = {
'--angle': `${heart.angle}deg`,
@@ -82,6 +94,9 @@ export function FloatingHearts({ intensity }: FloatingHeartsProps) {
key={heart.id}
className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 animate-float-heart"
style={style}
onAnimationEnd={() => {
setHearts(prev => prev.filter(h => h.id !== heart.id));
}}
>
<HeartIcon
className="w-16 h-16 text-pink-500 opacity-80 animate-fade-out"