UNPKG

aura-glass

Version:

A comprehensive glassmorphism design system for React applications with 142+ production-ready components

399 lines (349 loc) 12.1 kB
'use client'; import { jsx } from 'react/jsx-runtime'; import { useRef, useState, useEffect } from 'react'; import { cn } from '../../lib/utilsComprehensive.js'; // Vertex shader (common for all variants) const vertexShaderSource = ` attribute vec2 a_position; attribute vec2 a_texCoord; varying vec2 v_texCoord; void main() { gl_Position = vec4(a_position, 0.0, 1.0); v_texCoord = a_texCoord; } `; // Fragment shaders for different glass effects const fragmentShaders = { refraction: ` precision mediump float; uniform sampler2D u_image; uniform vec2 u_resolution; uniform vec2 u_mouse; uniform float u_time; uniform float u_intensity; varying vec2 v_texCoord; void main() { vec2 uv = v_texCoord; vec2 center = vec2(0.5, 0.5); vec2 mouseNorm = u_mouse / u_resolution; // Calculate refraction based on distance from mouse float dist = distance(uv, mouseNorm); float refraction = sin(dist * 10.0 - u_time * 2.0) * 0.02 * u_intensity; // Apply chromatic aberration vec2 rOffset = uv + vec2(refraction, 0.0); vec2 gOffset = uv; vec2 bOffset = uv - vec2(refraction, 0.0); float r = texture2D(u_image, rOffset).r; float g = texture2D(u_image, gOffset).g; float b = texture2D(u_image, bOffset).b; gl_FragColor = vec4(r, g, b, 1.0); } `, dispersion: ` precision mediump float; uniform sampler2D u_image; uniform vec2 u_resolution; uniform float u_time; uniform float u_intensity; varying vec2 v_texCoord; void main() { vec2 uv = v_texCoord; // Rainbow dispersion effect float wave = sin(uv.y * 10.0 + u_time) * 0.01 * u_intensity; vec2 rUV = uv + vec2(wave * 2.0, 0.0); vec2 gUV = uv + vec2(wave, 0.0); vec2 bUV = uv; vec3 color; color.r = texture2D(u_image, rUV).r; color.g = texture2D(u_image, gUV).g; color.b = texture2D(u_image, bUV).b; // Add prismatic highlights float highlight = sin(uv.x * 20.0 + u_time * 2.0) * 0.1; color += vec3(highlight, highlight * 0.5, highlight * 0.3); gl_FragColor = vec4(color, 1.0); } `, frosted: ` precision mediump float; uniform sampler2D u_image; uniform vec2 u_resolution; uniform float u_time; uniform float u_intensity; varying vec2 v_texCoord; float random(vec2 st) { return fract(sin(dot(st.xy, vec2(12.9898, 78.233))) * 43758.5453123); } void main() { vec2 uv = v_texCoord; vec3 color = vec3(0.0); // Frosted glass blur float blurSize = 0.01 * u_intensity; int samples = 9; for(int i = 0; i < samples; i++) { float angle = float(i) / float(samples) * 6.28318; vec2 offset = vec2(cos(angle), sin(angle)) * blurSize; offset *= random(uv + float(i)); color += texture2D(u_image, uv + offset).rgb; } color /= float(samples); // Add frost texture float frost = random(uv * 100.0 + u_time * 0.1) * 0.1; color += vec3(frost); gl_FragColor = vec4(color, 1.0); } `, crystal: ` precision mediump float; uniform sampler2D u_image; uniform vec2 u_resolution; uniform vec2 u_mouse; uniform float u_time; uniform float u_intensity; varying vec2 v_texCoord; void main() { vec2 uv = v_texCoord; vec2 center = vec2(0.5, 0.5); // Crystal facets float angle = atan(uv.y - center.y, uv.x - center.x); float facets = 8.0; angle = floor(angle * facets) / facets; float dist = distance(uv, center); vec2 facetUV = center + vec2(cos(angle), sin(angle)) * dist; // Refraction through crystal vec2 refractUV = mix(uv, facetUV, u_intensity * 0.5); vec3 color = texture2D(u_image, refractUV).rgb; // Add sparkle float sparkle = sin(angle * 20.0 + u_time * 3.0) * 0.2; color += vec3(sparkle); // Inner reflections float reflection = sin(dist * 30.0 - u_time * 2.0) * 0.1; color += vec3(reflection, reflection * 0.8, reflection * 1.2); gl_FragColor = vec4(color, 1.0); } `, prism: ` precision mediump float; uniform sampler2D u_image; uniform vec2 u_resolution; uniform float u_time; uniform float u_intensity; varying vec2 v_texCoord; vec3 hsv2rgb(vec3 c) { vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); } void main() { vec2 uv = v_texCoord; // Prism light splitting float prismAngle = uv.x + sin(uv.y * 10.0 + u_time) * 0.1; // Sample at different wavelengths vec3 color = vec3(0.0); int samples = 7; for(int i = 0; i < samples; i++) { float wavelength = float(i) / float(samples); vec2 offset = vec2(wavelength * 0.02 * u_intensity, 0.0); vec3 sample = texture2D(u_image, uv + offset).rgb; // Apply wavelength-based tinting vec3 tint = hsv2rgb(vec3(wavelength, 0.8, 1.0)); color += sample * tint; } color /= float(samples); // Add rainbow spectrum overlay float spectrum = sin(prismAngle * 20.0) * 0.3; vec3 rainbow = hsv2rgb(vec3(prismAngle, 1.0, spectrum)); color = mix(color, rainbow, 0.2 * u_intensity); gl_FragColor = vec4(color, 1.0); } ` }; function GlassWebGLShader({ className, variant = "refraction", intensity = 1, animated = true, interactive = true, backgroundColor = "transparent" }) { const canvasRef = useRef(null); const glRef = useRef(null); const programRef = useRef(null); const animationRef = useRef(0); const mouseRef = useRef({ x: 0, y: 0 }); const [isSupported, setIsSupported] = useState(true); // Initialize WebGL useEffect(() => { const canvas = canvasRef.current; if (!canvas) return; const gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl"); if (!gl) { setIsSupported(false); console.warn("WebGL not supported"); return; } glRef.current = gl; // Create shaders const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexShaderSource); const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentShaders[variant]); if (!vertexShader || !fragmentShader) { setIsSupported(false); return; } // Create program const program = createProgram(gl, vertexShader, fragmentShader); if (!program) { setIsSupported(false); return; } programRef.current = program; // Set up geometry setupGeometry(gl, program); // Set up texture setupTexture(gl); // Start render loop if (animated) { render(); } return () => { if (animationRef.current) { cancelAnimationFrame(animationRef.current); } }; }, [variant, animated]); // Handle mouse interaction useEffect(() => { if (!interactive) return; const handleMouseMove = e => { const canvas = canvasRef.current; if (!canvas) return; const rect = canvas.getBoundingClientRect(); mouseRef.current = { x: e.clientX - rect.left, y: e.clientY - rect.top }; }; window.addEventListener("mousemove", handleMouseMove); return () => window.removeEventListener("mousemove", handleMouseMove); }, [interactive]); // Create shader function createShader(gl, type, source) { const shader = gl.createShader(type); if (!shader) return null; gl.shaderSource(shader, source); gl.compileShader(shader); if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { console.error("Shader compilation error:", gl.getShaderInfoLog(shader)); gl.deleteShader(shader); return null; } return shader; } // Create program function createProgram(gl, vertexShader, fragmentShader) { const program = gl.createProgram(); if (!program) return null; gl.attachShader(program, vertexShader); gl.attachShader(program, fragmentShader); gl.linkProgram(program); if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { console.error("Program linking error:", gl.getProgramInfoLog(program)); gl.deleteProgram(program); return null; } return program; } // Set up geometry function setupGeometry(gl, program) { // Positions const positions = new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]); const positionBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW); const positionLocation = gl.getAttribLocation(program, "a_position"); gl.enableVertexAttribArray(positionLocation); gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0); // Texture coordinates const texCoords = new Float32Array([0, 1, 1, 1, 0, 0, 1, 0]); const texCoordBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer); gl.bufferData(gl.ARRAY_BUFFER, texCoords, gl.STATIC_DRAW); const texCoordLocation = gl.getAttribLocation(program, "a_texCoord"); gl.enableVertexAttribArray(texCoordLocation); gl.vertexAttribPointer(texCoordLocation, 2, gl.FLOAT, false, 0, 0); } // Set up texture function setupTexture(gl) { const texture = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, texture); // Create gradient texture const width = 256; const height = 256; const pixels = new Uint8Array(width * height * 4); for (let y = 0; y < height; y++) { for (let x = 0; x < width; x++) { const i = (y * width + x) * 4; pixels[i] = x / width * 255; // R pixels[i + 1] = y / height * 255; // G pixels[i + 2] = 128; // B pixels[i + 3] = 255; // A } } gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, pixels); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); } // Render loop function render() { const gl = glRef.current; const program = programRef.current; const canvas = canvasRef.current; if (!gl || !program || !canvas) return; // Clear canvas gl.clearColor(0, 0, 0, 0); gl.clear(gl.COLOR_BUFFER_BIT); // Use program gl.useProgram(program); // Set uniforms const resolutionLocation = gl.getUniformLocation(program, "u_resolution"); gl.uniform2f(resolutionLocation, canvas.width, canvas.height); const mouseLocation = gl.getUniformLocation(program, "u_mouse"); gl.uniform2f(mouseLocation, mouseRef.current.x, mouseRef.current.y); const timeLocation = gl.getUniformLocation(program, "u_time"); gl.uniform1f(timeLocation, performance.now() / 1000); const intensityLocation = gl.getUniformLocation(program, "u_intensity"); gl.uniform1f(intensityLocation, intensity); // Draw gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); // Continue animation if (animated) { animationRef.current = requestAnimationFrame(render); } } if (!isSupported) { return jsx("div", { "data-glass-component": true, className: cn("OptimizedGlass intensity={0.2} glassBlur={6} glass-p-4", className), children: jsx("p", { className: "glass-text-secondary", children: "WebGL not supported" }) }); } return jsx("div", { className: cn("relative overflow-hidden", className), style: { backgroundColor }, children: jsx("canvas", { ref: canvasRef, className: 'absolute inset-0 glass-w-full glass-h-full', width: 800, height: 600 }) }); } export { GlassWebGLShader }; //# sourceMappingURL=GlassWebGLShader.js.map