"use client" import React, { useCallback, useMemo, useRef } from "react" import { motion, useAnimationControls } from "motion/react" import { v4 as uuidv4 } from "uuid" import { cn } from "@/lib/utils" import { useDimensions } from "@/hooks/use-dimensions" interface PixelTrailProps { pixelSize: number // px fadeDuration?: number // ms delay?: number // ms className?: string pixelClassName?: string } const PixelTrail: React.FC = ({ pixelSize = 20, fadeDuration = 500, delay = 0, className, pixelClassName, }) => { const containerRef = useRef(null) const dimensions = useDimensions(containerRef) const trailId = useRef(uuidv4()) const handleMouseMove = useCallback( (e: React.MouseEvent) => { if (!containerRef.current) return const rect = containerRef.current.getBoundingClientRect() const x = Math.floor((e.clientX - rect.left) / pixelSize) const y = Math.floor((e.clientY - rect.top) / pixelSize) const pixelElement = document.getElementById( `${trailId.current}-pixel-${x}-${y}` ) if (pixelElement) { const animatePixel = (pixelElement as any).__animatePixel if (animatePixel) animatePixel() } }, [pixelSize] ) const columns = useMemo( () => Math.ceil(dimensions.width / pixelSize), [dimensions.width, pixelSize] ) const rows = useMemo( () => Math.ceil(dimensions.height / pixelSize), [dimensions.height, pixelSize] ) return (
{Array.from({ length: rows }).map((_, rowIndex) => (
{Array.from({ length: columns }).map((_, colIndex) => ( ))}
))}
) } export default PixelTrail interface PixelDotProps { id: string size: number fadeDuration: number delay: number className?: string } const PixelDot: React.FC = React.memo( ({ id, size, fadeDuration, delay, className }) => { const controls = useAnimationControls() const animatePixel = useCallback(() => { controls.start({ opacity: [1, 0], transition: { duration: fadeDuration / 1000, delay: delay / 1000 }, }) }, []) // Attach the animatePixel function to the DOM element const ref = useCallback( (node: HTMLDivElement | null) => { if (node) { ;(node as any).__animatePixel = animatePixel } }, [animatePixel] ) return ( ) } ) PixelDot.displayName = "PixelDot"