"use client";
import { useRef, useState } from "react";
import { motion, useMotionValue, useSpring } from "framer-motion";
interface MaskCursorEffectProps {
revealText?: string;
baseText?: string;
className?: string;
}
export function MaskCursorEffect({
revealText = "✦ Hover to reveal the magic beneath the surface ✦",
baseText = "✦ Move your cursor across the text to see what is hidden ✦",
className = "",
}: MaskCursorEffectProps) {
const containerRef = useRef<HTMLDivElement>(null);
const [active, setActive] = useState(false);
const rawX = useMotionValue(0);
const rawY = useMotionValue(0);
const x = useSpring(rawX, { stiffness: 300, damping: 30 });
const y = useSpring(rawY, { stiffness: 300, damping: 30 });
const handleMove = (e: React.MouseEvent<HTMLDivElement>) => {
const rect = containerRef.current?.getBoundingClientRect();
if (!rect) return;
rawX.set(e.clientX - rect.left);
rawY.set(e.clientY - rect.top);
};
return (
<div
ref={containerRef}
onMouseMove={handleMove}
onMouseEnter={() => setActive(true)}
onMouseLeave={() => setActive(false)}
className={`relative overflow-hidden cursor-none select-none ${className}`}
>
{/* Base dark layer */}
<p className="text-zinc-700 text-xl font-bold text-center leading-relaxed px-8 py-12">
{baseText}
</p>
{/* Masked reveal layer */}
<motion.div
className="absolute inset-0 flex items-center justify-center"
style={{
WebkitMaskImage: `radial-gradient(circle 100px at ${x.get()}px ${y.get()}px, black 40%, transparent 80%)`,
maskImage: `radial-gradient(circle 100px at ${x.get()}px ${y.get()}px, black 40%, transparent 80%)`,
opacity: active ? 1 : 0,
}}
>
<p className="text-white text-xl font-bold text-center leading-relaxed px-8 py-12">
{revealText}
</p>
</motion.div>
</div>
);
}