{ "name": "noise-texture", "type": "registry:ui", "dependencies": [], "registryDependencies": [], "description": "An animated noise/grain texture overlay effect with customizable grain size and blend modes.", "files": [ { "path": "components/ui/noise-texture.tsx", "content": "\"use client\"\n\nimport { useEffect, useRef } from \"react\"\nimport { cn } from \"@/lib/utils\"\n\ninterface NoiseTextureProps {\n className?: string\n opacity?: number\n speed?: number\n grain?: \"fine\" | \"medium\" | \"coarse\"\n blend?: \"overlay\" | \"soft-light\" | \"multiply\" | \"screen\" | \"normal\"\n animate?: boolean\n}\n\nexport function NoiseTexture({\n className,\n opacity = 0.4,\n speed = 10,\n grain = \"medium\",\n blend = \"normal\",\n animate = true,\n}: NoiseTextureProps) {\n const canvasRef = useRef(null)\n const animationRef = useRef(0)\n\n useEffect(() => {\n const canvas = canvasRef.current\n if (!canvas) return\n\n const ctx = canvas.getContext(\"2d\")\n if (!ctx) return\n\n const grainSizes: Record = {\n fine: 1,\n medium: 2,\n coarse: 3,\n }\n const grainSize = grainSizes[grain] ?? 2\n\n const resize = () => {\n const rect = canvas.getBoundingClientRect()\n const dpr = Math.min(window.devicePixelRatio || 1, 2)\n canvas.width = Math.ceil(rect.width / grainSize) * dpr\n canvas.height = Math.ceil(rect.height / grainSize) * dpr\n canvas.style.width = `${rect.width}px`\n canvas.style.height = `${rect.height}px`\n }\n\n resize()\n window.addEventListener(\"resize\", resize)\n\n const renderNoise = () => {\n const { width, height } = canvas\n if (width === 0 || height === 0) return\n \n const imageData = ctx.createImageData(width, height)\n const data = imageData.data\n\n for (let i = 0; i < data.length; i += 4) {\n const value = Math.random() * 255\n data[i] = value\n data[i + 1] = value\n data[i + 2] = value\n data[i + 3] = 255\n }\n\n ctx.putImageData(imageData, 0, 0)\n }\n\n renderNoise()\n\n if (animate) {\n const animateNoise = () => {\n renderNoise()\n animationRef.current = setTimeout(() => {\n requestAnimationFrame(animateNoise)\n }, 1000 / speed)\n }\n animateNoise()\n }\n\n return () => {\n window.removeEventListener(\"resize\", resize)\n if (animationRef.current) {\n clearTimeout(animationRef.current as NodeJS.Timeout)\n }\n }\n }, [grain, speed, animate])\n\n const grainSizes: Record = {\n fine: 1,\n medium: 2,\n coarse: 3,\n }\n\n return (\n \n )\n}\n", "type": "registry:ui" } ] }