xlibs-components
Version:
Modern React component library with Aceternity, MagicUI, and ShadCN components for building beautiful web applications
101 lines (100 loc) • 5.99 kB
JavaScript
"use client";
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
import { memo, useCallback, useEffect, useRef } from "react";
import { cn } from '../../lib/utils.js';
import { animate } from "framer-motion";
const GlowingEffect = memo(({ blur = 0, inactiveZone = 0.7, proximity = 0, spread = 20, variant = "default", glow = false, className, movementDuration = 2, borderWidth = 1, disabled = true, }) => {
const containerRef = useRef(null);
const lastPosition = useRef({ x: 0, y: 0 });
const animationFrameRef = useRef(0);
const handleMove = useCallback((e) => {
if (!containerRef.current)
return;
if (animationFrameRef.current) {
cancelAnimationFrame(animationFrameRef.current);
}
animationFrameRef.current = requestAnimationFrame(() => {
const element = containerRef.current;
if (!element)
return;
const { left, top, width, height } = element.getBoundingClientRect();
const mouseX = e?.x ?? lastPosition.current.x;
const mouseY = e?.y ?? lastPosition.current.y;
if (e) {
lastPosition.current = { x: mouseX, y: mouseY };
}
const center = [left + width * 0.5, top + height * 0.5];
const distanceFromCenter = Math.hypot(mouseX - center[0], mouseY - center[1]);
const inactiveRadius = 0.5 * Math.min(width, height) * inactiveZone;
if (distanceFromCenter < inactiveRadius) {
element.style.setProperty("--active", "0");
return;
}
const isActive = mouseX > left - proximity &&
mouseX < left + width + proximity &&
mouseY > top - proximity &&
mouseY < top + height + proximity;
element.style.setProperty("--active", isActive ? "1" : "0");
if (!isActive)
return;
const currentAngle = parseFloat(element.style.getPropertyValue("--start")) || 0;
let targetAngle = (180 * Math.atan2(mouseY - center[1], mouseX - center[0])) /
Math.PI +
90;
const angleDiff = ((targetAngle - currentAngle + 180) % 360) - 180;
const newAngle = currentAngle + angleDiff;
animate(currentAngle, newAngle, {
duration: movementDuration,
ease: [0.16, 1, 0.3, 1],
onUpdate: (value) => {
element.style.setProperty("--start", String(value));
},
});
});
}, [inactiveZone, proximity, movementDuration]);
useEffect(() => {
if (disabled)
return;
const handleScroll = () => handleMove();
const handlePointerMove = (e) => handleMove(e);
window.addEventListener("scroll", handleScroll, { passive: true });
document.body.addEventListener("pointermove", handlePointerMove, {
passive: true,
});
return () => {
if (animationFrameRef.current) {
cancelAnimationFrame(animationFrameRef.current);
}
window.removeEventListener("scroll", handleScroll);
document.body.removeEventListener("pointermove", handlePointerMove);
};
}, [handleMove, disabled]);
return (_jsxs(_Fragment, { children: [_jsx("div", { className: cn("pointer-events-none absolute -inset-px hidden rounded-[inherit] border opacity-0 transition-opacity", glow && "opacity-100", variant === "white" && "border-white", disabled && "!block") }), _jsx("div", { ref: containerRef, style: {
"--blur": `${blur}px`,
"--spread": spread,
"--start": "0",
"--active": "0",
"--glowingeffect-border-width": `${borderWidth}px`,
"--repeating-conic-gradient-times": "5",
"--gradient": variant === "white"
? `repeating-conic-gradient(
from 236.84deg at 50% 50%,
var(--black),
var(--black) calc(25% / var(--repeating-conic-gradient-times))
)`
: `radial-gradient(circle, #dd7bbb 10%, #dd7bbb00 20%),
radial-gradient(circle at 40% 40%, #d79f1e 5%, #d79f1e00 15%),
radial-gradient(circle at 60% 60%, #5a922c 10%, #5a922c00 20%),
radial-gradient(circle at 40% 60%, #4c7894 10%, #4c789400 20%),
repeating-conic-gradient(
from 236.84deg at 50% 50%,
#dd7bbb 0%,
#d79f1e calc(25% / var(--repeating-conic-gradient-times)),
#5a922c calc(50% / var(--repeating-conic-gradient-times)),
#4c7894 calc(75% / var(--repeating-conic-gradient-times)),
#dd7bbb calc(100% / var(--repeating-conic-gradient-times))
)`,
}, className: cn("pointer-events-none absolute inset-0 rounded-[inherit] opacity-100 transition-opacity", glow && "opacity-100", blur > 0 && "blur-[var(--blur)] ", className, disabled && "!hidden"), children: _jsx("div", { className: cn("glow", "rounded-[inherit]", 'after:content-[""] after:rounded-[inherit] after:absolute after:inset-[calc(-1*var(--glowingeffect-border-width))]', "after:[border:var(--glowingeffect-border-width)_solid_transparent]", "after:[background:var(--gradient)] after:[background-attachment:fixed]", "after:opacity-[var(--active)] after:transition-opacity after:duration-300", "after:[mask-clip:padding-box,border-box]", "after:[mask-composite:intersect]", "after:[mask-image:linear-gradient(#0000,#0000),conic-gradient(from_calc((var(--start)-var(--spread))*1deg),#00000000_0deg,#fff,#00000000_calc(var(--spread)*2deg))]") }) })] }));
});
GlowingEffect.displayName = "GlowingEffect";
export { GlowingEffect };