import React, { useRef, useState, useEffect, useCallback } from 'react'; import { motion, AnimatePresence } from 'framer-motion'; import { cn } from '@/lib/utils'; import { sanitizeColor } from './types'; import { useAnimationPause } from '@/hooks/useAnimationLoop'; interface Props { settings: Record; } interface BeamOptions { initialX: number; translateX: number; duration: number; repeatDelay: number; delay?: number; className?: string; } const BEAMS: BeamOptions[] = [ { initialX: 10, translateX: 10, duration: 7, repeatDelay: 3, delay: 2 }, { initialX: 600, translateX: 600, duration: 3, repeatDelay: 3, delay: 4 }, { initialX: 100, translateX: 100, duration: 7, repeatDelay: 7, className: 'h-6' }, { initialX: 400, translateX: 400, duration: 5, repeatDelay: 14, delay: 4 }, { initialX: 800, translateX: 800, duration: 11, repeatDelay: 2, className: 'h-20' }, { initialX: 1000, translateX: 1000, duration: 4, repeatDelay: 2, className: 'h-12' }, { initialX: 1200, translateX: 1200, duration: 6, repeatDelay: 4, delay: 2, className: 'h-6' }, ]; function Explosion({ beamColor, explosionColor, ...props }: React.HTMLProps & { beamColor: string; explosionColor: string }) { const spans = Array.from({ length: 20 }, (_, i) => ({ id: i, directionX: Math.floor(Math.random() * 80 - 40), directionY: Math.floor(Math.random() * -50 - 10), })); return (
{spans.map((span) => ( ))}
); } function CollisionMechanism({ containerRef, parentRef, beamOptions, beamColor, explosionColor, }: { containerRef: React.RefObject; parentRef: React.RefObject; beamOptions: BeamOptions; beamColor: string; explosionColor: string; }) { const beamRef = useRef(null); const [collision, setCollision] = useState<{ detected: boolean; coordinates: { x: number; y: number } | null; }>({ detected: false, coordinates: null }); const [beamKey, setBeamKey] = useState(0); const [cycleCollisionDetected, setCycleCollisionDetected] = useState(false); const checkRef = useRef({ cycleDetected: false }); checkRef.current.cycleDetected = cycleCollisionDetected; const checkCollision = useCallback(() => { if (checkRef.current.cycleDetected) return; if (!beamRef.current || !containerRef.current || !parentRef.current) return; const beamRect = beamRef.current.getBoundingClientRect(); const containerRect = containerRef.current.getBoundingClientRect(); const parentRect = parentRef.current.getBoundingClientRect(); if (beamRect.bottom >= containerRect.top) { const relativeX = beamRect.left - parentRect.left + beamRect.width / 2; const relativeY = beamRect.bottom - parentRect.top; setCollision({ detected: true, coordinates: { x: relativeX, y: relativeY } }); setCycleCollisionDetected(true); } }, [containerRef, parentRef]); useEffect(() => { let animId = 0; let lastCheck = 0; const CHECK_INTERVAL = 100; const loop = (time: number) => { if (time - lastCheck >= CHECK_INTERVAL) { lastCheck = time; checkCollision(); } animId = requestAnimationFrame(loop); }; animId = requestAnimationFrame(loop); return () => { if (animId) cancelAnimationFrame(animId); }; }, [checkCollision]); useEffect(() => { if (!collision.detected || !collision.coordinates) return; const timer = setTimeout(() => { setCollision({ detected: false, coordinates: null }); setCycleCollisionDetected(false); setBeamKey((prev) => prev + 1); }, 2000); return () => clearTimeout(timer); }, [collision.detected, collision.coordinates]); return ( <> {collision.detected && collision.coordinates && ( )} ); } export default function BackgroundBeamsCollision({ settings }: Props) { const containerRef = useRef(null); const parentRef = useRef(null); const paused = useAnimationPause(); const beamColor = sanitizeColor(settings.beamColor, '#6366f1'); const explosionColor = sanitizeColor(settings.explosionColor, '#a855f7'); return (
{!paused && BEAMS.map((beam) => ( ))}
); }