11"use client" ;
2-
3- import { motion , MotionValue , useScroll , useTransform } from "motion/react" ;
2+ import { motion , useInView } from "motion/react" ;
43import { ComponentPropsWithoutRef , FC , ReactNode , useRef } from "react" ;
5-
64import { cn } from "@/lib/utils" ;
75
86export interface TextRevealProps extends ComponentPropsWithoutRef < "div" > {
97 children : string ;
108}
119
12- export const TextReveal : FC < TextRevealProps > = ( { children, className } ) => {
13- const targetRef = useRef < HTMLDivElement | null > ( null ) ;
14- const { scrollYProgress } = useScroll ( {
15- target : targetRef ,
16- } ) ;
10+ const TextReveal : FC < TextRevealProps > = ( { children, className } ) => {
11+ const targetRef = useRef < HTMLDivElement > ( null ) ;
12+ const isInView = useInView ( targetRef , { once : false , amount : 0.3 } ) ;
1713
1814 if ( typeof children !== "string" ) {
1915 throw new Error ( "TextReveal: children must be a string" ) ;
@@ -22,23 +18,25 @@ export const TextReveal: FC<TextRevealProps> = ({ children, className }) => {
2218 const words = children . split ( " " ) ;
2319
2420 return (
25- < div ref = { targetRef } className = { cn ( "relative z-0 h-[200vh] " , className ) } >
21+ < div ref = { targetRef } className = { cn ( "relative z-0" , className ) } >
2622 < div
2723 className = {
28- "sticky top-0 mx-auto flex h-[50%] max-w-4xl items-center bg-transparent px-[1rem] py-[5rem]"
24+ "mx-auto flex max-w-4xl items-center bg-transparent px-[1rem] py-[5rem]"
2925 }
3026 >
3127 < span
32- ref = { targetRef }
3328 className = {
3429 "flex flex-wrap p-5 text-2xl text-black/20 dark:text-white/20 md:p-8 md:text-3xl lg:p-10 lg:text-4xl xl:text-5xl"
3530 }
3631 >
3732 { words . map ( ( word , i ) => {
38- const start = i / words . length ;
39- const end = start + 1 / words . length ;
4033 return (
41- < Word key = { i } progress = { scrollYProgress } range = { [ start , end ] } >
34+ < Word
35+ key = { i }
36+ index = { i }
37+ totalWords = { words . length }
38+ isInView = { isInView }
39+ >
4240 { word }
4341 </ Word >
4442 ) ;
@@ -51,21 +49,31 @@ export const TextReveal: FC<TextRevealProps> = ({ children, className }) => {
5149
5250interface WordProps {
5351 children : ReactNode ;
54- progress : MotionValue < number > ;
55- range : [ number , number ] ;
52+ index : number ;
53+ totalWords : number ;
54+ isInView : boolean ;
5655}
5756
58- const Word : FC < WordProps > = ( { children, progress, range } ) => {
59- const opacity = useTransform ( progress , range , [ 0 , 1 ] ) ;
57+ const Word : FC < WordProps > = ( { children, index, totalWords, isInView } ) => {
6058 return (
6159 < span className = "xl:lg-3 relative mx-1 lg:mx-1.5" >
6260 < span className = "absolute opacity-30" > { children } </ span >
6361 < motion . span
64- style = { { opacity : opacity } }
62+ initial = { { opacity : 0 } }
63+ animate = { {
64+ opacity : isInView ? 1 : 0.2 ,
65+ } }
66+ transition = { {
67+ duration : 0.4 ,
68+ delay : isInView ? index * 0.08 : 0 ,
69+ } }
70+
6571 className = { "text-black dark:text-white" }
6672 >
6773 { children }
6874 </ motion . span >
6975 </ span >
7076 ) ;
7177} ;
78+
79+ export default TextReveal ;
0 commit comments