Jump to content

GreenSock last won the day on September 22

GreenSock had the most liked content!


  • Posts

  • Joined

  • Last visited

  • Days Won


Posts posted by GreenSock

  1. From your screen capture, it kinda looks to me like a thread synchronization issue caused by the fact that modern browsers perform scrolling on a totally different thread that's not synced with the main JS thread. Have you tried either using ScrollTrigger.normalizeScroll(true) -OR- ScrollSmoother to ensure that the scroll-related updates are done on the main thread? 

  2. This is definitely beyond the scope of help we can typically provide in these free forums because there's a lot of custom logic necessary, but here's a rough idea of a different approach that hopefully helps get you going in a better direction: 

    See the Pen mdaxepQ?editors=0010 by GreenSock (@GreenSock) on CodePen


    If you need more assistance, you're welcome to contact us about paid consulting services or post in the "Jobs & Freelance" forum. Good luck! 👍

  3. @Sublimio yes, it'd be much better to start a new thread for a new question like that. 


    The problem is that you're animating the element (or parent of the element which of course also animates the element itself) that's used as the trigger. That's almost always a bad idea because it'll throw off all the start/end calculations. What you're asking to do can’t happen automatically. In this particular case you could attempt the calculations manually, like this:

    See the Pen poqLJGX?editors=1010 by GreenSock (@GreenSock) on CodePen


    I hope that helps. If you still need assistance, please start a new thread. 

  4. That's just a logic thing related to the fact that your very first color-changing section is below the trigger area So you can either add an invisible section that fills that top area as a trigger to go back to the transparent state, or you can add some conditional logic that'll animate back to the initial state in the onLeaveBack() of the very FIRST section: 

    See the Pen oNJEWXx?editors=0010 by GreenSock (@GreenSock) on CodePen


    Is that what you want? 

  5. 3 hours ago, aln said:

    This is for filter:blur() right? How do i change it to backdrop-filter which is the original question anyway? Also how can i include the webkit prefix?

    If you'd like some help, @aln, please provide a minimal demo (like a CodePen) showing what you're trying to do. The overall concept illustrated by the BlurPlugin above should apply to whatever property you want - you just have to adjust the code in the plugin to target that property. 

    • Like 1
  6. On 9/19/2023 at 2:50 AM, grizhlie said:

    Do you have any future plans to make GSAP more modular than it is currently, hence reducing its size?

    Great question. I've thought a LOT about this and on the surface it seems like a great idea, but consider the following: 

    1. The more modular, the less cacheable and "packagable". Currently, the "gsap" core object is super capable and robust. It has things like gsap.to(), gsap.timeline(), gsap.utils.*, gsap.ticker, gsap.globalTimeline, eases, etc., etc. Everything is very reliable, consistent, and ever-present. Load one file, and BOOM, you've got a ton of power at your fingertips. GSAP has been standardized on almost every ad network on the planet so that its file size doesn't count against ad budgets. Just one CDN had over 13 BILLION requests for GSAP in a single month. If we busted everything apart into little modular pieces, that might be nice for build tools but it'd be terrible for CDNs and browser caching. Please read this article: 
    2. There are lots of inter-dependencies in the core. For example, tweens get put onto a global timeline, so you can't effectively separate Tweens and Timelines. Various internal functionalities rely on some of the utility functions. Another example: gsap.matchMedia() is insanely powerful but it requires some very deep hooks into the core that affect a lot of little pieces, so it's just not feasible to pull that out into its own little modular chunk unless we degraded performance by adding a bunch of little optional hooks and callbacks elsewhere in the core to bolt in the logic in the rights spots. It just made a lot more sense to bundle that functionality in the core. Better performance, and overall less file size as a whole. 
    3. Performance or file size can sometimes be improved by reusing internal variables and chunks. So if they're all busted apart into their own little independent modules and then you try to put them together, you may end up with a larger file size because of the redundancies. For example, a lot of the easing functions are dynamically built using some helper functions that piece them all together. If they were all separated out, they'd each be bigger independently. Merging them results in overall file size savings if you're using more than a couple of them. 
    4. It'd require a BIG change to the API. Again, you couldn't just rely on gsap.to(), gsap.from(), gsap.timeline(), etc. - we'd have to create little independent exports like Tween, Timeline, etc. that you'd import which may work well for build tools but then what about people who load GSAP over a CDN? We could try to put the most common things into a "gsap" object, but again, the overall size would likely be larger for that and then it'd get more clunky API-wise because the code itself would look different if you're using a bundler/ES Modules vs doing a standard <script> load from a CDN (like Tween(...) instead of gsap.to(...)). Historically, we've put a huge priority on consistency and ease of use. It goes counter to that if ES Module code looks different than UMD code. A lot of people have invested time into learning GSAP over the years and feel comfortable with the API, so switching things up like this could be quite jarring. 
    5. Developers often think of file size in a way that's not very helpful or accurate. They simplify it to "bigger is always worse", but forget about caching or runtime performance tradeoffs or code clarity/portability/compatibility. They're quick to sacrifice all that other stuff in order to gain an average of maybe 10ms extra up-front load time...ONCE in their entire app/site. Even if it means their runtime animations bog down in the browser or their code can't be used in various other contexts. It becomes all about the initial file size hit rather than more of a holistic approach that considers all those other factors. 
    6. We've already taken a modular approach in terms of GSAP "plugins". The features we deemed critical, we put into the core and then we wrap less commonly used features in plugins. That keeps file size down.  

    I know it's easy for other newer libraries to tout small file size as a benefit to try to appear "better" than GSAP. And I have nothing against any of the other libraries in particular - use them if you like, but beware of the tradeoffs because they'll rarely mention them and we try not to talk too much about them because we don't want to seem like we're insulting the hard work of other library authors. 


    Another way of looking at it: would you rather spend an extra 8ms (or whatever tiny fraction of a second it'd take to load GSAP by your average user) once on the initial load for an entire site and get all the rich benefits of GSAP including all the functionality and runtime performance or sacrifice that for the difference in load time that virtually nobody would notice?


    All that being said, we'll certainly be looking for ways to increase modularity and minimize file size in future releases. 


    And again, it was an excellent question. Thanks for giving me a chance to blabber on about it a little bit. :)

    • Like 2
    • Haha 1
  7. Oh, I see. Well, honestly I'm on the fence about that because as a user, I absolutely HATE when websites lock the scroll like that. I can see the scrollbar and I know there's more content to get to...but I'm forcibly prevented from getting to it. Very confusing. Makes me wonder if the browser froze or there's a problem with my device. So adding a helper function like that to the official docs makes me feel a little gross, like I'm actively helping more people do that. 


    I'll ponder it. Especially if more people ask for it, perhaps I should get over my personal issue with it. 😬

    • Like 1
    • Haha 2
  8. This is the key: 

    end: () => "+=" + (large.offsetHeight - window.innerHeight),

    That makes the pinning distance correspond to the overflow/height of the content. More content would mean a bigger number there, so it stays pinned longer. And I added conditional logic to skip the whole thing if the content is too short (no overflow). 


    Does that clear things up? 

  9. 8 hours ago, Rodrigo said:

    If you want to target every touch screen (be aware that some laptops and other desktop screens do have touch events), you can use a combination of ScrollTrigger isTouch property

    Just to clarify, you can sense that directly in a matchMedia() query: 

    "(hover: none), (pointer: coarse)"

    That indicates it's touch. So you can add that to any media query. Like:

    mm.add("(max-width: 720px) and (hover: none) and (pointer: coarse)", () => {...})

    But if you're just doing conditional logic, it's totally fine to use ScrollTrigger.isTouch

  10. I'm not a fan of that approach because:

    1. Adding to the GSAP Timeline prototype gives your code a funky "smell" because anyone else looking at it might think "wait, I didn't know GSAP had a method like that..." (and it doesn't). 
    2. You're relying on Math.random() always being perfectly unique but that isn't bulletproof. You might run into a bug like that where one gets overwritten. Unlikely, but possible. There's really no reason to use a label anyway - you can just use this.duration() as the insertion point.
    3. Your code assumes the function will always return an animation which may be true in your particular case, but if I were you I'd make it more bulletproof. 

    Here's another approach that [in my opinion] is cleaner: 

    See the Pen RwExVQy?editors=0010 by GreenSock (@GreenSock) on CodePen


    You just wrap synced(...) around your function and that's it. Notice that'll also work fine if you add it at a certain position, like: 

    .add(synced(createNestedTimeline), 2)


    I hope that helps!

    • Like 2
  11. 10 hours ago, Sublimio said:

    p.s. I tried before this solution but Safari Lag with 10x scale too.

    Do you mean scale: 10? If so, I think you misunderstood the solution. 


    BAD: scale: 1 -> 10

    GOOD: scale: 0.1 -> 1 (with the native size being 10x what you had previously)


    Glad you got it working.

  12. I think you might be misunderstanding when onStart fires. According to the docs: 


    A function that should be called when the animation begins (when its time changes from 0 to some other value which can happen more than once if the tween is restarted multiple times).


    You seem to be expecting it to fire the exact moment you CREATE the animation. But at that moment, the playhead is at 0. On the very next tick, the playhead updates and moves past 0 and THAT is when onStart fires. It's when the playhead shifted away from 0. 


    From what I can tell, it's doing exactly what it's supposed to do in all your demos. 


    So let's walk through this: you're placing a callback at exactly 1 second into the main timeline. When the playhead either lands directly on that spot or anywhere PAST that spot (let's say it's 1.02), it'll fire that callback. Now inside that callback, you're creating a new timeline with an onStart. But at this point, its playhead hasn't moved at all. According to the original timeline's time, this new one starts at 1.02. On the very next tick, it updates (let's say 0.016 seconds later), and that's when the onStart of that new timeline would fire. 


    The other small problem is that you're using Date.now() for the startTime which means you're not synchronizing time with the GSAP core ticker. The entire GSAP ecosystem is completely synchronized, all based on that ticker. When you load web pages, you have no idea how long that's going to take. Your Date.now() might be at the start or at the middle or at the end of that whole startup routine that could take dozens or even hundreds of milliseconds. It's not a safe way to synchronize things. 


    If I understand your goal correctly, you'd need to adjust your new timeline's playhead according to the playhead of that parent (the one with the callback). Figure out how far past the embedded spot the playhead is. For example, if the playhead goes 0.01 seconds past that spot, you should set that new timeline's playhead to 0.01 right after creating it. 


    See the Pen Vwqyemv?editors=0010 by GreenSock (@GreenSock) on CodePen


    Does that clear things up?

    • Like 1
  13. Please read this article: 


    You're not doing proper cleanup nor are you structuring things properly in React (this has nothing to do with GSAP/Draggable - it's all React stuff). You should put things in a useLayoutEffect() to ensure it runs after the render so that the elements actually exist. Right now, you were trying to grab an element that didn't even exist at that point. 


    I assume you were looking for something like this?: 


  14. Scaling just stretches things. And border-radius is a tricky thing to set directly like that when there are different values for the different axis since it's a complex property (8-in-1). In this case, it's better to set it directly in the onUpdate rather than going through GSAP: 

    See the Pen xxmPWqa?editors=1010 by GreenSock (@GreenSock) on CodePen


    Honestly, I doubt you'd notice any real-world performance difference if you just animate width instead of trying to calculate and update the border-radius on every single tick. It's simpler to just turn off the scale: true option. 

    • Like 2