Animation » Example Animations » 24. Cursor

24. 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

export function CC24Cursor() {
    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 (
        <Frame
            // Visual & layout
            size="100%"
            backgroundColor="transparent"
            center
            // 3D perspective
            style={{ perspective: 1000 }}
            // Event
            onMouseMove={handleMouse}
        >
            <Frame
                // Visual & layout
                size={150}
                borderRadius={30}
                backgroundColor="#fff"
                center
                // Transformation
                rotateX={rotateX}
                rotateY={rotateY}
            />
        </Frame>
    )
}

Framer Motion

export function FM24Cursor() {
    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 (
        <motion.div
            style={{
                width: "100%",
                height: "100%",
                display: "flex",
                placeItems: "center",
                placeContent: "center",
                perspective: 1000,
            }}
            onMouseMove={handleMouse}
        >
            <motion.div
                style={{
                    width: 150,
                    height: 150,
                    borderRadius: 30,
                    backgroundColor: "#fff",
                    rotateX: rotateX,
                    rotateY: rotateY,
                }}
            />
        </motion.div>
    )
}

The version in the CodeSandbox is a bit different because it doesn’t have a fixed size background (we get the getBoundingClientRect() of the div being hovered over):

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: 1000
      }}
      onMouseMove={handleMouse}
    >
      <motion.div
        style={{
          width: 150,
          height: 150,
          borderRadius: 30,
          backgroundColor: "#fff",
          rotateX: rotateX,
          rotateY: rotateY
        }}
      />
    </motion.div>
  );
}

Overrides

const appState = Data({
    rotateX: 0,
    rotateY: 0,
})

export function Background(): Override {
    const xTransform = transform([0, 400], [-45, 45])
    const yTransform = transform([0, 400], [45, -45])

    return {
        onMouseMove(event) {
            appState.rotateX = yTransform(event.pageY)
            appState.rotateY = xTransform(event.pageX)
        },
        style: {
            perspective: 1000,
        },
    }
}

export function Frame(): Override {
    return {
        rotateX: appState.rotateX,
        rotateY: appState.rotateY,
    }
}

Leave a Reply