xlibs-components
Version:
Modern React component library with Aceternity, MagicUI, and ShadCN components for building beautiful web applications
29 lines (28 loc) • 3.25 kB
JavaScript
"use client";
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
import { useRef, useEffect, useState } from "react";
import { motion } from "framer-motion";
export const TextHoverEffect = ({ text, duration, }) => {
const svgRef = useRef(null);
const [cursor, setCursor] = useState({ x: 0, y: 0 });
const [hovered, setHovered] = useState(false);
const [maskPosition, setMaskPosition] = useState({ cx: "50%", cy: "50%" });
useEffect(() => {
if (svgRef.current && cursor.x !== null && cursor.y !== null) {
const svgRect = svgRef.current.getBoundingClientRect();
const cxPercentage = ((cursor.x - svgRect.left) / svgRect.width) * 100;
const cyPercentage = ((cursor.y - svgRect.top) / svgRect.height) * 100;
setMaskPosition({
cx: `${cxPercentage}%`,
cy: `${cyPercentage}%`,
});
}
}, [cursor]);
return (_jsxs("svg", { ref: svgRef, width: "100%", height: "100%", viewBox: "0 0 300 100", xmlns: "http://www.w3.org/2000/svg", onMouseEnter: () => setHovered(true), onMouseLeave: () => setHovered(false), onMouseMove: (e) => setCursor({ x: e.clientX, y: e.clientY }), className: "select-none", children: [_jsxs("defs", { children: [_jsx("linearGradient", { id: "textGradient", gradientUnits: "userSpaceOnUse", cx: "50%", cy: "50%", r: "25%", children: hovered && (_jsxs(_Fragment, { children: [_jsx("stop", { offset: "0%", stopColor: "#eab308" }), _jsx("stop", { offset: "25%", stopColor: "#ef4444" }), _jsx("stop", { offset: "50%", stopColor: "#3b82f6" }), _jsx("stop", { offset: "75%", stopColor: "#06b6d4" }), _jsx("stop", { offset: "100%", stopColor: "#8b5cf6" })] })) }), _jsxs(motion.radialGradient, { id: "revealMask", gradientUnits: "userSpaceOnUse", r: "20%", initial: { cx: "50%", cy: "50%" }, animate: maskPosition, transition: { duration: duration ?? 0, ease: "easeOut" }, children: [_jsx("stop", { offset: "0%", stopColor: "white" }), _jsx("stop", { offset: "100%", stopColor: "black" })] }), _jsx("mask", { id: "textMask", children: _jsx("rect", { x: "0", y: "0", width: "100%", height: "100%", fill: "url(#revealMask)" }) })] }), _jsx("text", { x: "50%", y: "50%", textAnchor: "middle", dominantBaseline: "middle", strokeWidth: "0.3", className: "fill-transparent stroke-neutral-200 font-[helvetica] text-7xl font-bold dark:stroke-neutral-800", style: { opacity: hovered ? 0.7 : 0 }, children: text }), _jsx(motion.text, { x: "50%", y: "50%", textAnchor: "middle", dominantBaseline: "middle", strokeWidth: "0.3", className: "fill-transparent stroke-neutral-200 font-[helvetica] text-7xl font-bold dark:stroke-neutral-800", initial: { strokeDashoffset: 1000, strokeDasharray: 1000 }, animate: {
strokeDashoffset: 0,
strokeDasharray: 1000,
}, transition: {
duration: 4,
ease: "easeInOut",
}, children: text }), _jsx("text", { x: "50%", y: "50%", textAnchor: "middle", dominantBaseline: "middle", stroke: "url(#textGradient)", strokeWidth: "0.3", mask: "url(#textMask)", className: "fill-transparent font-[helvetica] text-7xl font-bold", children: text })] }));
};