Figo Posted April 16, 2021 Posted April 16, 2021 I have tried to different methods for creating a stagger animation with scrollTrigger, is this possible thought with GSAP (in ReactJS) ? What I'm trying to achieve, is to have a headline animate, followed by a second headline, follow by p text once the user scrolls into the trigger. In this method, GSAP ignores "delay": LINK TO CODESANDBOX => https://codesandbox.io/s/winter-bush-z0qbu?file=/src/App.js Here is another method where I use staggerFrom, but as soon as I add scrollTrigger to the animation it doesn't work: LINK TO CODESANDBOX (without scrollTrigger) => https://codesandbox.io/s/great-goldberg-ivkf6?file=/src/App.js LINK TO CODESANDBOX (with scrollTrigger) =>https://codesandbox.io/s/icy-bash-180e8?file=/src/App.js Any advice would be super appreciated
PointC Posted April 16, 2021 Posted April 16, 2021 Hi @Figo We just had a thread showing some problems with using ScrollTrigger, stagger and delay. @GreenSock has a fix here: Happy tweenng.
GreenSock Posted April 16, 2021 Posted April 16, 2021 I don't think it was related to that edge case in the other thread (that'd only occur if you had a "restart" in your toggleActions and a stagger and delay). I noticed a few things: You're using invalid start values. It's supposed to be a space-delimited value where the first one relates to the trigger, and the second to the scroller (viewport). // BAD start: "20%" // GOOD start: "20% bottom" // when 20% from the top of the trigger hits the bottom of the viewport You were missing the "." in the class name selector text: // BAD trigger: "trigger-container" // GOOD trigger: ".trigger-container" Beware you may need to use refs in React, but I'm not sure (I'm not a React guy). You're creating 3 separate animations, each in a useEffect(), but you can just create a single staggered animation like: useEffect(() => { gsap.from([headlineFirst, headlineSecond, contentP], { y: 49, duration: 1, stagger: 0.2, scrollTrigger: { trigger: ".trigger-container", start: "20% 100%", markers: true, toggleActions: "restart none none none" } }); }, []); This makes them always come in staggered in the same way, but if you prefer to heave each individual element only start animating when it enters the viewport (which would sorta look like staggering if you scroll relatively slowly), then just use a .forEach() and create an animation for each one, as described here. Happy tweening! 2
PointC Posted April 16, 2021 Posted April 16, 2021 Ha. Whoops. I read ScrollTrigger, stagger & delay and then stopped reading. 1
Figo Posted April 16, 2021 Author Posted April 16, 2021 Amazing thanks for the swift response from both of you, Jack i will give your suggestions a go (and thanks for the advice) 1
Figo Posted April 16, 2021 Author Posted April 16, 2021 James you are a legend! I'm 99% there. If you don't mind me asking you, in that example in number 3, if I had to give the "contentP", different values for the animation, would it be possible? Still the same trigger Here is an example but running into a syntax error: useEffect(() => { gsap.from([headlineFirst, headlineSecond, contentP], { y: 49, duration: 1, stagger: 0.2, scrollTrigger: { trigger: ".trigger-container", start: "20% 100%", markers: true, toggleActions: "restart none none none" }.from(contentP, 1, { y: 20, opacity: 0 } }) }, []);
Figo Posted April 17, 2021 Author Posted April 17, 2021 I can probably just write the animation twice for the other div to have it's own values, but would be neat if I could combine them. useEffect(() => { gsap.from([headlineFirst, headlineSecond, contentP], { y: 49, duration: 1, stagger: 0.2, scrollTrigger: { trigger: ".trigger-container", start: "20% 100%", markers: true, toggleActions: "play none none none" } }); }, []); useEffect(() => { gsap.from(contentP, { y: 20, duration: 1, opacity:0, stagger: 0.2, scrollTrigger: { trigger: ".trigger-container", start: "20% 100%", markers: true, toggleActions: "play none none none" } }); }, []);
GreenSock Posted April 17, 2021 Posted April 17, 2021 Technically you could by using a function-based value, but it's probably simplest to just use two tweens. It's totally up to you. gsap.from([headlineFirst, headlineSecond, contentP], { y: (i, target) => { return target.classList.contains("contentP") ? 20 : 49; }, duration: 1, stagger: 0.2, scrollTrigger: { trigger: ".trigger-container", start: "20% 100%", markers: true, toggleActions: "play none none none" } }); I don't know why you'd need to put each one into a separate useEffect(), though. And if you're only animating one thing (contentP), a "stagger" is kinda pointless Happy tweening!
Figo Posted April 17, 2021 Author Posted April 17, 2021 No ideally I want one useEffect, but in that sample I want to give the "contentP" different animation values. For example "HeadlineFirst" and "HeadlineSecond" starts from y:49, but I want "contentP" to start from y:20 as an example. Or what if I wanted to "contentP" to start from opacity:0, but not for "HeadlineFirst" and "HeadlineSecond". Writing two useEffects won't work because they start at the same time, not giving the stagger effect.
GreenSock Posted April 17, 2021 Posted April 17, 2021 I think you might be misunderstanding me. In one useEffect(), you can create any number of animations, and they can be staggered (or not). useEffect(() => { gsap.from([headlineFirst, headlineSecond], { y: 49, duration: 1, stagger: 0.2, scrollTrigger: { trigger: ".trigger-container", start: "20% 100%", markers: true, toggleActions: "play none none none" } }); gsap.from(contentP, { y: 20, duration: 1, delay: 0.4, opacity:0, scrollTrigger: { trigger: ".trigger-container", start: "20% 100%", markers: true, toggleActions: "play none none none" } }); }, []); Or just use a timeline: useEffect(() => { const tl = gsap.timeline(); tl.from([headlineFirst, headlineSecond], { y: 49, duration: 1, stagger: 0.2, scrollTrigger: { trigger: ".trigger-container", start: "20% 100%", markers: true, toggleActions: "play none none none" } }); tl.from(contentP, { y: 20, duration: 1, opacity:0, scrollTrigger: { trigger: ".trigger-container", start: "20% 100%", markers: true, toggleActions: "play none none none" } }); }, []); And I already showed you how you could use a single tween for various values by using a function-based value. Did you notice the function that I used for the "y" value in the previous post? y: (i, target) => { return target.classList.contains("contentP") ? 20 : 49; } But again, it's probably simpler to just create a separate tween if you're going to target different values rather than getting fancy with function-based values. It's totally up to you. 1
Figo Posted April 17, 2021 Author Posted April 17, 2021 Thank you Jack for your thorough explanation and taking the time to respond and providing solutions. I just need to learn more about function-based values for writing it in a single tween. I do understand the function part e.g. " ()=>{ } " and the ternary operator at the end but will do some self-educating on the "target.classList.contains" part. Anyway what i was trying to point out, is if you write two separate tweens, and "fake" the stagger effect by giving one of the tweens a "delay", scrollTrigger ignores the delay. Here is your example with one of the tweens having a delay of 1000. Link=> https://codesandbox.io/s/zen-dew-fr0xv?file=/src/App.js For example if run your code above, but changed the delay to be more noticeable, does the delay or opacity work on your side? useEffect(() => { gsap.from([headlineFirst, headlineSecond], { y: 49, duration: 1, stagger: 0.2, scrollTrigger: { trigger: ".trigger-container", start: "20% 100%", markers: true, toggleActions: "play none none none" } }); gsap.from(contentP, { y: 20, duration: 1, delay: 1000, opacity:0, scrollTrigger: { trigger: ".trigger-container", start: "20% 100%", markers: true, toggleActions: "play none none none" } }); }, []);
Solution Figo Posted April 17, 2021 Author Solution Posted April 17, 2021 Regardless I managed to create the desired effect, thanks to you Jack useEffect(() => { gsap.from([headlineFirst, headlineSecond , contentP], { y: (i, target) => { return target.classList.contains("contentP") ? 20 : 49; }, opacity: (i, target) => { return target.classList.contains("contentP") ? 0 : 1; }, duration: 1, ease: Power3.easeOut, stagger: 0.3, scrollTrigger: { trigger: ".aboutV2", start: "10% 100%", markers: true, toggleActions: "play none none pause" } }); }, []);
GreenSock Posted April 17, 2021 Posted April 17, 2021 8 hours ago, Figo said: scrollTrigger ignores the delay. That issue is resolved in the next release which you can preview at https://assets.codepen.io/16327/ScrollTrigger.min.js 7 hours ago, Figo said: I managed to create the desired effect, thanks to you Jack Excellent, glad you got things working. Enjoy! 1
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