So I have this component which aims to add a sort of spotlight effect to a paragraph/text. I've used a radial gradient
as the background image and background clip text
to make the effect.
I wanted to animate it so the spotlight follows the cursor so I added the handleMouseMove
function which sets the useMotionValues
. Then I use the values in the style to adjust the X and Y of the gradient.
Everything seems to be working, however I'm stuck on making it animate smoothly. For example, when the cursor is not hovering anymore I wanted to move the spotlight back to the middle. I made the handleMouseLeave
function which does move it back to the middle, but it snaps it instead of animating it.
Some pics of the component (colors are changed)
In this second image I was hovering over the right end of the text, so the middle of the gradient (black) moved there
Code
const GradientBackgroundParagraph = ({ text }) => {
let mouseX = useMotionValue(50);
let mouseY = useMotionValue(50);
const handleMouseMove = ({ clientX, clientY, currentTarget }) => {
const { left, top, width, height } = currentTarget.getBoundingClientRect();
mouseX.set(((clientX - left) / width) * 100);
mouseY.set(((clientY - top) / height) * 100);
};
const handleMouseLeave = () => {
mouseX.set(50);
mouseY.set(50);
};
return (
<motion.p
onMouseMove={handleMouseMove}
onMouseLeave={handleMouseLeave}
style={{
backgroundImage: useMotionTemplate`radial-gradient(circle at ${mouseX}% ${mouseY}%, rgb(var(--foreground-rgb)), red)`,
backgroundClip: "text",
}}
className="text-2xl bg-clip-text text-transparent cursor-default transition-all"
>
{text}
</motion.p>
);
};
Update:
So for anyone that may have the same question in the future, I figured it out and it was quite simple. All I did was change useMotionValue
to useSpring
. Everything else was kept the same and the effect is now animated.
Here is the final code: (Have done some cleanup, but the main part is the useSpring
at the top)
const GradientBackgroundParagraph = ({ text }) => {
const mouseX = useMotionValue(50);
const mouseY = useMotionValue(50);
const mouseXSpring = useSpring(mouseX);
const mouseYSpring = useSpring(mouseY);
const handleMouseMove = ({ clientX, clientY, currentTarget }) => {
const { left, top, width, height } = currentTarget.getBoundingClientRect();
mouseX.set(((clientX - left) / width) * 100);
mouseY.set(((clientY - top) / height) * 100);
};
const handleMouseLeave = () => {
mouseX.set(50);
mouseY.set(50);
};
const spotlightBackground = useMotionTemplate`radial-gradient(circle at ${mouseXSpring}% ${mouseYSpring}%, rgb(var(--foreground-rgb)), transparent 150%)`;
return (
<motion.p
onMouseMove={handleMouseMove}
onMouseLeave={handleMouseLeave}
style={{
backgroundImage: spotlightBackground,
backgroundClip: "text",
}}
className="text-2xl bg-clip-text text-transparent cursor-default"
>
{text}
</motion.p>
);
};