NickWoodward Posted September 6, 2025 Posted September 6, 2025 Hello! Couple of questions if someone has a sec or two please?😊 https://codesandbox.io/p/devbox/7p786f Is there a better way to sync the masking reveal of the text with the pinning here? Obviously it's a bit of a matter of taste, but maybe with the duration of the pinning, or so that at least the reveal happens after the pinning? I've had a bit of a play around, but I'm just mindful of learning bad habits. Also - the responsive splitting - the background text is obviously responsive, but the split text isn't. The docs reference autosplit, but I'm a little confused how to create the splitting animation in the onSplit callback if it's part of a larger timeline? I'm also seeing a lot of the bug in the screenshot - where resizing makes content and triggers 'jump'. I'm just not sure how to debug them tbh Hope I've explained that well enough! Nick
Sam Tremblay Posted September 6, 2025 Posted September 6, 2025 Hello! Â Instead of calculating each line, you could use background-clip: text on each line, or set up mix-blend-mode with layered elements at different depths. Then, you can animate the background clip using a GSAP stagger based on the section, or apply a specific translate or scale to a div if you go with the mix-blend-mode approach.
Solution Sam Tremblay Posted September 6, 2025 Solution Posted September 6, 2025 Hello again! Â I do an example for you with background-clip: text. Â You can inspect behavior here:Â https://studiochampgauche.com/demo/ Â Here is my JS: 'use strict'; import React, { useEffect, useRef } from 'react'; import { Link } from 'react-router-dom'; import { gsap } from 'gsap'; import { ScrollTrigger } from 'gsap/ScrollTrigger'; import { SplitText } from 'gsap/SplitText'; import Image from './components/Image'; const Demo = () => { const ref = useRef(null); const innerRef = useRef(null); const containerRef = useRef(null); const textRef = useRef(null); const paragraphRef = useRef([]); useEffect(() => { gsap.registerPlugin(SplitText); let killEvents = []; /* * Pin inner element */ let pinAnimation = ScrollTrigger.create({ trigger: ref.current, start: 'top top', end: 'bottom bottom', pin: innerRef.current, pinSpacing: false, scrub: true }); /* * Split Text */ let split = SplitText.create(paragraphRef.current, { type: 'lines', linesClass: 'line', }); /* * Anim split */ let splitAnimation = gsap.to([paragraphRef.current[0].querySelectorAll('.line'), paragraphRef.current[1].querySelectorAll('.line')], 1, { backgroundPositionY: 0, stagger: .6, ease: 'none', scrollTrigger: { trigger: ref.current, start: 'top top', end: 'bottom bottom', scrub: true } }); killEvents.push(() => { if(pinAnimation){ pinAnimation.kill(); pinAnimation = null; } if(split){ split.revert(); split = null; } if(splitAnimation){ splitAnimation.revert(); splitAnimation = null; } }); return () => { killEvents?.forEach(killEvent => killEvent()); killEvents = []; } }); return( <> <section ref={ref} id="demo-1"> <div ref={innerRef} className="inner"> <div ref={containerRef} className="container"> <div ref={textRef} className="text"> <p ref={el => paragraphRef.current[0] = el}>Lorem ipsum dolor sit amet consectetur adipisicing elit. Excepturi laborum repellendus.</p> <p ref={el => paragraphRef.current[1] = el}>Rerum assumenda dolore neque mollitia praesentium adipisci amet commodi sint aliquid pariatur.</p> </div> </div> </div> </section> <section className="fake" style={{ height: '100svh' }}> <div className="container"> <p>Silence is golden</p> </div> </section> </> ); } export default Demo; Â Here is my SCSS: //@use '../inc/variables' as *; //@use '../inc/functions' as *; //@use '../inc/mixins' as *; #demo-1{ height: 300svh; padding: 0 30px; .inner{ display: flex; height: 100svh; align-items: center; justify-content: center; will-change: transform; .container{ width: 100%; max-width: 1640px; .text{ width: 100%; margin: 0 auto; max-width: 1088px; p{ font-size: 45px; line-height: 140%; //color: transparent; .line{ position: relative; background-image: linear-gradient(to top, #ff0000 50%, #00ff00 0); background-position-y: 100%; background-size: 100% 200%; background-clip: text; color: transparent; .word{ .char{ } } } & + p{ margin: 25px 0 0; } } } } } } Â 1
NickWoodward Posted September 7, 2025 Author Posted September 7, 2025 Cool effect - I never would have thought of doing it that way. Like how it also handles multiple paragraphs. Thanks! I assume with useGSAP I don't have to set up that killEvents array? And for responsiveness I'd just have to use a screen width value in the useEffect/GSAP dependency array? Thanks again!
Sam Tremblay Posted September 10, 2025 Posted September 10, 2025 Thanks chief! Â About useGSAP, I don't know because I use all time useEffect. I never install the react part of GSAP. Â And for the responsive, it's depend. You have multiple ways. I love use px/vw/vh units directly in CSS (with clamp & some breakpoints) and gsap.matchMedia in JavaScript for start or kill some animations based on media query. 1
NickWoodward Posted September 27, 2025 Author Posted September 27, 2025 Sorry to bother you again @Sam Tremblay, but I don't suppose you can see why my implementation of your effect isn't animating please? (it's instantly switching the background position, for all lines). I just can't see why it's not working, looks functionally the same to me? Your working code example:Â https://codesandbox.io/p/devbox/fkgtrg Mine:Â https://codesandbox.io/p/devbox/creed-hero-forked-pzsnfr?workspaceId=ws_3p4VjtDpEFGetZAxu6vymW Â
NickWoodward Posted September 27, 2025 Author Posted September 27, 2025 ahhh, for some reason - that I haven't yet worked out - your scrolltrigger is twice the size as mine. if i add "+=20%" to my 'end' - end: "bottom+=20% bottom" it works, but I don't really know why. I mean it works!😄
Sam Tremblay Posted September 29, 2025 Posted September 29, 2025 Hey @NickWoodward! I'm not sure to understand the problem, my english is not super good hehe. I see you use +=100% now instead of 20%.... Maybe you need that because you have the pinSpacing to true. Try to turn this parameter to false. Â Does it help? Â Â EDIT: hmmmm... Or maybe it's because I have a pinned container 100vh moving in a div 300vh and not you ? 1
NickWoodward Posted September 29, 2025 Author Posted September 29, 2025 7 hours ago, Sam Tremblay said: maybe it's because I have a pinned container 100vh moving in a div 300vh and not you yeah i did think it might be this. it's ok though, i've got it working well now, with the autoSplit/onSplit callback. thanks a lot for your help. your english seems great btw 🙂 1
Sam Tremblay Posted September 29, 2025 Posted September 29, 2025 Hehe! Awesome 🥳  Thanks for my english!
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now