so I am trying to recreate those cool custom cursors you see on hover with framer motion. So basically I have a div and whenever you hover over the div, the cursor should disappear and instead a medium sized circle should appear and follow the mouse. I got it to work somewhat but the movement of the circle is extremely jittery and it seems to be trying to return to the corner.
Here's the component:
"use client";
import { useState } from "react";
import { motion } from "framer-motion";
type Position = {
x: number;
y: number;
};
function BigBlockImage() {
const [isHovering, setIsHovering] = useState(false);
const [position, setPosition] = useState<Position>({ x: 0, y: 0 });
return (
<div
className="w-full aspect-video bg-placeholder relative cursor-none"
onMouseEnter={(e) => {
setIsHovering(true);
}}
onMouseLeave={(e) => {
setIsHovering(false);
}}
onMouseMove={(e) => {
const rect = e.target.getBoundingClientRect();
const x = e.clientX - rect.left - 57;
const y = e.clientY - rect.top - 57;
console.log(x, y);
setPosition({
x,
y,
});
}}
>
{isHovering && (
<motion.div
animate={{ x: position.x, y: position.y }}
transition={{ type: "tween", ease: "backOut" }}
className="absolute w-[114px] aspect-square bg-accent rounded-full flex justify-center items-center uppercase text-primary pt-1"
>
VIEW
</motion.div>
)}
</div>
);
}
export default BigBlockImage;
I am not sure if it's an issue with setting the state so many times in such a short time or am I doing it the wrong way. Any help is appreciated. Thank you
Use e.currentTarget
instead of e.target
.
<div
onMouseMove={(e) => {
const rect = e.currentTarget.getBoundingClientRect();
...
}}
>
...
</div>
What's happening is a result of Event Bubbling. When you move your mouse over the BigBlockImage, the custom cursor div moves to your cursor's position. This makes it so that your cursor is now over the custom cursor div. When you move your mouse again, the onMouseMove
event handler will bubble up from the custom cursor div to the main wrapping div and fire the onMouseMove
with the custom cursor div as the target
. This then causes the rect
to be the measurements of the custom cursor div instead of the main div like you want.
The currentTarget
property "identifies the element to which the event handler has been attached", whereas the target
is "a reference to the object onto which the event was dispatched."
I created this working example that has the fix as well as an example of what the issue was.