Animation » Example Animations » 26. Tracking the cursor

26. Tracking the cursor

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

Code component

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 default function CC_26_Tracking_the_cursor(props) {
    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) {
        x.set(event.pageX)
        y.set(event.pageY)
    }

    return (
        <div
            style={{
                width: 400,
                height: 400,
                ...props.style,
                display: "flex",
                placeItems: "center",
                placeContent: "center",
                perspective: 400,
            }}
            onMouseMove={handleMouse}
        >
            <motion.div
                style={{
                    width: 150,
                    height: 150,
                    borderRadius: 30,
                    backgroundColor: "#fff",
                    rotateX,
                    rotateY,
                }}
            />
        </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 default function CC_26_Tracking_the_cursor(props) {
    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) {
        x.set(event.pageX)
        y.set(event.pageY)
    }

    return (
        <div
            style={{
                width: 400,
                height: 400,
                ...props.style,
                display: "flex",
                placeItems: "center",
                placeContent: "center",
                perspective: 400,
            }}
            onMouseMove={handleMouse}
        >
            <motion.div
                style={{
                    width: 150,
                    height: 150,
                    borderRadius: 30,
                    backgroundColor: "#fff",
                    rotateX,
                    rotateY,
                }}
            />
        </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.

CodeSandbox

The version in the CodeSandbox is a bit different because we can’t know the position of the div in the background up front.

Here, 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()

Code overrides

Actually, we don’t have to use Motion values; we can also save the pointer’s position in a state, or here: a CreateStore() data store.

const useStore = createStore({ rotateX: 0, rotateY: 0 })

export function Background(Component): ComponentType {
    return (props) => {
        const { style, ...rest } = props

        const [store, setStore] = useStore()

        // this Frame is 402 more points to the right
        const xTransform = transform([402, 802], [-45, 45])
        const yTransform = transform([0, 400], [45, -45])

        return (
            <Component
                {...props}
                style={{ ...style, perspective: 400 }}
                onMouseMove={(event) => {
                    setStore({
                        rotateX: yTransform(event.pageY),
                        rotateY: xTransform(event.pageX),
                    })
                }}
            />
        )
    }
}

export function Tracking_the_cursor(Component): ComponentType {
    return (props) => {
        const { style, ...rest } = props

        const [store, setStore] = useStore()

        return (
            <Component
                {...rest}
                style={{
                    ...style,
                    rotateX: store.rotateX,
                    rotateY: store.rotateY,
                }}
            />
        )
    }
}

Also, since we’re not using Motion values, we use transform() instead of useTransform() to change them.

transform()


Join the Framer book mailing list    ( ± 6 emails/year )

GDPR

We use Mailchimp as our marketing platform. By clicking below to subscribe, you acknowledge that your information will be transferred to Mailchimp for processing per their Privacy Policy and Terms.



2 comments on “26. Tracking the cursor”

Leave a Reply