Jump to content
Search Community

GreenSock last won the day on March 24

GreenSock had the most liked content!

GreenSock

Administrators
  • Posts

    23,068
  • Joined

  • Last visited

  • Days Won

    815

Everything posted by GreenSock

  1. You're making one of the common ScrollTrigger mistakes - nesting ScrollTriggers inside of a timeline. That can't logically work because you can't have the parent timeline AND the scroll position both control the same playhead(s), as they could be going in different directions. So either separate out each tween into its own individual tween that has a ScrollTrigger on it, OR put your tweens into a timeline that has a ScrollTrigger applied to that timeline. As for once: true, you could do something like this instead: onLeave: self => { let max = ScrollTrigger.maxScroll(window), // record maximum scroll amount, and the current scroll position scroll = self.scroll(); self.kill(true, true); self.scroll(scroll - (max - ScrollTrigger.maxScroll(window))); // adjust the scroll based on how much was lost when self.kill() was called. } https://codepen.io/GreenSock/pen/jORLdmy?editors=0010 (I didn't fix all your nested ScrollTriggers - you'll need to work through those).
  2. No, sorry. But hopefully my description gets you going in the right direction. I'm not sure if it'd be any easier or work the way you want, but another idea is to use DrawSVGPlugin to basically draw a super fat orange stroke, and see if you can do a blur effect on the end of it (you'd likely need to clip/mask the other end to keep that sharp). If you need more help, we do offer paid consulting services which you can contact us about. 👍 Good luck with the project, and thanks for being a Club GSAP member! 💚
  3. That's exactly why - you put it outside the wrapper. Why would you do that? If your goal is to have a position: fixed element (not pinned with ScrollTrigger), that's fine to have it outside the wrapper. Otherwise, it should be inside. https://codepen.io/GreenSock/pen/KKYvbRw
  4. Is that orange "fill" supposed to be gradated like that (feathered/blurred)? That makes things more difficult, but not impossible. I mean in its most simple form, you could place pie-cut segments of orange along the circle, and just animate the opacity in a staggered fashion. If you need it to be gradated in a totally smooth way, you might need to make a .png that's basically HALF of the circle. Make a mask of the opposite half, and animate the rotation of that .png so that it comes into that mask on the other half. Then when that reaches 180 degrees (half the circle), pop that out of the mask and keep going to 360 degrees while you do exactly the same thing with another .png that's half the circle but fully opaque orange. Rinse and repeat until that first one gets to 540 degrees and the other two half-circle .png files are filling the circle. Good luck!
  5. Yeah, that's because you're listening to the window's "scroll" event, but ScrollSmoother gradually applies that. So you're using stale values. Just set up an onUpdate event handler on the ScrollSmoother, and for maximum performance you can leverage a cache boolean to only trigger updates when absolutely necessary: https://codepen.io/GreenSock/pen/RwOZBjp?editors=0010 Is that what you're looking for?
  6. I built a helper function specifically for this: https://codepen.io/GreenSock/pen/vYMJdjB?editors=0010 Related forums post: I hope that helps!
  7. Hi @codechirag. It's super tough to troubleshoot a live site. There are way too many factors and we can't really tweak things to see what's going on. If you'd like some help, please provide a minimal demo, like a Stackblitz or CodePen. Here's a starter template you can fork: https://stackblitz.com/edit/react-cxv92j Also keep in mind that Split-Type is not a GSAP product, so we can't really support that. Our tool is SplitText. 🙂
  8. Sure, just put this at the end of your timeline: .to(".background-tint", { opacity: 0.5, duration: tl.duration()}, 0) Notice it's duration is the whole duration of the timeline, and it's being inserted at a time of 0, thus it takes up the whole animation. I read that a few times and I'm still totally lost. Sorry - can you please be very specific about what the problem is and how we can reproduce it? What markup did you add exactly? How did it "mess up"?
  9. When an animation renders for the first time, it records the start/end values internally so that it can very quickly interpolate between them during the animation. You've created a scenario where you actually want to CHANGE those values on resize. invalidate() will do exactly that - it flushes any recorded start/end values so that on the next render of that animation, it'll re-calculate those. With that in mind, you can just invalidate the animation and restore its progress: window.addEventListener("resize", () => { let progress = tl.progress(); // remember the progress tl.progress(0).invalidate().progress(progress); // rewind to the beginning, invalidate to flush any recorded start/end values, then restore the progress }); https://codepen.io/GreenSock/pen/BaEdQBy?editors=0110 Is that what you're looking for? You could also consider using a gsap.matchMedia(). There are many ways to accomplish something similar 🙂
  10. That's not an Observer issue - it's just that Apple touchpads and magic mouse keep emitting "wheel" events for a while after you release - it's what creates the momentum effect. So if you flick very strongly, you're firing the animation and then when the animation is done, there are still wheel events getting fired from the strong flick (momentum), thus it triggers another animation because those residual wheel events triggered the Observer's onUp()/onDown(). See the problem? So you could add some logic to sense that condition and work around it, like maybe: https://codepen.io/GreenSock/pen/jORLMLO?editors=0010 To make things easy, I just use ScrollTrigger's "scrollEnd" event handler but you could wire that all up yourself manually if you prefer not to use ScrollTrigger. Does that work better for you?
  11. That sounds like it could simply be a consequence of the fact that most modern browsers handle scrolling on a completely different thread than the main JS thread, thus it renders the content as if it was scrolled (without any ScrollTrigger logic applied) first, and THEN when the JS thread executes, ScrollTrigger does what it must do to pin the element, making it look like it jumps back. Two things you can try (assuming that's the problem you're noticing): 1) Try setting ScrollTrigger.normalizeScroll(true) which forces all the scrolling to occur on the main thread. 2) Use a scroll smoothing library like ScrollSmoother or Lenis which accomplishes a similar thing, unhooking the native scroll and implementing JS-based scrolling instead.
  12. Not if you're doing some kind of routing. The first time the page loads, the window will fire the "load" event after the images load (assuming no lazy loading), but then if you're doing routing in React to go to other "pages" (content) without the browser actually loading an entirely new URL, then the "load" event doesn't fire, thus ScrollTrigger.refresh() isn't automatically called. See what I mean? Again, a minimal demo will go a very long way toward getting you a solid answer. I was totally just guessing here.
  13. Sorry if that was confusing, but let me clarify... You define the start/end values in the vars object when creating a ScrollTrigger, but then that gets parsed into a numeric property value which is what you were looking at in the docs. That property on the ScrollTrigger instance is ALWAYS a number because it's the result of the calculations at that particular viewport size. If you resize the viewport, the number would likely change. But again, that's the result of all the calculations that are based on the start/end values you defined in your vars object. So for example, let's say you did this: let st = ScrollTrigger.create({ trigger: ".trigger", start: "top center" }); Notice the start was set to "top center" which just means "when the top of the trigger element hits the center of the viewport", but then if you check st.start you'll see a number like 523 which simply means that the ScrollTrigger will start when the page scrolls 523px from the top - that is where the top of the trigger hits the center of the viewport. If you then resized the viewport and check st.start, it may be a completely different number, like 418 which means that now that ScrollTrigger will start when the page scrolls 418px from the top because now that is where the top of the trigger hits the center of the viewport. So you were looking at that property in the docs rather than the vars configuration object properties. Here's an excerpt from the ScrollTrigger docs for the vars.start property: Is that what you're looking for?
  14. You can't have nested pinning, but you could probably use position: sticky like this: https://codepen.io/GreenSock/pen/MWRoQoV?editors=0010 Is that what you're looking for?
  15. Yeah, a minimal demo will go a very long way toward getting you a solid answer, but it kinda sounds like maybe you are lazy-loading images such that they don't trigger the window's "load" event when they're done (which would fire the ScrollTrigger.refresh()). If that's the case, you need to either call ScrollTrigger.refresh() once your page layout is settled (after images finish loading), or explicitly set the width/height of your images so that there is no shifting of the layout when they finish loading.
  16. I've never heard of anything like that - ScrollTrigger.normalizeScroll() doesn't somehow change the scrolling speed. It uses the data provided by the touchmove events to determine that amount of delta. I wonder if you've got something else interfering. It's super difficult to troubleshoot blind, though. Do you have a minimal demo, like a CodePen or Stackblitz that clearly illustrates the issue?
  17. That's because the browser's getComputedStyle() doesn't return the calc() data properly. So when GSAP gets the starting (from) value, the browser gives it a value with no calc(), and then the end string has calc() in it with multiple numbers. It's the same issue with your other demos (mis-matched data/quantities). There are two solutions: 1) Use CSS variables: https://codepen.io/GreenSock/pen/RwOgRJQ 2) Use a normal .fromTo() tween so that GSAP understands what you're trying to do and can use the raw starting value instead of a computed one from the browser: https://codepen.io/GreenSock/pen/wvZeWXQ?editors=0110
  18. Is the same issue as in your other post - answer is here:
  19. That's because your starting and ending strings have different amounts of numbers and are formatted differently, thus the complex string animation can't function the way you expected. There are two pretty easy solutions: 1) Just make sure the start/end strings have matching formatting and quantity of numbers: https://codepen.io/GreenSock/pen/OJGgXRQ?editors=0010 2) Use a CSS variable to simplify things: https://codepen.io/GreenSock/pen/GRLEqNO Does that clear things up?
  20. A minimal demo will be essential for troubleshooting, but I wanted to point out that I assume this is what you're looking for in terms of the GSAP code: useGSAP(()=>{ gsap.to(group.current.rotation, { y: "+=1", repeat: -1, ease: 'none', repeatRefresh: true, duration: 4 }); }); I'm not familiar with Three.js, but you might need to trigger a render in an onUpdate (if updating the .rotation doesn't automatically render that change)
  21. Nothing has changed about how autoAlpha works. I noticed a few problems in your demo: Your "wrapper" variable was null. You called detail.querySelector(".splitScreenPinning__wrapper") - but that's actually an ancestor of the detail element, so querySelector() won't find it. I assume you meant to use closest()? Since your ScrollTrigger's trigger was null, it was defaulting to the viewport/body, and therefore your start: "top 80%" and end "top 50%" resulted in NEGATIVE start/end scroll positions, so your whole animation was FINISHED at the very top of the page, before scrolling. That's why autoAlpha: 0 was already set. I'm pretty sure you meant to set the trigger to the detail element. https://codepen.io/GreenSock/pen/NWmjJLm?editors=1010 Does that clear things up?
  22. That looks like all React-related logic. You were adding a .from() animation to the timeline that targets usernameFormRef.current...but at the end of the previous tween you're calling setJoined() which triggers a re-render of the component, so that element that you were tweening with the from() tween no longer exists! It was replaced by React with something else or removed altogether. When you setJoined(), React takes a little time to actually create/render the new state, so you have to wait for React to finish that before you can actually animate what it creates. That's the perfect case for a useGSAP() with a dependencies Array - it'll get triggered as soon as a dependency changes and is rendered. You created your timeline instance outside of any context, therefore it won't get reverted or cleaned up properly. You don't really need to set a scope if you're never using any selector text, just so you know. It doesn't hurt anything, of course. It's just that it is only useful for selector text. Are you trying to do this?: https://stackblitz.com/edit/nextjs-7ex2g3?file=app%2Fcomponents%2FControls.tsx
  23. Here's a great article from @PointC: https://www.motiontricks.com/svg-dashed-line-animation/ And yes, absolutely, GSAP's DrawSVGPlugin is great for that kind of thing. You'll need a Club GSAP membership. https://gsap.com/pricing
  24. I don't see any of that code in your demo. Am I missing something? Like Cassie said, you're asking GSAP to tween that image.src attribute which doesn't make any sense here. GSAP is doing what you're asking it to do - finding the numbers in that attribute string and animating between those, but of course that's not what you want. Why are you even setting a duration there - what exactly are you expecting to happen over the course of 1 second? You can certainly use a .set() to just set the attribute to a new value. It just doesn't make any sense to tween that. If you still need help, please make sure you post a minimal demo (like a CodePen) that clearly illustrates the issue and we'd be happy to answer any GSAP-specific questions.
×
×
  • Create New...