Code
app/page.tsx
1"use client";
2
3import gsap from "gsap";
4import { ScrollTrigger } from "gsap/ScrollTrigger";
5import React, { useRef, RefObject, useEffect, MutableRefObject } from "react";
6
7const phrase =
8 "It is a long established fact that a reader will be distracted...";
9
10export default function Home() {
11 const refs: MutableRefObject<HTMLSpanElement[]> = useRef([]);
12 const container: RefObject<HTMLElement> = useRef(null);
13
14 useEffect(() => {
15 gsap.registerPlugin(ScrollTrigger);
16
17 createAnimation();
18 }, []);
19
20 const createAnimation = () => {
21 gsap.to(refs.current, {
22 scrollTrigger: {
23 trigger: container.current,
24
25 scrub: true,
26
27 start: `top`,
28
29 end: `+=${window.innerHeight / 1.5}`,
30 },
31
32 opacity: 1,
33
34 ease: "none",
35
36 stagger: 0.1,
37 });
38 };
39
40 const splitWords = (phrase: string) => {
41 const body: JSX.Element[] = [];
42 phrase.split(" ").forEach((word, i) => {
43 const letters = splitLetters(word);
44 body.push(<p key={word + "_" + i}>{letters}</p>);
45 });
46 return body;
47 };
48
49 const splitLetters = (word: string) => {
50 const letters: JSX.Element[] = [];
51
52 word.split("").forEach((letter, i) => {
53 letters.push(
54 <span
55 key={letter + "_" + i}
56 ref={(el) => {
57 if (el) {
58 refs.current.push(el);
59 }
60 }}
61 >
62 {letter}
63 </span>
64 );
65 });
66
67 return letters;
68 };
69
70 return (
71 <main ref={container} className="main">
72 <div className="body">{splitWords(phrase)}</div>
73 </main>
74 );
75}
Installation
Initialized a project and installed the necessary dependencies to use this component.
1. Initialize a new Next.js project:
npx create-next-app@latest my-app
2. Install GSAP
npm install gsap
Add Styles
src/globals.css
1@tailwind base;
2@tailwind components;
3@tailwind utilities;
4
5body {
6 background: black;
7}
8
9.main {
10 display: flex;
11 height: 100vh;
12 align-items: flex-end;
13 justify-content: center;
14 margin-bottom: 100vh;
15 color: rgb(211, 211, 211);
16}
17
18.body {
19 width: 90%;
20 display: flex;
21 flex-wrap: wrap;
22}
23
24.body p {
25 font-size: 3.5vw;
26 margin: 0px;
27 margin-right: 1.5vw;
28 font-weight: 700;
29}
30
31.body span {
32 opacity: 0.2;
33}