iTranslated by AI

The content below is an AI-generated translation. This is an experimental feature, and may contain errors. View original article
✏️

Building an Auto-Scrolling Marquee Component in React for Text Exceeding a Certain Width

に公開

This is it.

App.tsx
import * as React from 'react';
import './style.css';
import './Marquee.tsx';
import { Marquee } from './Marquee';

export default function App() {
  return (
    <div>
      <h1>Hello Marquee</h1>
      <Marquee limitWidth={200}>
        Hi! This text is scrolling............ :)
      </Marquee>
      <Marquee limitWidth={200}>This text is not scroll!</Marquee>
    </div>
  );
}
style.css
.Marquee {
  position: relative;
}

.MarqueeWidthExtract {
  visibility: hidden;
  white-space: nowrap;
  position: absolute;
}

.MarqueeText {
  white-space: nowrap;
}
Marquee.tsx
import * as React from 'react';
import { ReactNode, FC, useEffect, useState, useRef } from 'react';
import { useSpring, animated } from '@react-spring/web';

export const Marquee: FC<{ children: ReactNode; limitWidth: number }> = ({
  children,
  limitWidth,
}) => {
  const containerRef = useRef<HTMLDivElement | null>(null);
  const [startAnimation, setStartAnimation] = useState(false);
  const { translateX } = useSpring({
    config: {
      duration: 10000,
    },
    from: {
      translateX: '0%',
    },
    to: {
      translateX: '-100%',
    },
    delay: 2000,
    onRest: () => {
      setStartAnimation(false);
    },
  });

  useEffect(() => {
    if (!containerRef.current) return;

    const { width } = containerRef.current.getBoundingClientRect();
    if (width >= limitWidth) setStartAnimation(true);
  }, [limitWidth]);

  return (
    <div className="Marquee">
      {!startAnimation && (
        <span ref={containerRef} className="MarqueeWidthExtract">
          {children}
        </span>
      )}
      {!startAnimation && <span className="MarqueeText">{children}</span>}
      {startAnimation && (
        <animated.div
          style={{
            translateX: translateX,
          }}
        >
          <span className="MarqueeText">{children}</span>
        </animated.div>
      )}
    </div>
  );
};

The key point is in Marquee.tsx, where the text is first rendered without wrapping to get its width in useEffect. If the width exceeds the specified limitWidth, the startAnimation flag is set to true. Once startAnimation is true, the text is animated using react-spring.

The following is the style for the MarqueeWidthExtract class. white-space: nowrap is used to prevent line breaks. We've used visibility: hidden and position: absolute to measure the width while hiding it from the screen.

.MarqueeWidthExtract {
  visibility: hidden;
  white-space: nowrap;
  position: absolute;
}

Discussion