0

I'm trying to create animated text in React (typescript) using GSAP.

Text variable is a string, which is split into an array of strings, form which separate div's are created for every letter. In order to get this animated with GSAP, every element needs to have its own ref - for now only last letter works as only one ref is assigned.

How to create separate refs for every element and pass them to gsap? Seen somewhere that I should pass a callback as refs, but I'm not sure how to do that.

const TextAnimator: FC<TextAnimatorTypes> = ({ text }) => {
    const animatedLetter = useRef(null);

    const letterArray = text.split('');
    const letterElements = [];
    for (let i = 0; i < letterArray.length; i++) {
        letterElements.push(<div ref={animatedLetter} className={styles.letter}>{letterArray[i] === ' ' ? '\xa0' : letterArray[i]}</div>);
    }

    useEffect(() => {
        const random = (min: number, max: number) => {
            return (Math.random() * (max - min)) + min;
        };

        gsap.from(animatedLetter.current, {
            duration: 2.5,
            opacity: 0,
            x: 0,
            y: random(-200, 200),
            z: random(500, 1000),
            scale: .1,
            delay: 0.2,
            yoyo: true,
            repeat: -1,
            repeatDelay: 4,
            ease: Power1.easeOut
        });
    }, []);

    return (
        <div className={styles.box}>
            <p className={styles.animatedText}>
                {letterElements}
            </p>
        </div>
    );
};
jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
kvba
  • 309
  • 3
  • 8
  • Does this answer your question? https://stackoverflow.com/questions/54940399/how-target-dom-with-react-useref-in-map/55105849 – keikai Mar 23 '20 at 01:11
  • Why not use [GSAP's SplitText](http://greensock.com/splittext) which returns an array of references to all of the letters? – Zach Saucier Mar 23 '20 at 15:01
  • Zach, GSAP split text is a paid add-on I'm afraid. And, as far as I know, all it does is wrap letters in div's :( – kvba Mar 23 '20 at 15:28

1 Answers1

0

There is a workaround I've figured out, but still would prefer the useRef hook if someone knows the way to do it...

const TextAnimator: FC<TextAnimatorTypes> = ({ text }) => {
    const letterArray = text.split('');
    const letterElements = [];
    const elementIds: string[] = [];
    for (let i = 0; i < letterArray.length; i++) {
        letterElements.push(<div className={styles.letter}
                                 id={letterArray[i] + i}>{letterArray[i] === ' ' ? '\xa0' : letterArray[i]}</div>);
        elementIds.push(letterArray[i] + i);
    }

    const random = (min: number, max: number) => {
        return (Math.random() * (max - min)) + min;
    };

    useEffect(() => {
        for (let i = 0; i < elementIds.length; i++) {
            const element = document.getElementById(elementIds[i]);
            gsap.from(element, {
                duration: 2.5,
                opacity: 0,
                x: random(-500, 500),
                y: random(-500, 500),
                z: random(-500, 500),
                scale: .1,
                delay: i * .02,
                yoyo: true,
                repeat: -1,
                repeatDelay: 10,
                ease: Power1.easeOut
            });
        }
    }, [elementIds]);

    return (
        <div className={styles.box}>
            <p className={styles.animatedText}>
                {letterElements}
            </p>
        </div>
    );
};
kvba
  • 309
  • 3
  • 8