@lobehub/ui
Version:
Lobe UI is an open-source UI component library for building AIGC web apps
1 lines • 15.4 kB
Source Map (JSON)
{"version":3,"file":"FluidGradient.mjs","names":[],"sources":["../../../src/awesome/FluidGradient/FluidGradient.tsx"],"sourcesContent":["'use client';\n\nimport { cx, useThemeMode } from 'antd-style';\nimport { type CSSProperties, memo, useEffect, useRef, useState } from 'react';\n\nimport { LANDING_PALETTE_DARK, LANDING_PALETTE_LIGHT } from '@/awesome/landingTokens';\n\nimport { styles } from './style';\nimport type { FluidGradientProps } from './type';\n\nconst VERTEX_SHADER = `#version 300 es\nin vec2 aPos;\nvoid main() {\n gl_Position = vec4(aPos, 0.0, 1.0);\n}`;\n\nconst FRAGMENT_SHADER = `#version 300 es\nprecision highp float;\n\nuniform vec2 uRes;\nuniform float uTime;\nuniform vec3 uStopA;\nuniform vec3 uStopB;\nuniform vec3 uStopC;\nuniform float uIntensity;\n\nout vec4 fragColor;\n\nfloat hash(vec2 p) {\n p = fract(p * vec2(234.34, 435.345));\n p += dot(p, p + 34.23);\n return fract(p.x * p.y);\n}\n\nfloat noise(vec2 p) {\n vec2 i = floor(p);\n vec2 f = fract(p);\n vec2 u = f * f * (3.0 - 2.0 * f);\n float a = hash(i);\n float b = hash(i + vec2(1.0, 0.0));\n float c = hash(i + vec2(0.0, 1.0));\n float d = hash(i + vec2(1.0, 1.0));\n return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);\n}\n\nfloat fbm(vec2 p) {\n float value = 0.0;\n float amplitude = 0.5;\n for (int i = 0; i < 4; i++) {\n value += amplitude * noise(p);\n p = mat2(0.8, 0.6, -0.6, 0.8) * p * 2.0 + vec2(3.7, 1.3);\n amplitude *= 0.5;\n }\n return value;\n}\n\nvoid main() {\n vec2 uv = gl_FragCoord.xy / uRes;\n vec2 p = vec2(uv.x * uRes.x / uRes.y, uv.y);\n\n float t = uTime * 0.05;\n\n vec2 q = vec2(fbm(p * 1.4 + t * 0.35), fbm(p * 1.4 - t * 0.28 + 5.2));\n vec2 r = vec2(\n fbm(p * 1.4 + 2.6 * q + vec2(1.7, 9.2) + t * 0.18),\n fbm(p * 1.4 + 2.6 * q + vec2(8.3, 2.8) - t * 0.12)\n );\n float field = fbm(p * 1.4 + 2.6 * r);\n\n vec3 color = mix(uStopA, uStopB, clamp(field * field * 2.4, 0.0, 1.0));\n color = mix(color, uStopC, clamp(dot(q, vec2(0.7)) - 0.25, 0.0, 1.0));\n\n float fade = smoothstep(0.02, 0.85, uv.y);\n float alpha = fade * fade * uIntensity * smoothstep(0.14, 0.66, field);\n\n fragColor = vec4(color * alpha, alpha);\n}`;\n\nconst hexToRgb = (hex: string): [number, number, number] => {\n const value = hex.replace('#', '');\n const full =\n value.length === 3\n ? value\n .split('')\n .map((char) => char + char)\n .join('')\n : value.slice(0, 6);\n const channel = (offset: number) => Number.parseInt(full.slice(offset, offset + 2), 16) / 255;\n return [channel(0), channel(2), channel(4)];\n};\n\nconst FluidGradient = memo<FluidGradientProps>(\n ({ className, colors, intensity = 0.45, speed = 1, style, ...rest }) => {\n const { isDarkMode } = useThemeMode();\n const canvasRef = useRef<HTMLCanvasElement>(null);\n const [ready, setReady] = useState(false);\n const [canvasEpoch, setCanvasEpoch] = useState(0);\n const palette = colors ?? (isDarkMode ? LANDING_PALETTE_DARK : LANDING_PALETTE_LIGHT);\n const paletteRef = useRef({ colors: palette, intensity });\n const speedRef = useRef(speed);\n const repaintRef = useRef<(() => void) | undefined>(undefined);\n\n useEffect(() => {\n paletteRef.current = { colors: palette, intensity };\n speedRef.current = speed;\n repaintRef.current?.();\n }, [palette[0], palette[1], palette[2], intensity, speed]);\n\n useEffect(() => {\n const canvas = canvasRef.current;\n if (!canvas) return undefined;\n\n const reducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)');\n\n let frameId = 0;\n let inView = true;\n let readySent = false;\n let drawFrame: ((seconds: number) => void) | null = null;\n let refreshPalette: (() => void) | null = null;\n let sizeAndDraw: (() => boolean) | null = null;\n let destroyResources: ((lose: boolean) => void) | null = null;\n\n const buildResources = () => {\n const gl = canvas.getContext('webgl2', {\n alpha: true,\n antialias: false,\n depth: false,\n stencil: false,\n powerPreference: 'low-power',\n });\n if (!gl) return false;\n\n const compileShader = (type: number, source: string) => {\n const shader = gl.createShader(type);\n if (!shader) return null;\n gl.shaderSource(shader, source);\n gl.compileShader(shader);\n if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {\n gl.deleteShader(shader);\n return null;\n }\n return shader;\n };\n\n const vertexShader = compileShader(gl.VERTEX_SHADER, VERTEX_SHADER);\n const fragmentShader = compileShader(gl.FRAGMENT_SHADER, FRAGMENT_SHADER);\n if (!vertexShader || !fragmentShader) return false;\n\n const program = gl.createProgram();\n if (!program) return false;\n gl.attachShader(program, vertexShader);\n gl.attachShader(program, fragmentShader);\n gl.linkProgram(program);\n gl.deleteShader(vertexShader);\n gl.deleteShader(fragmentShader);\n if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {\n gl.deleteProgram(program);\n return false;\n }\n gl.useProgram(program);\n\n const buffer = gl.createBuffer();\n gl.bindBuffer(gl.ARRAY_BUFFER, buffer);\n gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, 3, -1, -1, 3]), gl.STATIC_DRAW);\n const position = gl.getAttribLocation(program, 'aPos');\n gl.enableVertexAttribArray(position);\n gl.vertexAttribPointer(position, 2, gl.FLOAT, false, 0, 0);\n\n const timeLocation = gl.getUniformLocation(program, 'uTime');\n const stopLocations = ['uStopA', 'uStopB', 'uStopC'].map((name) =>\n gl.getUniformLocation(program, name),\n );\n\n const intensityLocation = gl.getUniformLocation(program, 'uIntensity');\n\n refreshPalette = () => {\n const { colors: stops, intensity: peak } = paletteRef.current;\n stops.map(hexToRgb).forEach((stop, index) => {\n const location = stopLocations[index];\n if (location) gl.uniform3f(location, stop[0], stop[1], stop[2]);\n });\n gl.uniform1f(intensityLocation, peak);\n };\n refreshPalette();\n\n drawFrame = (seconds: number) => {\n gl.uniform1f(timeLocation, seconds * speedRef.current);\n gl.drawArrays(gl.TRIANGLES, 0, 3);\n if (!readySent) {\n readySent = true;\n setReady(true);\n }\n };\n\n sizeAndDraw = () => {\n const dpr = Math.min(window.devicePixelRatio || 1, 1.5);\n const width = Math.max(1, Math.round(canvas.clientWidth * dpr));\n const height = Math.max(1, Math.round(canvas.clientHeight * dpr));\n if (canvas.width !== width || canvas.height !== height) {\n canvas.width = width;\n canvas.height = height;\n gl.viewport(0, 0, width, height);\n }\n return canvas.clientWidth > 0 && drawFrame !== null;\n };\n\n destroyResources = (lose: boolean) => {\n gl.deleteBuffer(buffer);\n gl.deleteProgram(program);\n drawFrame = null;\n refreshPalette = null;\n sizeAndDraw = null;\n destroyResources = null;\n if (lose) gl.getExtension('WEBGL_lose_context')?.loseContext();\n };\n\n return true;\n };\n\n if (!buildResources()) return undefined;\n\n const renderOnce = () => {\n if (!sizeAndDraw?.()) return;\n drawFrame?.(performance.now() / 1000);\n };\n\n const syncLoop = (milliseconds: number) => {\n frameId = requestAnimationFrame(syncLoop);\n if (sizeAndDraw?.()) drawFrame?.(milliseconds / 1000);\n };\n\n const updateLoop = () => {\n const shouldAnimate =\n inView && !document.hidden && !reducedMotion.matches && drawFrame !== null;\n\n if (shouldAnimate) {\n if (frameId === 0) frameId = requestAnimationFrame(syncLoop);\n return;\n }\n\n if (frameId !== 0) {\n cancelAnimationFrame(frameId);\n frameId = 0;\n }\n\n if (!document.hidden) renderOnce();\n };\n\n const resizeObserver = new ResizeObserver(() => {\n if (frameId === 0) renderOnce();\n });\n resizeObserver.observe(canvas);\n\n const intersectionObserver = new IntersectionObserver(([entry]) => {\n inView = entry.isIntersecting;\n updateLoop();\n });\n intersectionObserver.observe(canvas);\n\n const handleVisibilityChange = () => updateLoop();\n document.addEventListener('visibilitychange', handleVisibilityChange);\n\n const handleMotionPreferenceChange = () => updateLoop();\n reducedMotion.addEventListener('change', handleMotionPreferenceChange);\n\n repaintRef.current = () => {\n refreshPalette?.();\n if (frameId === 0) renderOnce();\n };\n\n const handleContextLost = (event: Event) => {\n event.preventDefault();\n if (frameId !== 0) {\n cancelAnimationFrame(frameId);\n frameId = 0;\n }\n setCanvasEpoch((value) => value + 1);\n };\n canvas.addEventListener('webglcontextlost', handleContextLost);\n\n updateLoop();\n\n return () => {\n if (frameId !== 0) cancelAnimationFrame(frameId);\n frameId = 0;\n resizeObserver.disconnect();\n intersectionObserver.disconnect();\n repaintRef.current = undefined;\n document.removeEventListener('visibilitychange', handleVisibilityChange);\n reducedMotion.removeEventListener('change', handleMotionPreferenceChange);\n canvas.removeEventListener('webglcontextlost', handleContextLost);\n if (!canvas.isConnected) destroyResources?.(true);\n else destroyResources?.(false);\n };\n }, [canvasEpoch]);\n\n return (\n <div\n aria-hidden\n className={cx(styles.root, className)}\n data-ready={ready ? '' : undefined}\n style={\n {\n '--fluid-stop-a': palette[0],\n '--fluid-stop-b': palette[1],\n '--fluid-stop-c': palette[2],\n ...style,\n } as CSSProperties\n }\n {...rest}\n >\n <canvas key={canvasEpoch} ref={canvasRef} />\n </div>\n );\n },\n);\n\nFluidGradient.displayName = 'FluidGradient';\n\nexport default FluidGradient;\n"],"mappings":"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"}