Jump to content
Search Community

Scroll Trigger - Prevent Overlaps does not appear to do anything

Samso test
Moderator Tag

Go to solution Solved by GreenSock,

Recommended Posts

Hello, I have a setup here that I want to use preventOverlaps on to make sure that the animations (on sections that are scrolled quickly) do no appear. In the demo, if you click the button for Section 4 you will see all of the animations occur before seeing the animation for that section.

 

What am I doing wrong here?

 

Based on this demo on preventOverlaps

See the Pen ZEyXPGj by GreenSock (@GreenSock) on CodePen

See the Pen MWLeazg by samso-studios (@samso-studios) on CodePen

Link to comment
Share on other sites

Hi @Samso and welcome to the GSAP forums!

 

First you have a typo in your demo, you're missing a comma here:

scrollTrigger: {
  trigger: curMarker,
  start: "top 20%",
  end: "+=100%",
  markers: true,
  toggleActions: "play none none reverse" // <- Missing comma!
  preventOverlaps: true
}

 

I think the issue here is the duration of the scroll animation. If you scroll with the scrollbar really fast using the mouse you can see that preventOverlaps is doing what is supposed to. What preventOverlaps does is to force the a GSAP instance to it's end when you reach the end point or trigger when you scroll down (going forward) or the start point or trigger when you scroll up (going backwards). That prevents that animation from keep running if another animation starts, like that you won't have two or more animations happening at the same time.

 

In your case your Tween with the ScrollTo plugin is really slow (2 seconds) which can cause the animations to keep running longer since the scroll position is changing at a slow rate so there is time for those animations to still happen.

 

Finally since you are using toggleActions, you can use complete in your onLeave action to force the animation to be completed when it leaves the end point going forward:

const st = gsap.timeline({
  scrollTrigger: {
    trigger: curMarker,
    start: "top 20%",
    end: "+=100%",
    markers: true,
    toggleActions: "play complete none reverse",
    preventOverlaps: true
  }
});

Here is a fork of your demo with those changes:

See the Pen ExryWRw by GreenSock (@GreenSock) on CodePen

 

Hopefully this helps.

Happy Tweening!

Link to comment
Share on other sites

3 hours ago, Rodrigo said:

What preventOverlaps does is to force the a GSAP instance to it's end when you reach the end point or trigger when you scroll down (going forward) or the start point or trigger when you scroll up (going backwards).

Just to clarify, with toggleActions, it'll force it to whatever that particular toggleAction part is, so if your onLeave is "none", it won't do anything when the scroll position flies past the end. If it's "complete", obviously it'd complete at that point, etc. 

Link to comment
Share on other sites

@Rodrigo @GreenSock Okay I've made the recommended changes but still not seeing what I am hoping for so let me clarify.

When I click on the scrollTo links on the top, how can I bypass any scrolled over animations?

EX: If I click "Section 4" is there a way to  show Section 1 - Out then Section 4 - In?

 

As it currently stands, you see the In and Out animation for sections 2&3. 

 

Link to comment
Share on other sites

Hi,

 

Is clear what you're trying to achieve here, but the explanation kind of remains the same. You are moving the scroll with your tween rather slowly and prevent overlaps forces a specific GSAP instance to completion after it end point has passed. When using ScrollTrigger the start/end points are specific scroll positions. If you pass through those points slowly you are giving time to the animation to actually run for some time (the time it takes the scroll to go from the start to the end points) and that's why you can see the sections 2 and 3 animating. If you scroll using the mouse pointer and the scroll bar really slow, you'll see the same result, as you can see in this screen recording:

https://i.imgur.com/ShTUVSt.mp4

 

If you go slow the animations have enough time to actually run, if you scroll fast ScrollTrigger sees that the end point has reached and forces the particular tween to completion very shortly after it starts, that's why it seems that the animations don't overlap. When scrolling slowly the animations don't overlap neither, the issue is that when the end point is reached the animation has progressed enough to actually give the impression that there is an overlap, but as soon as the end point is reached, ScrollTrigger will complete it.

 

Here is the same demo but the Tween with the ScrollTo plugin is 10 times faster, you can see the difference:

See the Pen VwgjqBd by GreenSock (@GreenSock) on CodePen

 

Hopefully this clear things up.

Happy Tweening!

Link to comment
Share on other sites

I had some comments in the code to make it clear. The general concept is to fire off a fresh animation whenever a section becomes active or inactive (onToggle). So a single ScrollTrigger for each with an onToggle. That way, you're just using trigger points to initiate individual animations when necessary.

 

Your previous technique was embedding very specific crossfades between only the neighboring sections. So you baked in "section 1" fading out with "section 2" fading in, etc. So it was all tied together. If you entered section 4, it'd have to play that very specific animation where a fully-opaque section 3 would fade out, then section 4 would fade in. No bueno. It's a logic/engineering thing. 

 

But the effect you asked for was more dynamic, where each individual section needed to handle itself on its own when it became active/inactive (not tying it together directly with the neighboring animation). 

 

Basically my approach made things very dynamic, so if you scroll from section 1 to section 4, it'd start fading out section 1 onLeave, and as you fly past section 2, that'd start as well but you'd hit its end so quickly that the "out" tween would overwrite the "in" one and basically you'd never see it because the opacity might be 0.07 and then go to 0. Same for section 3. And then by the time you got to section 4, it'd fire off its "in" animation with that 0.5-second delay, so the user would end up seeing section 1 fade out and section 4 fade in. 

 

I hope that clears things up. 

 

Also note that gsap.utils.toArray() might be useful in your code. And you can do standalone ScrollTriggers (like in my demo) - you don't always have to embed them in timelines/tweens. 

Link to comment
Share on other sites

@GreenSock Thanks for the detailed explanation. Definitely makes sense.

 

I do have an few additional question if you don't mind.

onToggle: (self) => {
          // skip the animation if it's the last one and we're scrolling foward, or if it's the first one and we're scrolling backwards
          if (self.isActive || !((isLast && self.direction === 1) || (isFirst && self.direction === -1))) {
            gsap.to(sItems[i], {
              opacity: self.isActive ? 1 : 0, 
              overwrite: true, 
              delay: self.isActive ? 0.5 : 0
            });
          }
        },

This is a simple crossfade, if it becomes more complex would you recommend wrapping everything into a timeline and then trigger it here? In my real project the animation targets more than just the parent element. Would there be a benefit to creating a animation or would you just essentially create it here? 

 

And finally, I updated the original example to have an animation to more closely resemble the animation I intend to use (using SplitText).  In the snippet below you are setting the opacity to two different states, what if I need three? Ideally the split header would enter from bottom, rest at mid, then exit top (see example).

gsap.to(sItems[i], {
    opacity: self.isActive ? 1 : 0, 
    overwrite: true, 
    delay: self.isActive ? 0.5 : 0
});

I tried to make a simple animation here but this only shows the out portion?

if (self.isActive || !((isLast && self.direction === 1) || (isFirst && self.direction === -1))) {
	gsap.to(splitHeaders[i].lines, {opacity: self.isActive ? 1 : 0, y: self.isActive ? '0%' : '-100%'});
}

//Trying to convert this
st.to(outHeader.lines, {
	duration: 1,
	y: "-100%",
	skewX: "8deg",
	skewY: "0deg",
	opacity: 0,
	stagger: 0.1,
	ease: "power4.inOut"
});


Link to comment
Share on other sites

  • Solution

This is definitely beyond the scope of help we can typically provide in these forums because it's all just custom logic stuff, but I got sucked into the challenge and wanted to get you going in a good direction, so here's one way you could approach it: 

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

 

Hopefully that illustrates the concept adequately so you can tweak it to get what you want. If you need more help, we do offer paid consulting services for custom logic stuff like this. 

 

Good luck!

  • Like 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.
×
×
  • Create New...