Jump to content
GreenSock

Search In
  • More options...
Find results that contain...
Find results in...
ncla

When using once + scrub combination, animation stops too early if scrolling past trigger area quickly

Recommended Posts

This seems to be a regression when going from 3.7.0 to 3.8.0 version. To reproduce, just scroll past the second spacer swiftly. You should see that the blur animation is stuck in time and the debug markers are removed.

See the Pen eYGoRmw by ncla (@ncla) on CodePen

Link to comment
Share on other sites

1 hour ago, ncla said:

debug markers are removed.

You set once: true so the ScrollTrigger will kill() itself as soon as the end position is reached once

 

Why did 

1 hour ago, ncla said:

blur animation is stuck

I don't know.

 

I see that after kill - blur for #spacer2 is not equal 0 its sometimes 0.01px, sometimes 2.0331px... its like random, I don't know hot to fix it

  • Like 1
Link to comment
Share on other sites

the team will have to comment on whether it's a bug or not, but in case it helps anyone, having scrub:true (not a number) seems to make it work properly. adding fastScrollEnd:true did not seem to have any impact. 

 

when scrub is a number it means the animation has to catch up to the scroll position with a slight ease. I guess in some ways it makes sense that since the animation is killed instantly when you leave (before it is finished catching up) you are seeing it in this "stuck" state.

Link to comment
Share on other sites

As @_Greg _ pointed out, using once will kill it, but maybe the threads linked in this post can help you out.

 

 

 

 

Link to comment
Share on other sites

Yep, that's because once: true will kill it as soon as you reach the end scroll position, and you've got a delayed scrub value which means the animation is likely in the process of being scrubbed at that point and is killed. I've added code to the next release to allow the scrub to finish in that case - you can see that in this fork: 

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

 

But in the meantime, you can simply remove the once: true and do this instead:

onLeave: self => self.getTween().eventCallback("onComplete", () => self.kill(false)),

Better?

  • Like 3
Link to comment
Share on other sites

Thanks everyone for the replies and ideas.

 

Probably should have been clearer in my initial post (sorry about that) because I just wanted to bring a potential bug to attention quickly, but yes, indeed, I want animation to happen only once, with scrubbing that has custom delay, and only in forward direction.

 

17 hours ago, GreenSock said:

But in the meantime, you can simply remove the once: true and do this instead:

onLeave: self => self.getTween().eventCallback("onComplete", () => self.kill(false)),

Better?

 

This does work but only if if you are going forward direction. If you position your scroll after the trigger, refresh, and scroll upwards, it will be animating backwards. This is not an issue with the original Codepen approach because upon page reload it "skips" the animation because scroll position is past trigger area.

 

As a workaround I ended up with something ridiculous like this. onComplete callback on Tweens does not provide scroll progress/direction, which made things quite wrangled for inexperienced me. The rather forceful delayed self.kill with setTimeout is there for when onLeave gets triggered on page load.

 

        const CharacterTween = gsap.from("#character", {
            filter: "blur(20px) grayscale(30%)",
            opacity: 0,
            autoRound: false,
            lazy: false
        });

        const CharacterScrollTrigger = ScrollTrigger.create({
            animation: CharacterTween,
            trigger: "#character",
            start: "top 70%",
            end: "bottom 80%",
            scrub: 0.45,
            onLeave: self => {
                if (self.progress === 1 && self.direction === 1) {                    
                    self.getTween().eventCallback("onComplete", event => {
                        self.kill(false)
                    })

                    try {
                        setTimeout(() => {
                            self.kill(false)
                        }, 500)
                    } catch (e) {
                        console.error(e)
                    }
                }
            },
        });

        CharacterTween.eventCallback("onComplete", function() {
            CharacterTween.kill(false)
            CharacterScrollTrigger.kill(false)
        });

Let me know if you know a better way than this one. Thanks!

Link to comment
Share on other sites

If you don't want it play backwards, you can do something like this. Basically animate the progress yourself.

 

let tl = gsa.timeline()...

ScrollTrigger.create({
    trigger: ".foo",
    onUpdate: ({ progress }) => {
      if (progress > tl.progress()) {
        gsap.to(tl, { 
          ease: "power3",   
          overwrite: true,
          progress 
        });
      }     
    }
  });

 

 

 

Link to comment
Share on other sites

4 hours ago, ncla said:

This is not an issue with the original Codepen approach because upon page reload it "skips" the animation because scroll position is past trigger area.

If Blake's suggestion doesn't deliver what you want, can you please provide a minimal demo that shows the negative behavior you described (you said the current demo works fine). That'd really help us craft a solution for your particular use case/context. 

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.
×