Rolling Text Cursor

Example

Move your cursor around
I AM ROLLING TEXT AROUND THE CURSOR  

Installation

Copy and paste the source code into your project.

"use client";
 
import { motion } from "framer-motion";
import React, { useEffect, useState } from "react";
 
type RollingTextCursorProps = {
  text: string;
  size?: number;
  slackWhenFollowing?: number;
  circleSpeed?: number;
  className?: string;
};
 
/**
 * RollingTextCursor component.
 *
 * A component that displays a rolling text cursor that follows the mouse movement.
 *
 * @param {string} [props.text] - The text to be displayed by the cursor.
 * @param {number} [props.size=150] - The size of the cursor.
 * @param {number} [props.slackWhenFollowing=0.5] - The duration of the cursor's animation when following the mouse movement.
 * @param {number} [props.circleSpeed=10] - The speed of the circular animation of the cursor.
 * @param {string} [props.className] - The additional CSS class for the text.
 */
export const RollingTextCursor = ({
  text,
  size = 150,
  slackWhenFollowing = 0.5,
  circleSpeed = 10,
  className,
}: RollingTextCursorProps) => {
  const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 });
 
  const handleMouseMove = (e: MouseEvent) => {
    setMousePosition({ x: e.clientX, y: e.clientY });
  };
 
  useEffect(() => {
    window.addEventListener("mousemove", handleMouseMove);
    return () => {
      window.removeEventListener("mousemove", handleMouseMove);
    };
  }, []);
 
  const offset = size / 2;
  const radius = size / 2;
  const circumference = 2 * Math.PI * radius;
  const textWithSpaceInTheEnd = `${text} \u00A0`;
 
  return (
    <motion.div
      initial={{ opacity: 0 }}
      animate={{ opacity: 1 }}
      transition={{ duration: 1, ease: "easeOut" }}
      className={`fixed top-0 left-0 w-full h-full pointer-events-none z-50`}
    >
      <motion.div
        animate={{
          x: mousePosition.x - offset,
          y: mousePosition.y - offset,
        }}
        transition={{
          type: "tween",
          ease: "backOut",
          duration: slackWhenFollowing,
        }}
        style={{
          transform: "translate(-50%, -50%)",
        }}
      >
        <motion.svg
          width={size}
          height={size}
          className="pointer-events-none absolute z-10"
          animate={{
            rotate: 360,
          }}
          transition={{
            duration: circleSpeed,
            repeat: Infinity,
            ease: "linear",
          }}
        >
          <path
            id="circlePath"
            d={`M${size / 2},${size / 2} m-${radius},0 a${radius},${radius} 0 1,0 ${size},0 a${radius},${radius} 0 1,0 -${size},0`}
            fill="none"
          ></path>
          <text>
            <textPath
              href="#circlePath"
              fill="currentColor"
              className={className}
              textLength={circumference}
            >
              {textWithSpaceInTheEnd}
            </textPath>
          </text>
        </motion.svg>
      </motion.div>
    </motion.div>
  );
};

    Update the import paths to match your project setup.

    Props

    PropTypeDefaultDescription
    textstring-The text to be displayed by the cursor.
    sizenumber150The size of the cursor.
    slackWhenFollowingnumber0.5The duration of the cursor's animation when following the mouse movement.
    circleSpeednumber10The speed of the circular animation of the cursor.
    classNamestring-The additional CSS class for the text.