Marquee Effect

The marquee component is designed to create a dynamic user experience by continuously scrolling text horizontally. This effect is perfect for highlighting important information, announcements, or news headlines that need to catch the user's attention. The smooth scrolling animation adds movement and liveliness to the interface, making important content more noticeable to users. This example was created using Tailwind CSS and Framer Motion.

Examples

Bundui ComponentsBundui ComponentsBundui ComponentsBundui Components

Reverse routing

Bundui ComponentsBundui ComponentsBundui ComponentsBundui Components
Bundui ComponentsBundui ComponentsBundui ComponentsBundui Components

Install the following dependencies:

npm i framer-motion clsx tailwind-merge @motionone/utils

Add util file

import { ClassValue, clsx } from 'clsx';
import { twMerge } from 'tailwind-merge';

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs));
}

Copy and paste the following code into your project.

"use client";

import { useRef } from "react";
import {
  motion,
  useScroll,
  useSpring,
  useTransform,
  useMotionValue,
  useVelocity,
  useAnimationFrame,
} from "framer-motion";
import { wrap } from "@motionone/utils";
import { cn } from "@/lib/utils";

type MarqueeAnimationProps = {
  children: string;
  className?: string;
  direction?: "left" | "right";
  baseVelocity: number;
};

export default function MarqueeAnimation({
  children,
  className,
  direction = "left",
  baseVelocity = 10,
}: MarqueeAnimationProps) {
  const baseX = useMotionValue(0);
  const { scrollY } = useScroll();
  const scrollVelocity = useVelocity(scrollY);
  const smoothVelocity = useSpring(scrollVelocity, {
    damping: 50,
    stiffness: 400,
  });
  const velocityFactor = useTransform(smoothVelocity, [0, 1000], [0, 0], {
    clamp: false,
  });

  const x = useTransform(baseX, (v) => `${wrap(-20, -45, v)}%`);

  const directionFactor = useRef<number>(1);
  useAnimationFrame((t, delta) => {
    let moveBy = directionFactor.current * baseVelocity * (delta / 1000);

    if (direction == "left") {
      directionFactor.current = 1;
    } else if (direction == "right") {
      directionFactor.current = -1;
    }

    moveBy += directionFactor.current * moveBy * velocityFactor.get();

    baseX.set(baseX.get() + moveBy);
  });

  return (
    <div className="overflow-hidden max-w-[100vw] text-nowrap flex-nowrap flex relative">
      <motion.div
        className={cn(
          "font-bold uppercase text-5xl flex flex-nowrap text-nowrap *:block *:me-10",
          className
        )}
        style={{ x }}
      >
        <span>{children}</span>
        <span>{children}</span>
        <span>{children}</span>
        <span>{children}</span>
      </motion.div>
    </div>
  );
}

Usage

import MarqueeEffect from "@/components/ui/marquee-effect";

export default function MarqueeEffectExample() {
  return <MarqueeEffect baseVelocity={-3}>Bundui Components</MarqueeEffect>;
}