Jose Pio Posted July 22, 2020 Posted July 22, 2020 Hello GSAP community! How's everybody? I would like to know if you could help me. I have two timelines that are triggered by ScrollTrigger when I am within the range of the start attribute. The onEnter and onLeaveBack events of the same ScrollTrigger instance work with timeline.play () and timeline.reverse () respectively, the problem starts when the two timelines are fired and loses the smoothness of the transition. Example: When I move from div.p3 to div.p1 tl2 is interrupted, it is interrupted by tl1 and overwrites everything. I don't quite understand if it could be fixed with invalidate or overwrite. Thank you very much in advance! Here I attach my codepen: <body> <div id="box"></div> <div class="p1 pages"></div> <div class="p2 pages"></div> <div class="p3 pages"></div> </body> const box = document.getElementById('box') const tl1 = gsap.timeline({paused: true}) const tl2 = gsap.timeline({paused: true}) tl1.to(box, { duration: 3, x: 350, y: 30, rotation: 45, ease: "expo.inOut" }) tl2.to(box, { duration: 3, x: 20, y: 20, rotation: -125, ease: "expo.inOut" }) ScrollTrigger.create({ trigger: ".p2", start: "0 top", markers: true, onEnter: () => tl1.play(), onLeaveBack: () => tl1.reverse() }) ScrollTrigger.create({ trigger: ".p3", start: "0 top", markers: true, onEnter: () => tl2.play(), onLeaveBack: () => tl2.reverse() }) PD: Excuse my bad English. See the Pen PoZVEva by cpiocova (@cpiocova) on CodePen.
ZachSaucier Posted July 22, 2020 Posted July 22, 2020 Hey Jose and welcome to the GreenSock forums. There are several ways to handle this sort of thing, the best depends on the effect that you want. If you want it to be smooth (no jumps) no matter what you have a couple of options: Use a single timeline to animate between the various states. Inside of the callbacks you animate the timeline's progress to a certain location. You should probably use overwriting on that tween. It'd look something like this: // helper function function getProgressOfLabel(tl, label) { return tl.labels[label] / tl.totalDuration(); } // in the ScrollTrigger onEnter: () => gsap.to(tl, {progress: getProgressOfLabel(tl, "myLabel"), ease: "none", overwrite: "auto"}) Create tweens when you need to use them (instead of using ones created in the past). That way they can use the current value as the start value. You should use overwriting on that tween. This is probably the method that I'd use. It'd look something like this: onEnter: () => gsap.to(box, { duration: 3, x: 350, y: 30, rotation: 45, ease: "expo.inOut", overwrite: "auto" }) 1
Jose Pio Posted July 22, 2020 Author Posted July 22, 2020 Thanks for helping me Zach. The first one I do not understand very well, I apply it, but I have no results yet, there are no tween. The second option is very good but I have a somewhat long timeline and you would have to build your reverse as well. Is that I have a mesh in position fixed, which rotates and moves its position as I scroll. If I scroll slowly, so that the tween duration ends before triggering the onEnter and onLeaveBack callbacks, it works otherwise, it doesn't work. But what I want is that if I scroll very fast, the timelines run smoothly. Thanks Zach and sorry for the inconvenience!
ZachSaucier Posted July 22, 2020 Posted July 22, 2020 To me it seems that this is primarily a logical issue: what do you want to happen when there's conflict? 1
Jose Pio Posted July 22, 2020 Author Posted July 22, 2020 Here I attach the screenshots of the behaviors. 1: Without Scroll Trigger: 2: ScrollTrigger onEnter (Complete First Timeline): 3: ScrollTrigger onEnter (Complete second timeline): from 1 to 2 is fine. from 2 to 3, this fine. the problem is when I scroll quickly from 1 to 3 or from 3 to 1 IN brief moments I will edit the post and attach a video that exemplifies the expected result and the result obtained. Thanks in advance Zach !!
ZachSaucier Posted July 22, 2020 Posted July 22, 2020 To do that I still recommend the second approach. You could use a timeline instead of a tween. Unfortunately it's hard for us to be able to help as we can't see the actual project.
Jose Pio Posted July 22, 2020 Author Posted July 22, 2020 Hi! Here I uploaded to YouTube the videos of the expected and obtained behavior. The two timelines modify the properties of the same object so there is a problem when the two timelines are fired at the same moment (When I scroll faster for the duration of each timeline) Expected Behavior: Obtained Behavior:
ZachSaucier Posted July 22, 2020 Posted July 22, 2020 I understand what you're wanting. I provided insight as to how you can fix this (the second method in my first post). But we can't help you implement it into your project since you haven't shared any code for it.
Jose Pio Posted July 22, 2020 Author Posted July 22, 2020 Here is an excerpt from where I have the conflict. Thanks for the support Zach and sorry for my slowness! ironScroll.js Here are the timelines called in Main.js export const iron1 = (model, material, tl, node) => { const aboutTitle = node.querySelectorAll(".about-info-line > div") const paragraph = node.querySelector(".about-info p") tl.to( material, { duration: 1.5, ease: "expo.inOut", }, "iron" ) tl.to( material.emissive, { duration: 1.5, r: 1, g: 1, b: 0, ease: "expo.inOut", }, "iron" ) tl.to( model.position, { duration: 2, x: 2.6, y: -8.77, z: -8.47, ease: "expo.inOut", }, "iron" ) tl.to( model.rotation, { duration: 2, x: 0.39, y: -1.09, ease: "expo.inOut", }, "iron" ) tl.from( [...aboutTitle], { duration: 1, y: 44, ease: "power3.out", stagger: 0.15, }, "iron+=.6" ) tl.from( paragraph, { duration: 1, y: 20, ease: "power3.out", }, "iron+=1.4" ) tl.to( paragraph, { opacity: 1, duration: 1, ease: "power3.out", }, "iron+=1.4" ) tl.to( [...aboutTitle], { duration: 0, opacity: 1, }, "iron" ) } export const iron2 = (model, material, tl, node) => { tl.to( model.rotation, { duration: 2, x: 0, y: -2.34, z: -0.05, ease: "expo.inOut", }, "iron" ) tl.to( model.position, { duration: 2, x: 3.47, y: -7.1, z: -13.27, ease: "expo.inOut", }, "iron" ) } Main.js ironGeo.current and ironMat.current are the objects that the timeline will interpolate import {iron1, iron2} from './ironScroll' const animIron1 = gsap.timeline({ paused: true }) const animIron2 = gsap.timeline({ paused: true }) const contactWrapper = document.querySelector(".contact-wrapper") iron1(ironGeo.current, ironMat.current, animIron1, contactWrapper) iron2(ironGeo.current, ironMat.current, animIron2, contactWrapper) ScrollTrigger.create({ trigger: ".sp-1", start: "0 top", markers: true, onEnter: () => animIron1.play(), onLeaveBack: () => animIron1.reverse(), }) ScrollTrigger.create({ trigger: ".sp-2", start: "0 top", markers: true, onEnter: () => animIron2.play(), onLeaveBack: () => animIron2.reverse(), })
ZachSaucier Posted July 22, 2020 Posted July 22, 2020 I would make a function that creates a new animation. Something like this: export const animIron2 = (model, material, node) => { const tl = gsap.timeline({defaults: {overwrite: "auto"}}); tl.to( model.rotation, { duration: 2, x: 0, y: -2.34, z: -0.05, ease: "expo.inOut", }, 0 ) tl.to( model.position, { duration: 2, x: 3.47, y: -7.1, z: -13.27, ease: "expo.inOut", }, 0 ) } Then call that function when you need it: ScrollTrigger.create({ trigger: ".sp-2", start: "0 top", markers: true, onEnter: animIron2 }) 1
Jose Pio Posted July 23, 2020 Author Posted July 23, 2020 Hello, good day! Thanks Zach. But I still have not been able to solve because with this solution I can make the transition from div.sp1 to div.sp2 but I cannot reverse (from div.sp2 to div.sp1)
ZachSaucier Posted July 23, 2020 Posted July 23, 2020 20 minutes ago, Jose Pio said: I cannot reverse Just add the same animation in the onEnterBack: ScrollTrigger.create({ trigger: ".sp-2", start: "0 top", markers: true, onEnter: animIron2, onEnterBack: animIron2 }) 1
Jose Pio Posted July 23, 2020 Author Posted July 23, 2020 4 hours ago, ZachSaucier said: Just add the same animation in the onEnterBack: ScrollTrigger.create({ trigger: ".sp-2", start: "0 top", markers: true, onEnter: animIron2, onEnterBack: animIron2 }) Thanks Zach, it already works with this solution. The only problem is that I have to write more code (2 animation logics: one with the animation logic for Enter and one with the animation logic for Leave. While with the timeline I only created 1 animation logic and I I was in charge of doing play and reverse but it was impossible for me to work and I was left with the uncertainty if there will be any way to do it with the timeline). But it works this way, thank you very much !!
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