reactbits-mcp-server
Version:
MCP Server for React Bits - Access 99+ React components with animations, backgrounds, and UI elements
193 lines (166 loc) • 5.61 kB
JSX
import { useEffect, useRef } from "react";
import { gsap } from "gsap";
const lerp = (a, b, n) => (1 - n) * a + n * b;
const getMousePos = (e, container) => {
if (container) {
const bounds = container.getBoundingClientRect();
return {
x: e.clientX - bounds.left,
y: e.clientY - bounds.top,
};
}
return { x: e.clientX, y: e.clientY };
};
const Crosshair = ({ color = "white", containerRef = null }) => {
const cursorRef = useRef(null);
const lineHorizontalRef = useRef(null);
const lineVerticalRef = useRef(null);
const filterXRef = useRef(null);
const filterYRef = useRef(null);
let mouse = { x: 0, y: 0 };
useEffect(() => {
const handleMouseMove = (ev) => {
// eslint-disable-next-line react-hooks/exhaustive-deps
mouse = getMousePos(ev, containerRef?.current);
if (containerRef?.current) {
const bounds = containerRef.current.getBoundingClientRect();
if (
ev.clientX < bounds.left ||
ev.clientX > bounds.right ||
ev.clientY < bounds.top ||
ev.clientY > bounds.bottom
) {
gsap.to([lineHorizontalRef.current, lineVerticalRef.current], {
opacity: 0,
});
} else {
gsap.to([lineHorizontalRef.current, lineVerticalRef.current], {
opacity: 1,
});
}
}
};
const target = containerRef?.current || window;
target.addEventListener("mousemove", handleMouseMove);
const renderedStyles = {
tx: { previous: 0, current: 0, amt: 0.15 },
ty: { previous: 0, current: 0, amt: 0.15 },
};
gsap.set([lineHorizontalRef.current, lineVerticalRef.current], {
opacity: 0,
});
const onMouseMove = () => {
renderedStyles.tx.previous = renderedStyles.tx.current = mouse.x;
renderedStyles.ty.previous = renderedStyles.ty.current = mouse.y;
gsap.to([lineHorizontalRef.current, lineVerticalRef.current], {
duration: 0.9,
ease: "Power3.easeOut",
opacity: 1,
});
requestAnimationFrame(render);
target.removeEventListener("mousemove", onMouseMove);
};
target.addEventListener("mousemove", onMouseMove);
const primitiveValues = { turbulence: 0 };
const tl = gsap
.timeline({
paused: true,
onStart: () => {
lineHorizontalRef.current.style.filter = `url(#filter-noise-x)`;
lineVerticalRef.current.style.filter = `url(#filter-noise-y)`;
},
onUpdate: () => {
filterXRef.current.setAttribute(
"baseFrequency",
primitiveValues.turbulence
);
filterYRef.current.setAttribute(
"baseFrequency",
primitiveValues.turbulence
);
},
onComplete: () => {
lineHorizontalRef.current.style.filter = lineVerticalRef.current.style.filter = "none";
},
})
.to(primitiveValues, {
duration: 0.5,
ease: "power1",
startAt: { turbulence: 1 },
turbulence: 0,
});
const enter = () => tl.restart();
const leave = () => tl.progress(1).kill();
const render = () => {
renderedStyles.tx.current = mouse.x;
renderedStyles.ty.current = mouse.y;
for (const key in renderedStyles) {
renderedStyles[key].previous = lerp(
renderedStyles[key].previous,
renderedStyles[key].current,
renderedStyles[key].amt
);
}
gsap.set(lineVerticalRef.current, { x: renderedStyles.tx.previous });
gsap.set(lineHorizontalRef.current, { y: renderedStyles.ty.previous });
requestAnimationFrame(render);
};
const links = containerRef?.current
? containerRef.current.querySelectorAll("a")
: document.querySelectorAll("a");
links.forEach((link) => {
link.addEventListener("mouseenter", enter);
link.addEventListener("mouseleave", leave);
});
return () => {
target.removeEventListener("mousemove", handleMouseMove);
target.removeEventListener("mousemove", onMouseMove);
links.forEach((link) => {
link.removeEventListener("mouseenter", enter);
link.removeEventListener("mouseleave", leave);
});
};
}, [containerRef]);
return (
<div
ref={cursorRef}
className={`${
containerRef ? "absolute" : "fixed"
} top-0 left-0 w-full h-full pointer-events-none z-[10000]`}
>
<svg className="absolute top-0 left-0 w-full h-full">
<defs>
<filter id="filter-noise-x">
<feTurbulence
type="fractalNoise"
baseFrequency="0.000001"
numOctaves="1"
ref={filterXRef}
/>
<feDisplacementMap in="SourceGraphic" scale="40" />
</filter>
<filter id="filter-noise-y">
<feTurbulence
type="fractalNoise"
baseFrequency="0.000001"
numOctaves="1"
ref={filterYRef}
/>
<feDisplacementMap in="SourceGraphic" scale="40" />
</filter>
</defs>
</svg>
<div
ref={lineHorizontalRef}
className={`absolute w-full h-px pointer-events-none opacity-0 transform translate-y-1/2`}
style={{ background: color }}
></div>
<div
ref={lineVerticalRef}
className={`absolute h-full w-px pointer-events-none opacity-0 transform translate-x-1/2`}
style={{ background: color }}
></div>
</div>
);
};
export default Crosshair;