{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "image-trail",
  "type": "registry:ui",
  "title": "Image Trail",
  "dependencies": [],
  "devDependencies": [],
  "registryDependencies": [],
  "description": "Component for image-trail",
  "files": [
    {
      "path": "components/ui/image-trail.tsx",
      "content": "\"use client\"\n\nimport React, { useEffect, useRef } from \"react\"\nimport { Expo, gsap, Power1, Quint } from \"gsap\"\n\ninterface ImageTrailProps {\n    images: string[]\n    imageWidth?: number\n    imageHeight?: number\n    threshold?: number\n    duration?: number\n}\n\nexport function ImageTrail({\n    images = [],\n    imageWidth = 200,\n    imageHeight = 200,\n    threshold = 50,\n    duration = 1.6,\n}: ImageTrailProps) {\n    const contentRef = useRef<HTMLDivElement | null>(null)\n    const imagesRef = useRef<HTMLImageElement[]>([])\n    const mousePos = useRef({ x: 0, y: 0 })\n    const cacheMousePos = useRef({ x: 0, y: 0 })\n    const lastMousePos = useRef({ x: 0, y: 0 })\n    const zIndexVal = useRef(1)\n    const imgPosition = useRef(0)\n    const parentSize = useRef({ width: 0, height: 0 })\n\n    useEffect(() => {\n        if (contentRef.current) {\n            imagesRef.current = Array.from(contentRef.current.querySelectorAll(\"img\"))\n        }\n\n        const handleMouseMove = (e: MouseEvent) => {\n            const rect = contentRef.current?.getBoundingClientRect()\n            if (rect) {\n                mousePos.current = {\n                    x: e.clientX - rect.left,\n                    y: e.clientY - rect.top,\n                }\n            }\n        }\n\n        calcParentSize()\n        if (imagesRef.current.length === 0) {\n            return\n        }\n\n        window.addEventListener(\"mousemove\", handleMouseMove)\n        window.addEventListener(\"resize\", calcParentSize)\n\n        requestAnimationFrame(renderImages)\n\n        return () => {\n            window.removeEventListener(\"mousemove\", handleMouseMove)\n            window.removeEventListener(\"resize\", calcParentSize)\n        }\n        // eslint-disable-next-line react-hooks/exhaustive-deps\n    }, [])\n\n    const calcParentSize = () => {\n        const rect = contentRef.current?.getBoundingClientRect()\n        if (rect) {\n            parentSize.current = { width: rect.width, height: rect.height }\n        }\n    }\n\n    const lerp = (a: number, b: number, n: number) => (1 - n) * a + n * b\n\n    const getMouseDistance = () => {\n        const dx = mousePos.current.x - lastMousePos.current.x\n        const dy = mousePos.current.y - lastMousePos.current.y\n        return Math.hypot(dx, dy)\n    }\n\n    const renderImages = () => {\n        const distance = getMouseDistance()\n\n        cacheMousePos.current.x = lerp(\n            cacheMousePos.current.x,\n            mousePos.current.x,\n            0.1\n        )\n        cacheMousePos.current.y = lerp(\n            cacheMousePos.current.y,\n            mousePos.current.y,\n            0.1\n        )\n\n        if (distance > threshold) {\n            showNextImage()\n            zIndexVal.current += 1\n            imgPosition.current = (imgPosition.current + 1) % imagesRef.current.length\n            lastMousePos.current = { ...mousePos.current }\n        }\n\n        requestAnimationFrame(renderImages)\n    }\n\n    const showNextImage = () => {\n        const img = imagesRef.current[imgPosition.current]\n        if (!img) return\n\n        const rect = img.getBoundingClientRect()\n        gsap.killTweensOf(img)\n\n        gsap\n            .timeline()\n            .set(img, {\n                startAt: { opacity: 0 },\n                opacity: 1,\n                zIndex: zIndexVal.current,\n                x: cacheMousePos.current.x - rect.width / 2,\n                y: cacheMousePos.current.y - rect.height / 2,\n            })\n            .to(img, {\n                duration: duration,\n                ease: Expo.easeOut,\n                x: mousePos.current.x - rect.width / 2,\n                y: mousePos.current.y - rect.height / 2,\n            })\n            .to(\n                img,\n                {\n                    duration: 1,\n                    ease: Power1.easeOut,\n                    opacity: 0,\n                },\n                duration - 1 > 0 ? duration - 1 : 0.4\n            )\n            .to(\n                img,\n                {\n                    duration: 1,\n                    ease: Quint.easeInOut,\n                    y: `+=${parentSize.current.height + rect.height / 2}`,\n                },\n                duration - 1 > 0 ? duration - 1 : 0.4\n            )\n    }\n\n    return (\n        <div\n            style={\n                {\n                    \"--image-width\": `${imageWidth}px`,\n                    \"--image-height\": `${imageHeight}px`,\n                } as React.CSSProperties\n            }\n            className=\"relative isolate z-0 flex h-full w-full items-center justify-center overflow-hidden\"\n            ref={contentRef}\n        >\n            {images.map((url, index) => (\n                <img\n                    key={index}\n                    className=\"pointer-events-none absolute top-0 left-0 h-[var(--image-height)] w-[var(--image-width)] object-cover opacity-0 will-change-transform\"\n                    src={url}\n                    alt={`trail element ${index + 1}`}\n                />\n            ))}\n        </div>\n    )\n}\n",
      "type": "registry:ui"
    }
  ]
}