{ "name": "animated-gradient", "type": "registry:ui", "dependencies": [], "registryDependencies": [], "description": "Component for animated-gradient", "files": [ { "path": "components/ui/animated-gradient.tsx", "content": "\"use client\";\n\nimport { useRef, useEffect, useMemo, useState, CSSProperties } from \"react\";\nimport { cn } from \"@/lib/utils\";\n\ntype PatternShape = \"Checks\" | \"Stripes\" | \"Edge\";\n\nconst PatternShapes: Record = {\n Checks: 0,\n Stripes: 1,\n Edge: 2,\n};\n\ninterface PresetParams {\n color1: string;\n color2: string;\n color3: string;\n rotation: number;\n proportion: number;\n scale: number;\n speed: number;\n distortion: number;\n swirl: number;\n swirlIterations: number;\n softness: number;\n offset: number;\n shape: PatternShape;\n shapeSize: number;\n}\n\nconst presets: Record = {\n Aurora: {\n color1: \"#0a001a\",\n color2: \"#1a0b2e\",\n color3: \"#f20089\",\n rotation: -45,\n proportion: 60,\n scale: 0.6,\n speed: 15,\n distortion: 40,\n swirl: 80,\n swirlIterations: 10,\n softness: 100,\n offset: 200,\n shape: \"Edge\",\n shapeSize: 50,\n },\n Oceanic: {\n color1: \"#000814\",\n color2: \"#001d3d\",\n color3: \"#00b4d8\",\n rotation: 0,\n proportion: 70,\n scale: 0.4,\n speed: 10,\n distortion: 15,\n swirl: 50,\n swirlIterations: 12,\n softness: 80,\n offset: 150,\n shape: \"Checks\",\n shapeSize: 30,\n },\n Amber: {\n color1: \"#140c00\",\n color2: \"#4a2500\",\n color3: \"#f57c00\",\n rotation: 120,\n proportion: 80,\n scale: 0.8,\n speed: 20,\n distortion: 25,\n swirl: 60,\n swirlIterations: 8,\n softness: 90,\n offset: 500,\n shape: \"Stripes\",\n shapeSize: 40,\n },\n Toxic: {\n color1: \"#050d05\",\n color2: \"#0a240a\",\n color3: \"#39ff14\",\n rotation: -90,\n proportion: 55,\n scale: 0.5,\n speed: 25,\n distortion: 60,\n swirl: 100,\n swirlIterations: 15,\n softness: 70,\n offset: -100,\n shape: \"Edge\",\n shapeSize: 20,\n },\n Ghost: {\n color1: \"#0a0a0a\",\n color2: \"#1c1c1c\",\n color3: \"#a3a3a3\",\n rotation: 45,\n proportion: 50,\n scale: 0.3,\n speed: 8,\n distortion: 10,\n swirl: 30,\n swirlIterations: 5,\n softness: 100,\n offset: 0,\n shape: \"Checks\",\n shapeSize: 60,\n },\n};\n\ntype PresetName = \"Aurora\" | \"Oceanic\" | \"Amber\" | \"Toxic\" | \"Ghost\";\n\nexport interface CustomConfig {\n preset: \"custom\";\n color1: string;\n color2: string;\n color3: string;\n rotation?: number;\n proportion?: number;\n scale?: number;\n speed?: number;\n distortion?: number;\n swirl?: number;\n swirlIterations?: number;\n softness?: number;\n offset?: number;\n shape?: PatternShape;\n shapeSize?: number;\n}\n\nexport interface PresetConfig {\n preset: PresetName;\n speed?: number;\n}\n\nexport type GradientConfig = CustomConfig | PresetConfig;\n\nexport interface NoiseConfig {\n opacity: number;\n scale?: number;\n}\n\nexport interface AnimatedGradientProps {\n config?: GradientConfig;\n noise?: NoiseConfig;\n radius?: string;\n style?: CSSProperties;\n className?: string;\n}\n\nexport function AnimatedGradient({\n config = { preset: \"Aurora\" },\n noise,\n radius = \"0px\",\n style,\n className,\n}: AnimatedGradientProps) {\n const canvasRef = useRef(null);\n const containerRef = useRef(null);\n const frameIdRef = useRef(undefined);\n const startTimeRef = useRef(0);\n\n const [isMounted, setIsMounted] = useState(false);\n\n useEffect(() => {\n setIsMounted(true);\n return () => setIsMounted(false);\n }, []);\n\n const params = useMemo((): PresetParams => {\n if (config.preset === \"custom\") {\n return {\n color1: config.color1,\n color2: config.color2,\n color3: config.color3,\n rotation: config.rotation ?? 0,\n proportion: config.proportion ?? 35,\n scale: config.scale ?? 1,\n speed: config.speed ?? 25,\n distortion: config.distortion ?? 12,\n swirl: config.swirl ?? 80,\n swirlIterations: config.swirlIterations ?? 10,\n softness: config.softness ?? 100,\n offset: config.offset ?? 0,\n shape: config.shape ?? \"Checks\",\n shapeSize: config.shapeSize ?? 10,\n };\n }\n const preset = presets[config.preset] || presets.Aurora;\n return {\n ...preset,\n speed: config.speed ?? preset.speed,\n };\n }, [config]);\n\n useEffect(() => {\n const canvas = canvasRef.current;\n const container = containerRef.current;\n if (!canvas || !container || !isMounted) return;\n\n const gl = canvas.getContext(\"webgl2\", {\n premultipliedAlpha: true,\n alpha: true,\n antialias: true,\n });\n if (!gl) return;\n\n const vertexShaderSource = `#version 300 es\n in vec4 a_position;\n void main() {\n gl_Position = a_position;\n }`;\n\n const vertexShader = gl.createShader(gl.VERTEX_SHADER)!;\n gl.shaderSource(vertexShader, vertexShaderSource);\n gl.compileShader(vertexShader);\n\n const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER)!;\n gl.shaderSource(fragmentShader, FRAGMENT_SHADER);\n gl.compileShader(fragmentShader);\n\n const program = gl.createProgram()!;\n gl.attachShader(program, vertexShader);\n gl.attachShader(program, fragmentShader);\n gl.linkProgram(program);\n gl.useProgram(program);\n\n const positionBuffer = gl.createBuffer();\n gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);\n gl.bufferData(\n gl.ARRAY_BUFFER,\n new Float32Array([-1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1]),\n gl.STATIC_DRAW\n );\n\n const positionLocation = gl.getAttribLocation(program, \"a_position\");\n gl.enableVertexAttribArray(positionLocation);\n gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);\n\n const uniforms = {\n u_time: gl.getUniformLocation(program, \"u_time\"),\n u_resolution: gl.getUniformLocation(program, \"u_resolution\"),\n u_pixelRatio: gl.getUniformLocation(program, \"u_pixelRatio\"),\n u_scale: gl.getUniformLocation(program, \"u_scale\"),\n u_rotation: gl.getUniformLocation(program, \"u_rotation\"),\n u_color1: gl.getUniformLocation(program, \"u_color1\"),\n u_color2: gl.getUniformLocation(program, \"u_color2\"),\n u_color3: gl.getUniformLocation(program, \"u_color3\"),\n u_proportion: gl.getUniformLocation(program, \"u_proportion\"),\n u_softness: gl.getUniformLocation(program, \"u_softness\"),\n u_shape: gl.getUniformLocation(program, \"u_shape\"),\n u_shapeScale: gl.getUniformLocation(program, \"u_shapeScale\"),\n u_distortion: gl.getUniformLocation(program, \"u_distortion\"),\n u_swirl: gl.getUniformLocation(program, \"u_swirl\"),\n u_swirlIterations: gl.getUniformLocation(program, \"u_swirlIterations\"),\n };\n\n const resize = () => {\n const width = container.clientWidth;\n const height = container.clientHeight;\n const pixelRatio = window.devicePixelRatio || 1;\n canvas.width = width * pixelRatio;\n canvas.height = height * pixelRatio;\n canvas.style.width = `${width}px`;\n canvas.style.height = `${height}px`;\n gl.viewport(0, 0, canvas.width, canvas.height);\n };\n\n resize();\n const resizeObserver = new ResizeObserver(resize);\n resizeObserver.observe(container);\n\n startTimeRef.current = performance.now();\n\n const animate = (time: number) => {\n const elapsed = (time - startTimeRef.current) / 1000;\n const speed = (params.speed / 100) * 5;\n\n gl.uniform1f(uniforms.u_time, elapsed * speed + params.offset * 0.01);\n gl.uniform2f(uniforms.u_resolution, canvas.width, canvas.height);\n gl.uniform1f(uniforms.u_pixelRatio, window.devicePixelRatio || 1);\n gl.uniform1f(uniforms.u_scale, params.scale);\n gl.uniform1f(uniforms.u_rotation, (params.rotation * Math.PI) / 180);\n\n const c1 = hexToRgba(params.color1);\n const c2 = hexToRgba(params.color2);\n const c3 = hexToRgba(params.color3);\n gl.uniform4f(uniforms.u_color1, c1[0], c1[1], c1[2], c1[3]);\n gl.uniform4f(uniforms.u_color2, c2[0], c2[1], c2[2], c2[3]);\n gl.uniform4f(uniforms.u_color3, c3[0], c3[1], c3[2], c3[3]);\n\n gl.uniform1f(uniforms.u_proportion, params.proportion / 100);\n gl.uniform1f(uniforms.u_softness, params.softness / 100);\n gl.uniform1f(uniforms.u_shape, PatternShapes[params.shape]);\n gl.uniform1f(uniforms.u_shapeScale, params.shapeSize / 100);\n gl.uniform1f(uniforms.u_distortion, params.distortion / 50);\n gl.uniform1f(uniforms.u_swirl, params.swirl / 100);\n gl.uniform1f(\n uniforms.u_swirlIterations,\n params.swirl === 0 ? 0 : params.swirlIterations\n );\n\n gl.drawArrays(gl.TRIANGLES, 0, 6);\n frameIdRef.current = requestAnimationFrame(animate);\n };\n\n frameIdRef.current = requestAnimationFrame(animate);\n\n return () => {\n if (frameIdRef.current !== undefined) {\n cancelAnimationFrame(frameIdRef.current);\n }\n resizeObserver.disconnect();\n gl.deleteProgram(program);\n gl.deleteShader(vertexShader);\n gl.deleteShader(fragmentShader);\n gl.deleteBuffer(positionBuffer);\n };\n }, [isMounted, params]);\n\n return (\n \n \n {noise && noise.opacity > 0 && (\n \n )}\n \n );\n}\n\nfunction hexToRgba(hex: string): [number, number, number, number] {\n let r = 0,\n g = 0,\n b = 0,\n a = 1;\n\n if (hex.startsWith(\"rgba(\")) {\n const parts = hex.slice(5, -1).split(\",\");\n r = parseInt(parts[0] ?? \"0\") / 255;\n g = parseInt(parts[1] ?? \"0\") / 255;\n b = parseInt(parts[2] ?? \"0\") / 255;\n a = parseFloat(parts[3] ?? \"1\");\n } else if (hex.startsWith(\"rgb(\")) {\n const parts = hex.slice(4, -1).split(\",\");\n r = parseInt(parts[0] ?? \"0\") / 255;\n g = parseInt(parts[1] ?? \"0\") / 255;\n b = parseInt(parts[2] ?? \"0\") / 255;\n } else if (hex.startsWith(\"hsla(\") || hex.startsWith(\"hsl(\")) {\n const isHsla = hex.startsWith(\"hsla(\");\n const parts = hex.slice(isHsla ? 5 : 4, -1).split(\",\");\n const h = parseFloat(parts[0] ?? \"0\") / 360;\n const s = parseFloat(parts[1] ?? \"0\") / 100;\n const l = parseFloat(parts[2] ?? \"0\") / 100;\n a = isHsla ? parseFloat(parts[3] ?? \"1\") : 1;\n [r, g, b] = hslToRgb(h, s, l);\n } else if (hex.startsWith(\"#\")) {\n const c = hex.slice(1);\n if (c.length === 3) {\n r = parseInt(c.charAt(0) + c.charAt(0), 16) / 255;\n g = parseInt(c.charAt(1) + c.charAt(1), 16) / 255;\n b = parseInt(c.charAt(2) + c.charAt(2), 16) / 255;\n } else if (c.length >= 6) {\n r = parseInt(c.slice(0, 2), 16) / 255;\n g = parseInt(c.slice(2, 4), 16) / 255;\n b = parseInt(c.slice(4, 6), 16) / 255;\n if (c.length === 8) {\n a = parseInt(c.slice(6, 8), 16) / 255;\n }\n }\n }\n\n return [r, g, b, a];\n}\n\nfunction hslToRgb(h: number, s: number, l: number): [number, number, number] {\n let r: number, g: number, b: number;\n\n if (s === 0) {\n r = g = b = l;\n } else {\n const hue2rgb = (p: number, q: number, t: number) => {\n if (t < 0) t += 1;\n if (t > 1) t -= 1;\n if (t < 1 / 6) return p + (q - p) * 6 * t;\n if (t < 1 / 2) return q;\n if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;\n return p;\n };\n\n const q = l < 0.5 ? l * (1 + s) : l + s - l * s;\n const p = 2 * l - q;\n r = hue2rgb(p, q, h + 1 / 3);\n g = hue2rgb(p, q, h);\n b = hue2rgb(p, q, h - 1 / 3);\n }\n\n return [r, g, b];\n}\n\nconst FRAGMENT_SHADER = `#version 300 es\nprecision highp float;\n\nuniform float u_time;\nuniform float u_pixelRatio;\nuniform vec2 u_resolution;\n\nuniform float u_scale;\nuniform float u_rotation;\nuniform vec4 u_color1;\nuniform vec4 u_color2;\nuniform vec4 u_color3;\nuniform float u_proportion;\nuniform float u_softness;\nuniform float u_shape;\nuniform float u_shapeScale;\nuniform float u_distortion;\nuniform float u_swirl;\nuniform float u_swirlIterations;\n\nout vec4 fragColor;\n\n#define TWO_PI 6.28318530718\n#define PI 3.14159265358979323846\n\nvec2 rotate(vec2 uv, float th) {\n return mat2(cos(th), sin(th), -sin(th), cos(th)) * uv;\n}\n\nfloat random(vec2 st) {\n return fract(sin(dot(st.xy, vec2(12.9898, 78.233))) * 43758.5453123);\n}\n\nfloat noise(vec2 st) {\n vec2 i = floor(st);\n vec2 f = fract(st);\n float a = random(i);\n float b = random(i + vec2(1.0, 0.0));\n float c = random(i + vec2(0.0, 1.0));\n float d = random(i + vec2(1.0, 1.0));\n\n vec2 u = f * f * (3.0 - 2.0 * f);\n\n float x1 = mix(a, b, u.x);\n float x2 = mix(c, d, u.x);\n return mix(x1, x2, u.y);\n}\n\nvec4 blend_colors(vec4 c1, vec4 c2, vec4 c3, float mixer, float edgesWidth, float edge_blur) {\n vec3 color1 = c1.rgb * c1.a;\n vec3 color2 = c2.rgb * c2.a;\n vec3 color3 = c3.rgb * c3.a;\n\n float r1 = smoothstep(.0 + .35 * edgesWidth, .7 - .35 * edgesWidth + .5 * edge_blur, mixer);\n float r2 = smoothstep(.3 + .35 * edgesWidth, 1. - .35 * edgesWidth + edge_blur, mixer);\n\n vec3 blended_color_2 = mix(color1, color2, r1);\n float blended_opacity_2 = mix(c1.a, c2.a, r1);\n\n vec3 c = mix(blended_color_2, color3, r2);\n float o = mix(blended_opacity_2, c3.a, r2);\n return vec4(c, o);\n}\n\nvoid main() {\n vec2 uv = gl_FragCoord.xy / u_resolution.xy;\n\n float t = .5 * u_time;\n\n float noise_scale = .0005 + .006 * u_scale;\n\n uv -= .5;\n uv *= (noise_scale * u_resolution);\n uv = rotate(uv, u_rotation * .5 * PI);\n uv /= u_pixelRatio;\n uv += .5;\n\n float n1 = noise(uv * 1. + t);\n float n2 = noise(uv * 2. - t);\n float angle = n1 * TWO_PI;\n uv.x += 4. * u_distortion * n2 * cos(angle);\n uv.y += 4. * u_distortion * n2 * sin(angle);\n\n float iterations_number = ceil(clamp(u_swirlIterations, 1., 30.));\n for (float i = 1.; i <= iterations_number; i++) {\n uv.x += clamp(u_swirl, 0., 2.) / i * cos(t + i * 1.5 * uv.y);\n uv.y += clamp(u_swirl, 0., 2.) / i * cos(t + i * 1. * uv.x);\n }\n\n float proportion = clamp(u_proportion, 0., 1.);\n\n float shape = 0.;\n float mixer = 0.;\n if (u_shape < .5) {\n vec2 checks_shape_uv = uv * (.5 + 3.5 * u_shapeScale);\n shape = .5 + .5 * sin(checks_shape_uv.x) * cos(checks_shape_uv.y);\n mixer = shape + .48 * sign(proportion - .5) * pow(abs(proportion - .5), .5);\n } else if (u_shape < 1.5) {\n vec2 stripes_shape_uv = uv * (.25 + 3. * u_shapeScale);\n float f = fract(stripes_shape_uv.y);\n shape = smoothstep(.0, .55, f) * smoothstep(1., .45, f);\n mixer = shape + .48 * sign(proportion - .5) * pow(abs(proportion - .5), .5);\n } else {\n float sh = 1. - uv.y;\n sh -= .5;\n sh /= (noise_scale * u_resolution.y);\n sh += .5;\n float shape_scaling = .2 * (1. - u_shapeScale);\n shape = smoothstep(.45 - shape_scaling, .55 + shape_scaling, sh + .3 * (proportion - .5));\n mixer = shape;\n }\n\n vec4 color_mix = blend_colors(u_color1, u_color2, u_color3, mixer, 1. - clamp(u_softness, 0., 1.), .01 + .01 * u_scale);\n\n fragColor = vec4(color_mix.rgb, color_mix.a);\n}\n`;\n", "type": "registry:ui" } ], "$schema": "https://ui.shadcn.com/schema/registry-item.json", "title": "Animated Gradient", "devDependencies": [] }