reactbits-mcp-server
Version:
MCP Server for React Bits - Access 99+ React components with animations, backgrounds, and UI elements
145 lines (124 loc) • 3.67 kB
JSX
import { Renderer, Program, Mesh, Color, Triangle } from "ogl";
import { useEffect, useRef } from "react";
import './Iridescence.css';
const vertexShader = `
attribute vec2 uv;
attribute vec2 position;
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = vec4(position, 0, 1);
}
`;
const fragmentShader = `
precision highp float;
uniform float uTime;
uniform vec3 uColor;
uniform vec3 uResolution;
uniform vec2 uMouse;
uniform float uAmplitude;
uniform float uSpeed;
varying vec2 vUv;
void main() {
float mr = min(uResolution.x, uResolution.y);
vec2 uv = (vUv.xy * 2.0 - 1.0) * uResolution.xy / mr;
uv += (uMouse - vec2(0.5)) * uAmplitude;
float d = -uTime * 0.5 * uSpeed;
float a = 0.0;
for (float i = 0.0; i < 8.0; ++i) {
a += cos(i - d - a * uv.x);
d += sin(uv.y * i + a);
}
d += uTime * 0.5 * uSpeed;
vec3 col = vec3(cos(uv * vec2(d, a)) * 0.6 + 0.4, cos(a + d) * 0.5 + 0.5);
col = cos(col * cos(vec3(d, a, 2.5)) * 0.5 + 0.5) * uColor;
gl_FragColor = vec4(col, 1.0);
}
`;
export default function Iridescence({
color = [1, 1, 1],
speed = 1.0,
amplitude = 0.1,
mouseReact = true,
...rest
}) {
const ctnDom = useRef(null);
const mousePos = useRef({ x: 0.5, y: 0.5 });
useEffect(() => {
if (!ctnDom.current) return;
const ctn = ctnDom.current;
const renderer = new Renderer();
const gl = renderer.gl;
gl.clearColor(1, 1, 1, 1);
let program;
function resize() {
const scale = 1;
renderer.setSize(ctn.offsetWidth * scale, ctn.offsetHeight * scale);
if (program) {
program.uniforms.uResolution.value = new Color(
gl.canvas.width,
gl.canvas.height,
gl.canvas.width / gl.canvas.height
);
}
}
window.addEventListener("resize", resize, false);
resize();
const geometry = new Triangle(gl);
program = new Program(gl, {
vertex: vertexShader,
fragment: fragmentShader,
uniforms: {
uTime: { value: 0 },
uColor: { value: new Color(...color) },
uResolution: {
value: new Color(
gl.canvas.width,
gl.canvas.height,
gl.canvas.width / gl.canvas.height
),
},
uMouse: { value: new Float32Array([mousePos.current.x, mousePos.current.y]) },
uAmplitude: { value: amplitude },
uSpeed: { value: speed },
},
});
const mesh = new Mesh(gl, { geometry, program });
let animateId;
function update(t) {
animateId = requestAnimationFrame(update);
program.uniforms.uTime.value = t * 0.001;
renderer.render({ scene: mesh });
}
animateId = requestAnimationFrame(update);
ctn.appendChild(gl.canvas);
function handleMouseMove(e) {
const rect = ctn.getBoundingClientRect();
const x = (e.clientX - rect.left) / rect.width;
const y = 1.0 - (e.clientY - rect.top) / rect.height;
mousePos.current = { x, y };
program.uniforms.uMouse.value[0] = x;
program.uniforms.uMouse.value[1] = y;
}
if (mouseReact) {
ctn.addEventListener("mousemove", handleMouseMove);
}
return () => {
cancelAnimationFrame(animateId);
window.removeEventListener("resize", resize);
if (mouseReact) {
ctn.removeEventListener("mousemove", handleMouseMove);
}
ctn.removeChild(gl.canvas);
gl.getExtension("WEBGL_lose_context")?.loseContext();
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [color, speed, amplitude, mouseReact]);
return (
<div
ref={ctnDom}
className="iridescence-container"
{...rest}
/>
);
}