Search

23. Tracking the cursor

23. Tracking the cursor

You’re not limited to Framer Motion’s events; you can also use common events (in React: synthetic events) like, for example, onMouseMove().

⚡️ Some Examples – 23 – Tracking the cursor

open in CodeSandbox

This one is comparable to Drag: 3D transform. Here, we’re also tracking x and y movements with Motion values, only this time, not directly.

We have x and y Motion values, but they’re not connected to any element. Instead, they are updated using set() in the handleMouse() event handler triggered by the background’s onMouseMove() event (an HTML event).

export function Example() {
    const x = useMotionValue(200);
    const y = useMotionValue(200);

    const rotateX = useTransform(y, [0, 400], [45, -45]);
    const rotateY = useTransform(x, [0, 400], [-45, 45]);

    function handleMouse(event) {
        const rect = event.currentTarget.getBoundingClientRect();

        x.set(event.clientX - rect.left);
        y.set(event.clientY - rect.top);
    }

    return (
        <motion.div
            style={{
                width: 400,
                height: 400,
                display: "flex",
                placeItems: "center",
                placeContent: "center",
                borderRadius: 30,
                backgroundColor: "rgba(255, 255, 255, 0.05)",
                perspective: 400
            }}
            onMouseMove={handleMouse}
        >
            <motion.div
                style={{
                    width: 150,
                    height: 150,
                    borderRadius: 30,
                    backgroundColor: "#fff",
                    rotateX,
                    rotateY
                }}
            />
        </motion.div>
    );
}

set()
SyntheticEvent, UI Events
onMouseMove()

The x and y Motion values are then transformed to rotateX and rotateY 3D rotation values, which we pass to the box in the center.

export function Example() {
    const x = useMotionValue(200);
    const y = useMotionValue(200);

    const rotateX = useTransform(y, [0, 400], [45, -45]);
    const rotateY = useTransform(x, [0, 400], [-45, 45]);

    function handleMouse(event) {
        const rect = event.currentTarget.getBoundingClientRect();

        x.set(event.clientX - rect.left);
        y.set(event.clientY - rect.top);
    }

    return (
        <motion.div
            style={{
                width: 400,
                height: 400,
                display: "flex",
                placeItems: "center",
                placeContent: "center",
                borderRadius: 30,
                backgroundColor: "rgba(255, 255, 255, 0.05)",
                perspective: 400
            }}
            onMouseMove={handleMouse}
        >
            <motion.div
                style={{
                    width: 150,
                    height: 150,
                    borderRadius: 30,
                    backgroundColor: "#fff",
                    rotateX,
                    rotateY
                }}
            />
        </motion.div>
    );
}

Here, the same happens as in the earlier example:

  • x changes the y-rotation: rotateY,
  • and y changes the x-rotation: rotateX.

Also, the background got a bit of perspective because otherwise, you wouldn’t see any 3D effect.

Getting the pointer’s position

To calculate the rotateX and rotateY correctly, we need to know the position of the pointer inside the background div.

So we grab the currentTarget from the event, use getBoundingClientRect() to get the div’s position (from the left and top), and then detract those from the absolute position of the pointer (clientX and clientY) to get the pointer’s position inside the div.

export function Example() {
    const x = useMotionValue(200);
    const y = useMotionValue(200);

    const rotateX = useTransform(y, [0, 400], [45, -45]);
    const rotateY = useTransform(x, [0, 400], [-45, 45]);

    function handleMouse(event) {
        const rect = event.currentTarget.getBoundingClientRect();

        x.set(event.clientX - rect.left);
        y.set(event.clientY - rect.top);
    }

    return (
        <motion.div
            style={{
                width: 400,
                height: 400,
                display: "flex",
                placeItems: "center",
                placeContent: "center",
                borderRadius: 30,
                backgroundColor: "rgba(255, 255, 255, 0.05)",
                perspective: 400
            }}
            onMouseMove={handleMouse}
        >
            <motion.div
                style={{
                    width: 150,
                    height: 150,
                    borderRadius: 30,
                    backgroundColor: "#fff",
                    rotateX,
                    rotateY
                }}
            />
        </motion.div>
    );
}

getBoundingClientRect()

Leave a Reply

plugins premium WordPress
Scroll to Top