UNPKG

reactbits-mcp-server

Version:

MCP Server for React Bits - Access 99+ React components with animations, backgrounds, and UI elements

173 lines (152 loc) 5.26 kB
import { useRef, useEffect } from "react"; import { Renderer, Program, Mesh, Triangle } from "ogl"; export const LiquidChrome = ({ baseColor = [0.1, 0.1, 0.1], speed = 0.2, amplitude = 0.5, frequencyX = 3, frequencyY = 2, interactive = true, ...props }) => { const containerRef = useRef(null); useEffect(() => { if (!containerRef.current) return; const container = containerRef.current; const renderer = new Renderer({ antialias: true }); const gl = renderer.gl; gl.clearColor(1, 1, 1, 1); const vertexShader = ` attribute vec2 position; attribute vec2 uv; varying vec2 vUv; void main() { vUv = uv; gl_Position = vec4(position, 0.0, 1.0); } `; const fragmentShader = ` precision highp float; uniform float uTime; uniform vec3 uResolution; uniform vec3 uBaseColor; uniform float uAmplitude; uniform float uFrequencyX; uniform float uFrequencyY; uniform vec2 uMouse; varying vec2 vUv; vec4 renderImage(vec2 uvCoord) { vec2 fragCoord = uvCoord * uResolution.xy; vec2 uv = (2.0 * fragCoord - uResolution.xy) / min(uResolution.x, uResolution.y); for (float i = 1.0; i < 10.0; i++){ uv.x += uAmplitude / i * cos(i * uFrequencyX * uv.y + uTime + uMouse.x * 3.14159); uv.y += uAmplitude / i * cos(i * uFrequencyY * uv.x + uTime + uMouse.y * 3.14159); } vec2 diff = (uvCoord - uMouse); float dist = length(diff); float falloff = exp(-dist * 20.0); float ripple = sin(10.0 * dist - uTime * 2.0) * 0.03; uv += (diff / (dist + 0.0001)) * ripple * falloff; vec3 color = uBaseColor / abs(sin(uTime - uv.y - uv.x)); return vec4(color, 1.0); } void main() { vec4 col = vec4(0.0); int samples = 0; for (int i = -1; i <= 1; i++){ for (int j = -1; j <= 1; j++){ vec2 offset = vec2(float(i), float(j)) * (1.0 / min(uResolution.x, uResolution.y)); col += renderImage(vUv + offset); samples++; } } gl_FragColor = col / float(samples); } `; const geometry = new Triangle(gl); const program = new Program(gl, { vertex: vertexShader, fragment: fragmentShader, uniforms: { uTime: { value: 0 }, uResolution: { value: new Float32Array([ gl.canvas.width, gl.canvas.height, gl.canvas.width / gl.canvas.height, ]), }, uBaseColor: { value: new Float32Array(baseColor) }, uAmplitude: { value: amplitude }, uFrequencyX: { value: frequencyX }, uFrequencyY: { value: frequencyY }, uMouse: { value: new Float32Array([0, 0]) }, }, }); const mesh = new Mesh(gl, { geometry, program }); function resize() { const scale = 1; renderer.setSize( container.offsetWidth * scale, container.offsetHeight * scale ); const resUniform = program.uniforms.uResolution.value; resUniform[0] = gl.canvas.width; resUniform[1] = gl.canvas.height; resUniform[2] = gl.canvas.width / gl.canvas.height; } window.addEventListener("resize", resize); resize(); function handleMouseMove(event) { const rect = container.getBoundingClientRect(); const x = (event.clientX - rect.left) / rect.width; const y = 1 - (event.clientY - rect.top) / rect.height; const mouseUniform = program.uniforms.uMouse.value; mouseUniform[0] = x; mouseUniform[1] = y; } function handleTouchMove(event) { if (event.touches.length > 0) { const touch = event.touches[0]; const rect = container.getBoundingClientRect(); const x = (touch.clientX - rect.left) / rect.width; const y = 1 - (touch.clientY - rect.top) / rect.height; const mouseUniform = program.uniforms.uMouse.value; mouseUniform[0] = x; mouseUniform[1] = y; } } if (interactive) { container.addEventListener("mousemove", handleMouseMove); container.addEventListener("touchmove", handleTouchMove); } let animationId; function update(t) { animationId = requestAnimationFrame(update); program.uniforms.uTime.value = t * 0.001 * speed; renderer.render({ scene: mesh }); } animationId = requestAnimationFrame(update); container.appendChild(gl.canvas); return () => { cancelAnimationFrame(animationId); window.removeEventListener("resize", resize); if (interactive) { container.removeEventListener("mousemove", handleMouseMove); container.removeEventListener("touchmove", handleTouchMove); } if (gl.canvas.parentElement) { gl.canvas.parentElement.removeChild(gl.canvas); } gl.getExtension("WEBGL_lose_context")?.loseContext(); }; }, [baseColor, speed, amplitude, frequencyX, frequencyY, interactive]); return ( <div ref={containerRef} className="w-full h-full" {...props} /> ); }; export default LiquidChrome;