Files
www_projectmycelium_com/src/components/ui/text-hover-effect.tsx
sasha-astiadi e5cf6ee362 refactor: remove unused imports and variables
- Removed unused React imports from components (now using named imports only)
- Removed unused cursor state and onMouseMove handler from text-hover-effect
- Removed unused H1 import from HomeBlink
2025-11-12 16:26:51 +01:00

140 lines
3.7 KiB
TypeScript

"use client";
import { useRef, useEffect, useState } from "react";
import { motion, useAnimation } from "motion/react";
export const TextHoverEffect = ({
text,
duration = 6, // loop duration
}: {
text: string;
duration?: number;
}) => {
const svgRef = useRef<SVGSVGElement>(null);
const controls = useAnimation();
const [hovered, setHovered] = useState(false);
// ✅ Animate mask looping automatically
useEffect(() => {
const loop = async () => {
while (true) {
await controls.start({
cx: ["20%", "80%", "50%"],
cy: ["20%", "80%", "50%"],
transition: {
duration,
ease: "easeInOut",
repeat: 0,
},
});
}
};
loop();
}, [controls, duration]);
return (
<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)}
className="select-none"
>
<defs>
{/* ✅ Softer cyan gradient */}
<linearGradient id="textGradient" gradientUnits="userSpaceOnUse">
{hovered ? (
<>
<stop offset="0%" stopColor="#7df3ff" />
<stop offset="40%" stopColor="#4adffa" />
<stop offset="70%" stopColor="#18c5e8" />
<stop offset="100%" stopColor="#0aaecb" />
</>
) : (
<>
<stop offset="0%" stopColor="#7df3ff33" />
<stop offset="100%" stopColor="#0aaecb33" />
</>
)}
</linearGradient>
{/* ✅ Mask with autoplay motion */}
<motion.radialGradient
id="revealMask"
gradientUnits="userSpaceOnUse"
r="45%"
animate={controls}
initial={{ cx: "50%", cy: "50%" }}
>
<stop offset="0%" stopColor="white" />
<stop offset="100%" stopColor="black" />
</motion.radialGradient>
{/* ✅ Glow */}
<filter id="glow">
<feGaussianBlur stdDeviation="3.2" result="blur" />
<feMerge>
<feMergeNode in="blur" />
<feMergeNode in="SourceGraphic" />
</feMerge>
</filter>
<mask id="textMask">
<rect x="0" y="0" width="100%" height="100%" fill="url(#revealMask)" />
</mask>
</defs>
{/* ✅ Background faint stroke */}
<text
x="50%"
y="50%"
textAnchor="middle"
dominantBaseline="middle"
strokeWidth="0.15"
className="fill-transparent stroke-neutral-300 dark:stroke-neutral-800 font-[helvetica] text-7xl font-bold"
style={{ opacity: hovered ? 0.25 : 0.1 }}
>
{text}
</text>
{/* ✅ Line drawing animation always plays too */}
<motion.text
x="50%"
y="50%"
textAnchor="middle"
dominantBaseline="middle"
strokeWidth="0.25"
className="fill-transparent stroke-cyan-300 font-[helvetica] text-7xl font-bold"
initial={{ strokeDashoffset: 600, strokeDasharray: 600 }}
animate={{
strokeDashoffset: 0,
strokeDasharray: 600,
}}
transition={{
duration: 2.2,
ease: "easeInOut",
}}
>
{text}
</motion.text>
{/* ✅ Final filled glowing cyan text (mask reveals it) */}
<text
x="50%"
y="50%"
textAnchor="middle"
dominantBaseline="middle"
stroke="url(#textGradient)"
strokeWidth="1.1"
mask="url(#textMask)"
filter="url(#glow)"
className="font-[helvetica] text-7xl font-bold fill-[url(#textGradient)]"
>
{text}
</text>
</svg>
);
};