1+ import { useGSAP } from '@gsap/react' ;
2+ import gsap from 'gsap' ;
3+ import { SplitText } from 'gsap/SplitText' ;
4+ import { ScrollTrigger } from 'gsap/ScrollTrigger' ;
5+ import React , { useRef } from 'react' ;
6+
7+ import "./footertitle.css" ;
8+
9+ gsap . registerPlugin ( SplitText , ScrollTrigger ) ;
10+
11+ const FooterTitle = ( ) => {
12+ const ftConRef = useRef ( null ) ;
13+
14+ useGSAP ( ( ) => {
15+ if ( ! ftConRef . current ) return ;
16+
17+ // Get the original HTML before splitting
18+ const originalHTML = ftConRef . current . querySelector ( ".footer-title h1" ) . innerHTML ;
19+
20+ // Create split - exclude the sub element from being split
21+ const split = new SplitText ( ".footer-title h1" , {
22+ type : "chars" ,
23+ charsClass : "ftChar" ,
24+ // Exclude the <sub> element from being split
25+ exclude : "sub"
26+ } ) ;
27+
28+ // Wrap each character in a span for animation
29+ split . chars . forEach ( char => {
30+ char . innerHTML = `<span>${ char . innerHTML } </span>` ;
31+ } ) ;
32+
33+ const innerChars = split . chars . map ( c => c . querySelector ( "span" ) ) ;
34+
35+ // Handle the sub element separately
36+ const sub = ftConRef . current . querySelector ( ".footer-title sub" ) ;
37+ if ( sub ) {
38+ sub . innerHTML = `<span>${ sub . innerHTML } </span>` ;
39+ const subSpan = sub . querySelector ( "span" ) ;
40+
41+ // Add to innerChars array
42+ innerChars . push ( subSpan ) ;
43+ }
44+
45+ // Initial state - start from left (-120%)
46+ gsap . set ( innerChars , { x : "-120%" } ) ;
47+
48+ // Animation - move to normal position
49+ gsap . to ( innerChars , {
50+ x : "0%" ,
51+ stagger : 0.02 , // Add stagger for character-by-character reveal
52+ ease : "power3.out" ,
53+ scrollTrigger : {
54+ trigger : ftConRef . current ,
55+ start : "top 85%" ,
56+ end : "top 70%" ,
57+ scrub : true ,
58+ // markers: true
59+ }
60+ } ) ;
61+
62+ // Cleanup - revert the split and restore original HTML
63+ return ( ) => {
64+ split . revert ( ) ;
65+ // Restore the original HTML with sub element
66+ ftConRef . current . querySelector ( ".footer-title h1" ) . innerHTML = originalHTML ;
67+ } ;
68+
69+ } , { scope : ftConRef } ) ;
70+
71+ return (
72+ < section ref = { ftConRef } className = 'relative z-1 w-screen h-[50vh] border-1 border-t-[#c4c1b9]' >
73+ < div className = 'w-full flex justify-between items-center px-6 mt-8' >
74+ < p className = 'text-[#b1a696] text-[0.7rem]' >
75+ Website made by—< a href = "#" className = 'text-[#f2ede5]' > Moyra.co</ a >
76+ </ p >
77+ < p className = 'text-[#b1a696] text-[0.7rem]' >
78+ This website is using < a href = "#" className = 'text-[#f2ede5]' > cookies</ a >
79+ </ p >
80+ < p className = 'text-[#b1a696] text-[0.7rem]' >
81+ All rights reserved © < a href = "#" className = 'text-[#f2ede5]' > 2025</ a >
82+ </ p >
83+ </ div >
84+
85+ < div className = 'footer-title w-full text-center' >
86+ < h1 className = 'text-[15vw] font-bold' >
87+ Capsules< sub > ®</ sub >
88+ </ h1 >
89+ </ div >
90+ </ section >
91+ ) ;
92+ } ;
93+
94+ export default FooterTitle ;
0 commit comments