Tonycre8 Posted August 1, 2020 Share Posted August 1, 2020 Hey guys, I'm having some trouble on something this evening, was wondering if you guys could help. Given that this uses a 3d model, I can't attach a codepen unfortunately. But I'll do the best to include all relevant code snippets and give all the details I can. I've been working on a website that uses scroll trigger to move content around, and also to move around this 3D car model that I have. I have 4 different slides, and for the sake of simplicity, I've gone ahead and recoloured them. Slide 1: Blue, start slide Slide 2: Pink, slides in from the right Slide 3: Yellow, slides in from bottom Slide 4: Green, slides in from the right Now Slide 4 is the main culprit, I'm trying to move this car around to have the car in the 4th slide look like the image below: However, the transition to the image is a little less than sleek. I've attached a video of how the transitions currently look, scrolling between these different slides. Ignore the music, I just recorded this very quickly on my ShadowPlay. Now let's talk about the code I've got. I'm trying to use ScrollTrigger to move between all of these, and I created a variable for a timeline just so I could keep it all in check (or so I thought). Here's what I have thusfar. scene.rotation.set(0, 9.8, 0) camera.position.set(-1.2,0,5) let car_anim = gsap.timeline() // Slide 2 car_anim.to(scene.rotation, {y: 4.79, easing: Power1.easeInOut, scrollTrigger: { trigger: "#slide2", scrub: 1, }}, 0) car_anim.to(camera.position, {x: -0.1, easing: Power1.easeInOut, scrollTrigger: { trigger: "#slide2", scrub: 1, start: "right right" }}, 0) // Slide 3 car_anim.to(scene.rotation, {z: 1.6, easing: Power1.easeInOut, scrollTrigger: { trigger: "#slide3", scrub: 1, start: "top top" }}, 0) // Slide 4 - The problem child car_anim.to(scene.rotation, {z: 0.02, y: 3.1, easing: Power1.easeInOut, scrollTrigger: { trigger: "#slide4", scrub: 1, start: "right+=2500 right" }}, 0) car_anim.to(camera.position, {x: 0.16, easing: Power1.easeInOut, scrollTrigger: { trigger: "#slide4", scrub: 1, start: "right+=2500 right" }}) I start out with setting a base camera position and scene position. The scene position refers to the car, and the camera just refers to the camera. The first three slides work well. I have another video somewhere of all of those working just fine. I'm just having trouble of smoothly continuing the movements of the car. It's not a ThreeJS issue I don't think. I'm pretty sure it's just the start of the scroll trigger, given that the other ones have worked fine. I guess it's as I've started making more complex movements. So yeah, how do you think I could get these working at all? I'm not entirely sure what I'm doing wrong to be very honest with you. Thanks in advance, and sorry for the lack of codepen - it's a little hard to do with all the technical set up. UPDATE: Just an edit after I've played around with it a bit more. I think it's becoming less and less of a gsap issue, but I'll keep this thread up in case anyone else knows how to solve it / it can be used as a future reference. It seems as if when I modify a different axis more than once (i.e. the y-axis), it updates the value weirdly. I don't have console log proof, but I messed around with the 4th slide and it seems to transition fine when I set it to affect the x-axis - a property that has yet to be used with the scene rotation. Link to comment Share on other sites More sharing options...
ZachSaucier Posted August 3, 2020 Share Posted August 3, 2020 Hey Tony. A few notes about your code: We recommend using the condensed string form of eases. In this case it'd be ease: "power1.inOut". We recommend using GSAP's defaults to save you from having to type the same property and value over and over again in your timeline tweens. You use a value of 0 in the position parameter for different tweens which makes them conflict. Why are you even using the position parameter in this case since you want the animations to be sequenced? I recommend that you learn more about the position parameter Link to comment Share on other sites More sharing options...
akapowl Posted August 4, 2020 Share Posted August 4, 2020 Since I am interested in this myself, I prepared a little reduced and slightly differed scenario that basically represents what @Tonycre8 is experiencing (at least I think so). See the Pen 856fe178c736444b2d6e1d7b1e13bce2 by akapowl (@akapowl) on CodePen When getting to the point, where a parameter (x, y or z of camera position or scene rotation), that already has been tweened, is going to be tweened again, the value where it is tweened from appears to be the value that has been set up prior to setting up the ScrollTriggers and not the value it has been tweened to before. In this case: scene.rotation.set(0, 1.88, 0) camera.position.set(2, 0, 5) I hope that is somewhat understandable. Interestingly enough though, I found, that if you scroll to the bottom of the page and refresh the page, the animation seems to run as probably intended by OP - in both directions, scrolling up and down. 2 Link to comment Share on other sites More sharing options...
GreenSock Posted August 4, 2020 Share Posted August 4, 2020 Just set immediateRender: false on your ScrollTriggers. By default, ScrollTrigger will force an initial render on the associated animation in order to ensure maximum performance and (the first render is always the most CPU-intensive because it must parse and record the starting/ending values so that it can quickly interpolate during the tween). So everything is working the way it's supposed to, but you've got multiple tweens affecting the same properties of the same object, thus the initial state is getting recorded for them all. You can tell ScrollTrigger not to do that with immediateRender: false. 1 1 Link to comment Share on other sites More sharing options...
akapowl Posted August 4, 2020 Share Posted August 4, 2020 100% buttery smoothness in my pen now. See the Pen 94ebcbff6f403f34075b36738ad41c3d by akapowl (@akapowl) on CodePen Also, I cleaned things up just a tiny bit to show the usage of GSAP's defaults, as @ZachSaucier hinted to. Thanks for the help and explenation @GreenSock You might wanna try this, @Tonycre8 2 Link to comment Share on other sites More sharing options...
Tonycre8 Posted August 4, 2020 Author Share Posted August 4, 2020 5 minutes ago, akapowl said: 100% buttery smoothness in my pen now. Also, I cleaned things up just a tiny bit to show the usage of GSAP's defaults, as @ZachSaucier hinted to. Thanks for the help and explenation @GreenSock You might wanna try this, @Tonycre8 This works like a charm! Thanks a lot you guys! I'll definetely have a look at making my code a little cleaner too haha. Thanks a lot to the lot of you champs. Also, quick shoutout to @akapowl for making a CodePen because I was too lazy too :'> Appreciate it 3 Link to comment Share on other sites More sharing options...
akapowl Posted August 4, 2020 Share Posted August 4, 2020 One more thing: When chaining the tweens up one after the other as tightly as that, I saw, that you will eventually still get jumps, when using scrub: 1 and scrolling too fast, because the scrubbed value is yet to be applied when the next tween already starts. So I guess, either just use scrub: true and lose that nice effect, or make sure to implement some "buffer-zones" in your markup to give it enough air to breathe, when you want to keep that nice effect, the scrub: 1 delivers. Someone please correct me if I am wrong or if there is another way to avoid that. 1 Link to comment Share on other sites More sharing options...
Tonycre8 Posted August 4, 2020 Author Share Posted August 4, 2020 6 minutes ago, akapowl said: One more thing: When chaining the tweens up one after the other as tightly as that, I saw, that you will eventually still get jumps, when using scrub: 1 and scrolling too fast, because the scrubbed value is yet to be applied when the next tween already starts. So I guess, either just use scrub: true and lose that nice effect, or make sure to implement some "buffer-zones" in your markup to give it enough air to breathe, when you want to keep that nice effect, the scrub: 1 delivers. Someone please correct me if I am wrong or if there is another way to avoid that. I was going to say, I was just experiencing a little issue with the scrolling where the object has some trouble flicking about a bit haha Not so sure how to implement buffer-zones though. Would these be like, additional divs to end on? I'm a bit new haha Link to comment Share on other sites More sharing options...
ZachSaucier Posted August 4, 2020 Share Posted August 4, 2020 25 minutes ago, Tonycre8 said: Not so sure how to implement buffer-zones though. Would these be like, additional divs to end on? Sure that's one way. Or using margin to push content down. 32 minutes ago, akapowl said: Someone please correct me if I am wrong or if there is another way to avoid that. If you made all of the tweens parts of a larger timeline and applied the ScrollTrigger to the timeline instead of the tweens in the timeline then it would avoid jumping. 2 Link to comment Share on other sites More sharing options...
akapowl Posted August 4, 2020 Share Posted August 4, 2020 On 8/4/2020 at 2:48 PM, ZachSaucier said: If you made all of the tweens parts of a larger timeline and applied the ScrollTrigger to the timeline instead of the tweens in the timeline then it would avoid jumping. Like so then, right? See the Pen d84ca98a04e21d5fcc63d0f9d5d43d3d by akapowl (@akapowl) on CodePen Works like a charm! Thanks a lot @ZachSaucier 1 Link to comment Share on other sites More sharing options...
Tonycre8 Posted August 4, 2020 Author Share Posted August 4, 2020 Okay so with that example then, how would I get it to properly stagger between slides? I can't get the start times to quite align. For example, I've copied a bit of what akapowl has and tried messing with it, but the animations trigger too quickly. Where I want one option to be tweened later on, it tweens it straight away. As an example in this image, the car should only be rotated fully like this when the pink is fully visible. I.e. and entire slide covers the view. But it's already at this point by the time I get to the second slide. Which is odd, considering I told it to **start** at the second slide, but it's now ending it's animation on there. Here's a quick snippet: let car_anim = gsap.timeline({ scrollTrigger: { trigger: "#slide2", endTrigger: "#slide5", start: "top top", end: "+=1000", scrub: 1 } }) ScrollTrigger.defaults({ immediateRender: false, ease: Power1.inOut }) car_anim // Slide 2 .to(scene.rotation, {y: -1.6}, 0) .to(camera.position, {x: -0.1}, 0) // Slide 3 .to(scene.rotation, {z: 1.6}) // This happens too quickly! Any advice on how to get that to work? So that it only triggers when it hits the next slide? Link to comment Share on other sites More sharing options...
ZachSaucier Posted August 4, 2020 Share Posted August 4, 2020 I think you should read about how scrub works in the docs (the section "How does duration work with scrub: true?"). It will explain how it works. Short answer: add spacing between your tweens. 2 Link to comment Share on other sites More sharing options...
ZachSaucier Posted August 4, 2020 Share Posted August 4, 2020 You might also learn from this pen created by Steve G: See the Pen GRooLza by ste-vg (@ste-vg) on CodePen 4 Link to comment Share on other sites More sharing options...
Tonycre8 Posted August 5, 2020 Author Share Posted August 5, 2020 Okay I finally got it! I'll drop a video here to just to show you guys. Just had to tweet how I was doing things, and adding labels too to keep the timeline going. Here's a snippet of what's changed: let car_anim = gsap.timeline({ duration: 1, scrollTrigger: { trigger: "#slide2", endTrigger: "#slide4", start: "top top", end: "+=3000", scrub: 1, markers: true } }) ScrollTrigger.defaults({ immediateRender: false, ease: Power1.inOut }) // Slide 2 car_anim .add("slide2", 0) .add("slide3", 10) .add("slide4", 20) car_anim .to(scene.rotation, {y: -1.55, duration: 4}, "slide2") .to(camera.position, {x: -0.1, duration: 4}, "slide2") // Slide 3 .to(scene.rotation, {z: 1.6, duration: 4}, "slide3") // Slide 4 .to(camera.position, {x: -.15, z: 4.2, duration: 4}, "slide4") .to(scene.rotation, {z: .035, y: -3.15, duration: 4}, "slide4") And then here's the video: Thanks a lot you guys for the help! -Tony 5 Link to comment Share on other sites More sharing options...
GreenSock Posted August 5, 2020 Share Posted August 5, 2020 Nice job! Thanks for sharing. 1 Link to comment Share on other sites More sharing options...
thvu Posted September 2, 2021 Share Posted September 2, 2021 Hello experts, I am also experiencing this issue in my Typescript setup with react-three-fiber and gsap ScrollTrigger. I can't seem to apply the fix in my code: import { gsap } from "gsap"; import { ScrollTrigger } from "gsap/ScrollTrigger"; gsap.registerPlugin(ScrollTrigger); // this doesn't work ScrollTrigger.defaults({ immediateRender: false, ease: "power1.inOut", scrub: true, } as any); function AnimationWrapper() { const { scene, camera } = useThree(); const tl = gsap.timeline(); useEffect(() => { scene.rotation.set(0, 3.1, 0); tl .to(scene.rotation, { y: 1.65, scrollTrigger: { trigger: ".section-two", // this won't work either immediateRender: false, scrub: true, end: "top top", } as any, }) .to(scene.rotation, { y: 3.1, scrollTrigger: { trigger: ".section-three", // this won't work either immediateRender: false, scrub: true, end: "top top", } as any, }); }, []); return null; } Is this solution still viable? 1 Link to comment Share on other sites More sharing options...
akapowl Posted September 2, 2021 Share Posted September 2, 2021 Hey @thvu, I am not familiar with react whatsoever, so in case this is a react problem, someone else would have to jump in. But I notice you are making one of the most common ScrollTrigger mistakes there, nesting ScrollTriggers inside of individual tweens of a timeline. That won't work like that. For timelines, ScrollTriggers are supposed to be declared in the timeline object only and not on individual tweens. If you need to trigger things at different trigger-points, you have the options to set up individual tweens with individual ScrollTriggers or create one longer timeline and space things out correctly via durations and/or the position parameter. Both options are to be seen in the examples above. If changing that doesn't help you resolve your issues, it would be awesome if you could create a minimal demo for us to tinker with. Welcome to the GSAP forums 4 Link to comment Share on other sites More sharing options...
thvu Posted September 2, 2021 Share Posted September 2, 2021 Hi @akapowl, Thanks for pointing out my rookie mistake. I finally got it working by applying your suggestion. Interestingly, I don't need to use `ScrollTrigger.defaults()` with `immediateRender`. It just works. For anyone having the same issue, here's what it look like (Hopefully I can find sometime to add a working codepen later): import { gsap } from "gsap"; import { ScrollTrigger } from "gsap/ScrollTrigger"; gsap.registerPlugin(ScrollTrigger); function AnimationWrapper() { const { scene, camera } = useThree(); const tl = gsap.timeline({ scrollTrigger: { trigger: ".section-two", endTrigger: ".section-five", scrub: true, }, }); useEffect(() => { scene.rotation.set(0, 3.1, 0); tl .to(scene.rotation, { y: 1.65 }) .to(scene.rotation, { y: 3.1 }); }, []); return null; } 1 Link to comment Share on other sites More sharing options...
OSUblake Posted September 2, 2021 Share Posted September 2, 2021 34 minutes ago, thvu said: `ScrollTrigger.defaults()` with `immediateRender`. It just works. immediateRender isn't a default. That's why you had to use any. interface Vars { anticipatePin?: number; end?: string | number | StartEndFunc; endTrigger?: gsap.DOMTarget; horizontal?: boolean; id?: string; invalidateOnRefresh?: boolean; markers?: boolean | MarkersVars; once?: boolean; onEnter?: Callback; onEnterBack?: Callback; onLeave?: Callback; onLeaveBack?: Callback; onRefresh?: Callback; onRefreshInit?: Callback; onSnapComplete?: Callback; onScrubComplete?: Callback; onUpdate?: Callback; onToggle?: Callback; pin?: boolean | gsap.DOMTarget; pinnedContainer?: gsap.DOMTarget; pinReparent?: boolean; pinSpacing?: boolean | string; pinType?: "fixed" | "transform"; refreshPriority?: number; scroller?: gsap.DOMTarget | Window; scrub?: boolean | number; snap?: number | number[] | "labels" | "labelsDirectional" | SnapFunc | SnapVars; start?: string | number | StartEndFunc; toggleActions?: string; toggleClass?: string | ToggleClassVars; trigger?: gsap.DOMTarget; } Link to comment Share on other sites More sharing options...
OSUblake Posted September 2, 2021 Share Posted September 2, 2021 6 minutes ago, OSUblake said: immediateRender isn't a default. That's why you had to use any. @GreenSock are immediateRender and ease supposed to be a valid ScrollTrigger vars? Link to comment Share on other sites More sharing options...
GreenSock Posted September 2, 2021 Share Posted September 2, 2021 3 hours ago, OSUblake said: @GreenSock are immediateRender and ease supposed to be a valid ScrollTrigger vars? Technically ScrollTrigger will recognize if you put immediateRender in the ScrollTrigger vars or the tween's vars object. By default, ScrollTrigger always immediately renders its animation in order to maximize performance and avoid a few edge case annoyances, but you can set immediateRender: false if you prefer (in either place). ease, however, does NOT belong inside the ScrollTrigger vars object. That's tween-specific. 1 1 Link to comment Share on other sites More sharing options...
thvu Posted September 3, 2021 Share Posted September 3, 2021 Here's a working example of react-three-fiber and gsap ScrollTrigger. I basically took @akapowl's pen and rewrite it: See the Pen rNwLKOj by thanghuuvu (@thanghuuvu) on CodePen Edit: I left out the opacity animation. Somehow adding it causes unwanted flickering. 1 Link to comment Share on other sites More sharing options...
OSUblake Posted September 3, 2021 Share Posted September 3, 2021 You should never create timelines/tweens outside of a hook. Once the state updates, it's going to create a brand new timeline and scroll trigger, messing everything up. I broke it for you ? See the Pen eYRzLJw by GreenSock (@GreenSock) on CodePen Be sure to check out our React Guide and the Creating and Controlling Timelines section. 5 Link to comment Share on other sites More sharing options...
thvu Posted September 3, 2021 Share Posted September 3, 2021 @OSUblake perhaps that's why I experienced some flickering if I add the opacity animation in @akapowl's pen. I fixed the pen, thank you for being so helpful! Link to comment Share on other sites More sharing options...
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