{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "light-lines", "type": "registry:ui", "dependencies": [], "files": [ { "path": "components/ui/light-lines.tsx", "content": "\"use client\";\r\n\r\nimport { useEffect, useRef } from \"react\";\r\nimport { cn } from \"@/lib/utils\";\r\n\r\ninterface LightLinesProps {\r\n /** Additional CSS classes */\r\n className?: string;\r\n /** Lines opacity (0-1) */\r\n linesOpacity?: number;\r\n /** Lights opacity (0-1) */\r\n lightsOpacity?: number;\r\n /** Animation speed multiplier */\r\n speedMultiplier?: number;\r\n /** Primary gradient color (from) */\r\n gradientFrom?: string;\r\n /** Primary gradient color (to) */\r\n gradientTo?: string;\r\n /** Light color */\r\n lightColor?: string;\r\n /** Line color */\r\n lineColor?: string;\r\n /** Children content to overlay */\r\n children?: React.ReactNode;\r\n}\r\n\r\ninterface AnimatedLightRef {\r\n element: SVGPathElement | null;\r\n from: number;\r\n to: number;\r\n duration: number;\r\n}\r\n\r\nexport function LightLines({\r\n className,\r\n linesOpacity = 0.05,\r\n lightsOpacity = 0.9,\r\n speedMultiplier = 1,\r\n gradientFrom = \"#2462F6\",\r\n gradientTo = \"#5999F8\",\r\n lightColor = \"#fff\",\r\n lineColor = \"#fff\",\r\n children,\r\n}: LightLinesProps) {\r\n const containerRef = useRef(null);\r\n const animationsRef = useRef([]);\r\n const frameRef = useRef(null);\r\n\r\n useEffect(() => {\r\n // Light elements configuration\r\n const lightsDown = [\r\n { selector: \".light4\", from: -1080, to: 1080 },\r\n { selector: \".light5\", from: -1080, to: 1080 },\r\n { selector: \".light6\", from: -1080, to: 1080 },\r\n { selector: \".light7\", from: -1080, to: 1080 },\r\n { selector: \".light8\", from: -1080, to: 1080 },\r\n { selector: \".light11\", from: -1080, to: 1080 },\r\n { selector: \".light12\", from: -1080, to: 1080 },\r\n { selector: \".light13\", from: -1080, to: 1080 },\r\n { selector: \".light14\", from: -1080, to: 1080 },\r\n { selector: \".light15\", from: -1080, to: 1080 },\r\n { selector: \".light16\", from: -1080, to: 1080 },\r\n ];\r\n\r\n const lightsUp = [\r\n { selector: \".light1\", from: 1080, to: -1080 },\r\n { selector: \".light2\", from: 1080, to: -1080 },\r\n { selector: \".light3\", from: 1080, to: -1080 },\r\n { selector: \".light9\", from: 1080, to: -1080 },\r\n { selector: \".light10\", from: 1080, to: -1080 },\r\n { selector: \".light17\", from: 1080, to: -1080 },\r\n ];\r\n\r\n const container = containerRef.current;\r\n if (!container) return;\r\n\r\n // Initialize animations\r\n const allLights = [...lightsDown, ...lightsUp];\r\n animationsRef.current = allLights.map((light) => {\r\n const element = container.querySelector(light.selector) as SVGPathElement | null;\r\n const duration = (Math.floor(Math.random() * 59) + 2) * 0.5 + 0.5;\r\n return {\r\n element,\r\n from: light.from,\r\n to: light.to,\r\n duration: duration / speedMultiplier,\r\n };\r\n });\r\n\r\n // Animation state for each light\r\n const animationState = animationsRef.current.map(() => ({\r\n startTime: performance.now() - Math.random() * 5000, // Stagger start times\r\n currentY: 0,\r\n }));\r\n\r\n let isVisible = false;\r\n\r\n const animate = (time: number) => {\r\n if (!isVisible) {\r\n frameRef.current = requestAnimationFrame(animate);\r\n return;\r\n }\r\n\r\n animationsRef.current.forEach((ref, index) => {\r\n if (!ref.element) return;\r\n\r\n const state = animationState[index];\r\n const elapsed = (time - state.startTime) / 1000; // Convert to seconds\r\n const progress = (elapsed % ref.duration) / ref.duration;\r\n const currentY = ref.from + (ref.to - ref.from) * progress;\r\n\r\n ref.element.style.transform = `translateY(${currentY}px)`;\r\n });\r\n\r\n frameRef.current = requestAnimationFrame(animate);\r\n };\r\n\r\n const observer = new IntersectionObserver(\r\n ([entry]) => {\r\n isVisible = entry.isIntersecting;\r\n },\r\n { threshold: 0 }\r\n );\r\n\r\n observer.observe(container);\r\n\r\n frameRef.current = requestAnimationFrame(animate);\r\n\r\n return () => {\r\n if (frameRef.current) {\r\n cancelAnimationFrame(frameRef.current);\r\n }\r\n observer.disconnect();\r\n };\r\n }, [speedMultiplier]);\r\n\r\n return (\r\n \r\n \r\n {/* Static Lines */}\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n\r\n {/* Animated Lights */}\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n\r\n {/* Content overlay */}\r\n {children && (\r\n
{children}
\r\n )}\r\n \r\n );\r\n}\r\n\r\nexport default LightLines;\r\n", "type": "registry:ui", "target": "components/ui/light-lines.tsx" } ] }