Jump to content
GreenSock

ashthornton

Enable ScrollTrigger on tweens when ready

Recommended Posts

I've had a look around to see if anyone else has asked about this but couldn't find anything in the sea of ScrollTrigger questions recently!

 

Basically I want to know if there's a way to set up tweens for a page which have ScrollTrigger attached, but have the ScrollTrigger disabled/inactive until I say so.

 

For example, when you're loading a page you'd build all your tweens, but you'd have a page loader/transition covering the page. Meaning the trigger elements are technically in view but you don't want them to trigger the tween until you've finished the loader/transition and hit go.

 

I can't find anything in the docs apart from the enable/disable functions on a ScrollTrigger instance, but this doesn't seem to work, even if the tweens are paused before I run .enable().

Link to comment
Share on other sites

Hey ashthornton and welcome.

 

Yes, that is definitely possible. The trick is to not create the ScrollTriggers until you want the animation to start playing. I show how to do that when the page is scrolled the first time in this thread:

 

Link to comment
Share on other sites

Thanks for the tip @ZachSaucier. It's a shame ScrollTrigger doesn't pay attention to the paused option in a tween/timeline by default and wait til you hit play() or something similar to activate, rather than using this workaround. But it works so thank you!

Link to comment
Share on other sites

7 hours ago, ashthornton said:

Thanks for the tip @ZachSaucier. It's a shame ScrollTrigger doesn't pay attention to the paused option in a tween/timeline by default and wait til you hit play() or something similar to activate, rather than using this workaround. But it works so thank you!

Can you elaborate? I have a feeling I may be misunderstanding, but one of the main purposes of applying a ScrollTrigger to an animation is so that the ScrollTrigger would control the animation (play, pause, scrub, whatever). It automatically pauses it initially. So why would it matter if you also paused it first? Like...how exactly are you wanting it to behave in an ideal world? If ScrollTrigger sees that you pause the tween on the outside, you want it to refuse to play/restart/scrub/whatever? It'd need to "watch" the tween to sense when you call play() and then...take control in which case it may or may not actually play depending on the scroll position? If so, that seems pretty odd to me (to call play() on an animation and it refuses to play). 

 

19 hours ago, ashthornton said:

I can't find anything in the docs apart from the enable/disable functions on a ScrollTrigger instance, but this doesn't seem to work, even if the tweens are paused before I run .enable().

Seems to work fine for me - am I missing something? 

let tween = gsap.to(".box", {
	x: 1000,
	scrollTrigger: {
		trigger: ".box-1",
		start: "top top",
		end: "+=500",
		scrub: true,
		pin: true
	}
});

tween.scrollTrigger.disable();

// simulate a delay (like waiting for a loader to finish)
gsap.delayedCall(2, () => tween.scrollTrigger.enable());

 

  • Like 1
Link to comment
Share on other sites

31 minutes ago, GreenSock said:

Can you elaborate? I have a feeling I may be misunderstanding, but one of the main purposes of applying a ScrollTrigger to an animation is so that the ScrollTrigger would control the animation (play, pause, scrub, whatever). It automatically pauses it initially. So why would it matter if you also paused it first? Like...how exactly are you wanting it to behave in an ideal world? If ScrollTrigger sees that you pause the tween on the outside, you want it to refuse to play/restart/scrub/whatever? It'd need to "watch" the tween to sense when you call play() and then...take control in which case it may or may not actually play depending on the scroll position? If so, that seems pretty odd to me (to call play() on an animation and it refuses to play). 

 

My comment really was in response to the solution that ZachSaucier posted as I found it contradictory that you'd need to create a timeline that pauses itself as soon as it starts playing. It seemed that was the only way which was strange, hence the feedback, but having looked at your demo code I realised I must've been targeting the wrong thing/tween reference or something, I can't remember now! Disabling the scrollTrigger instance immediately after creation and enabling again later works.

 

Thanks for clarifying!

  • Like 2
Link to comment
Share on other sites

I just remembered what I was doing @GreenSock.

 

See the CodePen. The method works when the ScrollTrigger instance is attached to a Timeline, but not when its a singular Tween:

 

Update: using your example, it seems that changing the start property allows it to be delayed, but when it's the default that's when it starts immediately.

 

See the Pen 48de17aa88051235647253ef4e6ed76c by ashthornton (@ashthornton) on CodePen

  • Thanks 1
Link to comment
Share on other sites

Hm, I have a feeling I'm missing something obvious, @ashthornton, but I followed your instructions with the CodePen you provided and it seemed to work exactly as expected. Are you saying that it behaves totally differently for you if it's in a timeline vs. in a singular tween? Any tips on replicating that behavior? 

Link to comment
Share on other sites

Ok I've updated that Codepen with a reduced example to make sure we're seeing the same thing. So in its current state, the h1s all turn red immediately. Do you also get the same result?

 

All the h1s turn red immediately when using the singular .to tween. Even though the ScrollTrigger instance is disabled immediately after creation and enabled 2 seconds later in the .delayedCall. This is the unexpected behaviour as it's not waiting 2 seconds to enable.

 

But if you disable the singular .to tween and enable the .timeline the h1s turn red after the set 2 seconds. This is the expected behaviour.

 

Interestingly, with the singular tween enabled all h1s that are visible in the viewport immediately turn red, but if you scroll down before the 2 seconds you'll see they are still black and then turn red after the 2 second delay.

  • Like 1
Link to comment
Share on other sites

Yes, I see what you mean. I can't say it's unexpected behavior, but see why you'd think it is. Let me explain...

 

Timelines are very unique in the sense that they're populated AFTER they're created. When you call gsap.timeline({...scrollTrigger:{...}}), the timeline is completely empty. Zero duration. The ScrollTrigger waits for one tick to perform its refresh() so that there's actually data in there to work with and calculate things properly (otherwise setting the progress of the animation, for example, couldn't work). This is a GOOD thing, but it's not necessary for individual tweens because they don't get populated after instantiation, thus they can refresh() right away. And keep in mind that the "start" and "end" values for the ScrollTrigger get populated in the refresh() call. It's best to have it happen as soon as it can so that things are set up properly. 

 

Each ScrollTrigger initially pauses the animation it's linked to. And then when it updates (refresh() triggers an update), it figures out if it's active or not and responds accordingly. So in your example, the ScrollTriggers linked to individual tweens refreshed immediately and updated, thus if their element is on-screen, they started playing (as they should). And THEN you called disable() on the ScrollTrigger which indeed caused the ScrollTrigger to relinquish control, but at that point they were already playing so they kept playing. Not a bug.

 

The timelines, however, needed to wait that one tick before refreshing/updating...and by the time that happened, you had already called disable() on those, so they never started playing. 

 

See the logic? 

 

It's doing what it should even though it appears odd in your edge case scenario. 

 

Now that you understand how things work under the hood, you can adjust accordingly. If your goal is to have things paused initially, just do so explicitly, and then when you enable(), check to see if the ScrollTrigger is active and play() it if so:

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

 

Does that clear things up? 

  • Like 3
Link to comment
Share on other sites

Thanks for taking the time to explain that @GreenSock, it definitely makes sense and I've now got a nice setup that allows me to create page entrance animations utilising ScrollTrigger.

 

Really appreciate the whole library and you catering for us scroll-jackers ;) And on that, thanks for featuring my library in the docs! I know you made ScrollTrigger to avoid scroll-jacking, I similarly made ASScroll to fix the issues in every smooth scroll lib I've seen, they all seem to unnecessarily take over native functionality even down to manually mapping keyboard buttons to scroll, so I appreciate every bit of exposure for it :)

  • Thanks 1
Link to comment
Share on other sites

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 account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×