Wavy Text Animation using React Hooks with GSAP v3

We will learn to use GSAP v3 with React hooks, next to that i'll build my own textsplitter.

4 min read
by Mees Rutten | Sun Jul 12 2020

In this article i'll create a wavy text animation with React Hooks and GSAP v3. For the example I created my own small text split function. This example should be usable in React frameworks like Gatsby, Next.js, etc.

Tech stack

  • React for markup, templating, routing, etc.
  • GSAP for animation
  • EmotionCSS to write normal CSS, nest selectors and componentize styles

Goal

We want to create text that animates on first-load. The text should be split for each letter, then animate once smoothly.

Markup

First we create a styled element

1<div className="App">
2 <WavyTextStyled ref={wavyTextRef}>ride the waaaaaaaave</WavyTextStyled>
3</div>

CSS

We make the text look pretty with some css. The font-size value is based on a clamp function used in WebGL. The function creates a range between 1600px and 320px screenwidth. Within this range it calculates a font-size between 64px and 32px.

1const TextStyled = styled.p`
2 font-size: calc(32px + (64 - 32) * ((100vw - 320px) / (1600 - 320)));
3 font-family: poppins;
4 font-weight: 500;
5 margin: 0;
6 color: white;
7`;

Using hooks

We need to keep a reference to the text to target it with GSAP. Next to that, we only want the animation to run on load and once. If we give an empty dependecy array to the useEffect, it will trigger once.

1export default function App() {
2 const wavyTextRef = useRef(null);
3
4 useEffect(() => {
5 // ANIMATION GOES HERE
6 }, []);
7
8 return (
9 <div className="App">
10 <TextStyled ref={wavyTextRef}>ride the waaaaaaaave</TextStyled>
11 </div>
12 );
13}

Animating with GSAP

To start animating with GSAP, we want to be sure the ref has been initialised. Afterwards, we split the text with the function underneath this codeblock. This function will return all letters in the text as span elements with some necessary styling.

To start animating, we set the perspective of the TextStyled component to 400 to create depth. Afterwards we animate the letters from certain values

I'm pretty sure, that gsap.from also does a gsap.set under the hood. This results in the element being set in the starting position on load.

1if (!wavyTextRef.current) return;
2const chars = SplitTextToChars(wavyTextRef.current);
3
4gsap.set(wavyTextRef.current, { perspective: 400 });
5
6gsap.from(
7 chars,
8 {
9 duration: 0.8,
10 opacity: 0,
11 scale: 1,
12 delay: 2,
13 y: -40,
14 rotationX: -90,
15 transformOrigin: '0% 50% -50',
16 ease: 'back',
17 stagger: 0.1,
18 },
19 '+=0'
20);
1function SplitTextToChars(textNode) {
2 const textContent = textNode.textContent;
3 const textSplit = textContent.split('');
4
5 const frag = document.createDocumentFragment();
6 textSplit.forEach((letter, i) => {
7 const span = document.createElement('span');
8 span.textContent = letter;
9 span.style = `${letter === ' ' ? 'min-width: 1rem;' : ''}z-index: ${
10 textSplit.length - i
11 }; position: relative; display: inline-block;`;
12 frag.appendChild(span);
13 });
14 textNode.textContent = '';
15 textNode.appendChild(frag);
16
17 return textNode.children;
18}
19
20export default SplitTextToChars;

Demo

That's it!

PS.

If you are wondering how you can use GSAP v3 during interaction we can just create functions like this, with event target or pass a reference:

1<button onClick={e => {
2 gsap.to(e.target, {
3 duration: 1,
4 color: green;
5 })
6}}>click!</button>

Final words

That's it! You can link this animation to an Intersection Observer to animate while scrolling, or keep it like this to animate when someone navigates.


Thanks for reading!

I hope someone somewhere learned something via this post! If you did, please consider sharing the article.

Special thanks to @moniac for sharing this template!

by Mees Rutten @

All rights reserved