{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "glowy-waves-hero-shadcnui", "type": "registry:block", "title": "Glowy Waves Hero", "description": "Hero section with glowy waves effect", "registryDependencies": [ "button" ], "dependencies": [ "framer-motion", "react" ], "files": [ { "path": "@uitripled/react-shadcn/src/components/sections/glowy-waves-hero.tsx", "content": "\"use client\";\n\nimport { motion, type Variants } from \"framer-motion\";\nimport { ArrowRight, Sparkles } from \"lucide-react\";\nimport { useEffect, useRef } from \"react\";\n\nimport { Button } from \"@/components/ui/button\";\n\ntype Point = {\n x: number;\n y: number;\n};\n\ninterface WaveConfig {\n offset: number;\n amplitude: number;\n frequency: number;\n color: string;\n opacity: number;\n}\n\nconst highlightPills = [\n \"Immersive visuals\",\n \"Responsive motion\",\n \"GPU friendly\",\n] as const;\n\nconst heroStats: { label: string; value: string }[] = [\n { label: \"Live installations\", value: \"320+\" },\n { label: \"Latency\", value: \"8ms\" },\n { label: \"Teams onboarded\", value: \"120+\" },\n];\n\nconst containerVariants: Variants = {\n hidden: { opacity: 0, y: 24 },\n visible: {\n opacity: 1,\n y: 0,\n transition: { duration: 0.8, staggerChildren: 0.12 },\n },\n};\n\nconst itemVariants: Variants = {\n hidden: { opacity: 0, y: 24 },\n visible: {\n opacity: 1,\n y: 0,\n transition: { duration: 0.6, ease: \"easeOut\" },\n },\n};\n\nconst statsVariants: Variants = {\n hidden: { opacity: 0, scale: 0.95 },\n visible: {\n opacity: 1,\n scale: 1,\n transition: { duration: 0.6, ease: \"easeOut\", staggerChildren: 0.08 },\n },\n};\n\nexport function GlowyWavesHero() {\n const canvasRef = useRef(null);\n const mouseRef = useRef({ x: 0, y: 0 });\n const targetMouseRef = useRef({ x: 0, y: 0 });\n\n useEffect(() => {\n const canvas = canvasRef.current;\n if (!canvas) return undefined;\n\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) return undefined;\n\n let animationId: number;\n let time = 0;\n\n const computeThemeColors = () => {\n const rootStyles = getComputedStyle(document.documentElement);\n\n // Helper to convert any CSS color to a Canvas-compatible format\n const resolveColor = (variables: string[], alpha = 1) => {\n // Create a temporary element to get computed color\n const tempEl = document.createElement(\"div\");\n tempEl.style.position = \"absolute\";\n tempEl.style.visibility = \"hidden\";\n tempEl.style.width = \"1px\";\n tempEl.style.height = \"1px\";\n document.body.appendChild(tempEl);\n\n let color = `rgba(255, 255, 255, ${alpha})`;\n\n for (const variable of variables) {\n const value = rootStyles.getPropertyValue(variable).trim();\n if (value) {\n // Try to set the background color using the CSS variable\n tempEl.style.backgroundColor = `var(${variable})`;\n const computedColor = getComputedStyle(tempEl).backgroundColor;\n\n if (computedColor && computedColor !== \"rgba(0, 0, 0, 0)\") {\n // Convert RGB to RGBA with alpha if needed\n if (alpha < 1) {\n const rgbMatch = computedColor.match(\n /rgba?\\((\\d+),\\s*(\\d+),\\s*(\\d+)(?:,\\s*[\\d.]+)?\\)/\n );\n if (rgbMatch) {\n color = `rgba(${rgbMatch[1]}, ${rgbMatch[2]}, ${rgbMatch[3]}, ${alpha})`;\n } else {\n color = computedColor;\n }\n } else {\n color = computedColor;\n }\n break;\n }\n }\n }\n\n document.body.removeChild(tempEl);\n return color;\n };\n\n return {\n backgroundTop: resolveColor([\"--background\"], 1),\n backgroundBottom: resolveColor([\"--muted\", \"--background\"], 0.95),\n wavePalette: [\n {\n offset: 0,\n amplitude: 70,\n frequency: 0.003,\n color: resolveColor([\"--primary\"], 0.8),\n opacity: 0.45,\n },\n {\n offset: Math.PI / 2,\n amplitude: 90,\n frequency: 0.0026,\n color: resolveColor([\"--accent\", \"--primary\"], 0.7),\n opacity: 0.35,\n },\n {\n offset: Math.PI,\n amplitude: 60,\n frequency: 0.0034,\n color: resolveColor([\"--secondary\", \"--foreground\"], 0.65),\n opacity: 0.3,\n },\n {\n offset: Math.PI * 1.5,\n amplitude: 80,\n frequency: 0.0022,\n color: resolveColor([\"--primary-foreground\", \"--foreground\"], 0.25),\n opacity: 0.25,\n },\n {\n offset: Math.PI * 2,\n amplitude: 55,\n frequency: 0.004,\n color: resolveColor([\"--foreground\"], 0.2),\n opacity: 0.2,\n },\n ] satisfies WaveConfig[],\n };\n };\n\n let themeColors = computeThemeColors();\n\n const handleThemeMutation = () => {\n themeColors = computeThemeColors();\n };\n\n const observer = new MutationObserver(handleThemeMutation);\n observer.observe(document.documentElement, {\n attributes: true,\n attributeFilter: [\"class\", \"data-theme\"],\n });\n\n const prefersReducedMotion = window.matchMedia(\n \"(prefers-reduced-motion: reduce)\"\n ).matches;\n\n const mouseInfluence = prefersReducedMotion ? 10 : 70;\n const influenceRadius = prefersReducedMotion ? 160 : 320;\n const smoothing = prefersReducedMotion ? 0.04 : 0.1;\n\n const resizeCanvas = () => {\n canvas.width = window.innerWidth;\n canvas.height = window.innerHeight;\n };\n\n const recenterMouse = () => {\n const centerPoint = { x: canvas.width / 2, y: canvas.height / 2 };\n mouseRef.current = centerPoint;\n targetMouseRef.current = centerPoint;\n };\n\n const handleResize = () => {\n resizeCanvas();\n recenterMouse();\n };\n\n const handleMouseMove = (event: MouseEvent) => {\n targetMouseRef.current = { x: event.clientX, y: event.clientY };\n };\n\n const handleMouseLeave = () => {\n recenterMouse();\n };\n\n resizeCanvas();\n recenterMouse();\n\n window.addEventListener(\"resize\", handleResize);\n window.addEventListener(\"mousemove\", handleMouseMove);\n window.addEventListener(\"mouseleave\", handleMouseLeave);\n\n const drawWave = (wave: WaveConfig) => {\n ctx.save();\n ctx.beginPath();\n\n for (let x = 0; x <= canvas.width; x += 4) {\n const dx = x - mouseRef.current.x;\n const dy = canvas.height / 2 - mouseRef.current.y;\n const distance = Math.sqrt(dx * dx + dy * dy);\n const influence = Math.max(0, 1 - distance / influenceRadius);\n const mouseEffect =\n influence *\n mouseInfluence *\n Math.sin(time * 0.001 + x * 0.01 + wave.offset);\n\n const y =\n canvas.height / 2 +\n Math.sin(x * wave.frequency + time * 0.002 + wave.offset) *\n wave.amplitude +\n Math.sin(x * wave.frequency * 0.4 + time * 0.003) *\n (wave.amplitude * 0.45) +\n mouseEffect;\n\n if (x === 0) {\n ctx.moveTo(x, y);\n } else {\n ctx.lineTo(x, y);\n }\n }\n\n ctx.lineWidth = 2.5;\n ctx.strokeStyle = wave.color;\n ctx.globalAlpha = wave.opacity;\n ctx.shadowBlur = 35;\n ctx.shadowColor = wave.color;\n ctx.stroke();\n\n ctx.restore();\n };\n\n const animate = () => {\n time += 1;\n\n mouseRef.current.x +=\n (targetMouseRef.current.x - mouseRef.current.x) * smoothing;\n mouseRef.current.y +=\n (targetMouseRef.current.y - mouseRef.current.y) * smoothing;\n\n const gradient = ctx.createLinearGradient(0, 0, 0, canvas.height);\n gradient.addColorStop(0, themeColors.backgroundTop);\n gradient.addColorStop(1, themeColors.backgroundBottom);\n\n ctx.fillStyle = gradient;\n ctx.fillRect(0, 0, canvas.width, canvas.height);\n\n ctx.globalAlpha = 1;\n ctx.shadowBlur = 0;\n\n themeColors.wavePalette.forEach(drawWave);\n\n animationId = window.requestAnimationFrame(animate);\n };\n\n animationId = window.requestAnimationFrame(animate);\n\n return () => {\n window.removeEventListener(\"resize\", handleResize);\n window.removeEventListener(\"mousemove\", handleMouseMove);\n window.removeEventListener(\"mouseleave\", handleMouseLeave);\n cancelAnimationFrame(animationId);\n observer.disconnect();\n };\n }, []);\n\n return (\n \n \n\n
\n
\n
\n
\n
\n\n
\n \n \n \n Reactive canvas hero\n \n\n \n Welcome to immersive{\" \"}\n \n realtime playgrounds\n \n \n\n \n Build living surfaces that respond to every interaction. Craft\n cinematic hero moments, responsive canvases, and luminous gradients\n without leaving your design system.\n \n\n \n \n Launch Studio\n \n \n \n Explore stories\n \n \n\n \n {highlightPills.map((pill) => (\n \n {pill}\n \n ))}\n \n\n \n {heroStats.map((stat) => (\n \n
\n {stat.label}\n
\n
\n {stat.value}\n
\n \n ))}\n \n \n
\n \n );\n}\n", "type": "registry:block", "target": "components/uitripled/glowy-waves-hero-shadcnui.tsx" } ] }