Jump to content
Search Community

animate tween independent of scroll trigger

skipper42 test
Moderator Tag

Recommended Posts

What's the best way to trigger a tween in a scroll trigger, but have it animate independently of the scroll? I have a function call when the tween starts, but this doesn't seem to be working. What's the best way to do this? Thanks.

 

function fadeInGrow(el) {
    var tl = gsap.timeline();
    tl.to(el, {duration: 1, opacity: 1, transform: 1} );
}

st.to(".imac-animation-wrapper .imac-text1", {duration: 10, onStart: function(){ fadeInGrow(this.targets()[0]) }})

 

What I'm trying to do essentially is disable the scrub briefly like this:

.to(".el1", {duration: 10, ...} )
.to(".el2", {duration: 30, ...} )
.to(".el3", {duration: 20, ...} )
.to(".el4", {duration: 20, ...} ) // <-- I want this one to animate without the scroller
.to(".el5", {duration: 40, ...} )

 

Link to comment
Share on other sites

Howdy, @skipper42. Would you please create a minimal demo? It's just super difficult to troubleshoot blind. I don't really understand what you mean by "disable the scrub briefly" - that sounds logically perilous. You want to unhook the animation from the scrollbar and then hook it back up? What if the user scrolled in the meantime? Would it just jump to that new position? 

 

Also, what are you trying to do with transform: 1? That looks incorrect to me. 

 

Side note: you don't need to create a timeline if you're just gonna populate it with one tween. You can simplify it:

// BEFORE
var tl = gsap.timeline();
    tl.to(el, {duration: 1, opacity: 1} );

// AFTER
gsap.to(el, {duration: 1, opacity: 1});

 

Link to comment
Share on other sites

Sure is. I'd recommend giving the docs a read - https://greensock.com/docs/v3/Plugins/ScrollTrigger

Very oversimplified but... 
 

// play on page load
gsap.to(".box", {
  x: 500
});

// play when the box enters the viewport
gsap.to(".box", {
  scrollTrigger: ".box",
  x: 500
});

// play when the box enters the viewport and link the progress to scroll duration
gsap.to(".box", {
  scrollTrigger: {
   trigger: ".box",
   scrub: true,
  }
  x: 500
});

 

  • Like 3
Link to comment
Share on other sites

Or potentially using callbacks?
 

let tl = gsap.timeline({
    scrollTrigger: {
      trigger: '.trigger',
      scrub: true,
    },
  });

  tl.to('.scrubbed-thing', { opacity: 0 })
    .to(
      '.scrubbed-thing',
      {
        opacity: 0,
        yPercent: 150,
        onStart: () => {
          gsap.to('.unscrubbed-thing', {
            opacity: 0,
          });
        },
        onReverseComplete: () => {
          gsap.to('.unscrubbed-thing', {
            opacity: 1,
          });
        },
      },
    )
   .to('.another-scrubbed-thing', { opacity: 1 })


There's a lot of different ways to approach it depending on when you want them to play and what connection they have with other animations - If this doesn't help it would be great if you could add a minimal demo so we can understand your particular use case.

  • Like 5
Link to comment
Share on other sites

@skipper42 I looked at the iPhone Page and curiously it didn't do anything fancy for me on windows chrome, only when i looked on Mac Safari the magic happend. (Don't know it it's just me, or if the really do it way..)

 

I assume you mean the lidar-section that 'pauses' the scrolling until the animation/video is finished.

 

I'd thing that one simple way to do it that way is by snapping scrollTrigger to that section. And then blocking mouse/scroll interaction for the full-screen element e.g. with pointer-events:none and at the end of the animation remove that 'block'. (Pointer-events block most scrolling, body is an exception in parts....)

 

You could do this by using two different scroll triggers and take the end of the first as start of the animation, it should also work with one scrollTrigger, as by letting no scrolling through to the scroller while the video plays it should not react.

 

So disabling scrolling onSnap should be one way to do it. 

 

On the iPhone site however scrolling isn't fully disabled while the video plays. There is only a quite high scroll resistance as far as I can tell.

 

That should be also doable one or the other way, but I can't think of an easy out of the box way right now. Would depend on the exact use case I'd guess.

 

That leads me to two questions/feature requests for @GreenSock:

 

Would it be possible to ad a resistance to snapping? So that only once the user scrolls a certain amount in percent of the animation or in pixels the snapped element / the whole scroller starts moving? (Probably catching up to the natural scroll position...)

 

I love snapping to labels, but maybe I just looked or tried hard enough, but I would love to have an easy way to get the name of the label that has been snapped to onSnapComplete  

Link to comment
Share on other sites

The iPhone page doesn't exactly do what I'm looking for. The part I'm having trouble with is that the things I'm animating are close to each other and the 'start/end' values get messed up because the pin creates huge padding underneath. It's kinda like this:

 

<div class="box-wrapper">
  <div class="box1">
  <div class="box2">
  <div class="box3">

 

I want the whole wrapper to be pinned until box3 finishes. But, I want box2 and box3 NOT controlled by the scroller, ie. when we scroll so far or passed box1 just fire box2 animation, scroll a little more fire box3, like that, and all of this pinned on screen until we're finished.

 

From what I've been able to do I either have one pin and the whole wrapper is scrollable, OR separate pins for each box. I'm attempting to do the latter however I'm coming across problems with when to start box2 and box3 (is it # pixels down the page? or after box1 starts to move and quickly pin box2...?, etc). Box1 is quite large and the others are pushed way down the page due to padding. I'm rereading the scrolltrigger docs and experimenting with values.

 

Link to comment
Share on other sites

You're probably looking for pinspacing @skipper42 - if you set it to false it'll remove the padding which is added to push the following sections down.

@iDad5 - I'm not sure what you mean by 'add a resistance to snapping' - that's what snapping is right? You scroll a certain amount and when you stop the whole scroller eases back or forwards to a defined position in the animation/scroll?
 

20 hours ago, iDad5 said:

Would it be possible to ad a resistance to snapping? So that only once the user scrolls a certain amount in percent of the animation or in pixels the snapped element / the whole scroller starts moving? (Probably catching up to the natural scroll position...)

 

I love snapping to labels, but maybe I just looked or tried hard enough, but I would love to have an easy way to get the name of the label that has been snapped to onSnapComplete  


You can add easing and delays and duration to snapping - I've added some params to this codepen - does that help?

You can get the currentLabel() within a callback already too 🥳 

demo -

See the Pen 62601a2e84a0ce6bcbb967aa33ddc01c?editors=1111 by cassie-codes (@cassie-codes) on CodePen

  • Like 1
Link to comment
Share on other sites

@Cassie Thanks for the Example with the current label. That will most likely help a lot the next time I will want to use it in earnest. As I most often work with more or less encapsulated classes getting the reference to the timeline should be no problem. Still I would think that under certain circumstances it might be profitable to get it inside the callback directly like direction etc.

 

Regarding resistance: You are right insofar as the snapping to the position is concerned. I was thinking about the leaving of the snapped in position (like in the iPhone-lidar video) In situations like these. or when you generally want to always snap forward and never back (one of the great feature that is already provided. as far as I understand) If would come in handy in my thinking if I could define a resistance against leaving a snapped in label/position, so that the user wont accidently leave a fixed position too easily. With a lot of modern devices it can happen quite easily to minimally scroll by accident (the apple magic mouse and a lot of touch-pads come to mind.

 

Did I manage to bring my idea across?

Link to comment
Share on other sites

7 hours ago, iDad5 said:

Still I would think that under certain circumstances it might be profitable to get it inside the callback directly like direction etc.

Yep, that's super easy:

onSnapComplete: self => console.log(self.animation.currentLabel())

 

7 hours ago, iDad5 said:

I could define a resistance against leaving a snapped in label/position, so that the user wont accidently leave a fixed position too easily

ScrollTrigger doesn't do scroll-jacking (that was very intentional), so it's not like we can prevent scrolling until a certain amount of dragging/wheeling occurs. In other words, if the user scrolls, ScrollTrigger ain't gonna stop them; it'll merely respond by updating the attached animation accordingly. Now of course we could introduce logic that would prevent the scrubbing of the animation until a certain threshold has been passed, but frankly I don't really see much value in that. In fact, it seems like it could be problematic because it'd sorta break the link but only sometimes and then it'd have to catch up (synchronize) beyond the threshold and although we could use a tween for that, what would you do if there was already a numeric scrub applied? Now there'd be multiple layers of interpolation and it's just messy/clunky. But maybe I misunderstood your request. 

  • Like 1
Link to comment
Share on other sites

@GreenSock You did understand my request perfectly, and I wasn't really expecting it to be easy.

On 7/17/2021 at 10:26 PM, GreenSock said:

Now of course we could introduce logic that would prevent the scrubbing of the animation until a certain threshold has been passed, but frankly I don't really see much value in that.

I don't know if you have taken a closer look to the op's original example of the iPhone site and the lidar video. In my mind in that case it adds a lot of value. We all try to use scrollTrigger to do engaging work - especially with the great scrubbing feature we have a great way of handing over contol to the user. But the attention span is what it is and to have the option to subtly take back control and tell a story within the story fells like an interesting option. But could be just me...

On 7/17/2021 at 10:26 PM, GreenSock said:

In fact, it seems like it could be problematic because it'd sorta break the link but only sometimes and then it'd have to catch up (synchronize) beyond the threshold and although we could use a tween for that, what would you do if there was already a numeric scrub applied?

The fact that you said 'could be' leaves a little hoe for me. I'm sure your concerns are valid, and I really don't know about the numeric scrub.

 

I also don't know if I'll have a good use case sometime soon if so I'll probably  try to solve it by using a temporary overlay that 'scroll-jacks' by taking the focus from the scroller. Thank you for your time and insights. 

Link to comment
Share on other sites

5 hours ago, iDad5 said:

I don't know if you have taken a closer look to the op's original example of the iPhone site and the lidar video. In my mind in that case it adds a lot of value.

I must be missing something because I looked at that site in Safari and it doesn't pause scrolling for me at all. Ever. And yes, I focused on the LiDAR section. I also don't notice anything there that can't already be done with ScrollTrigger. 

  • Like 2
Link to comment
Share on other sites

BTW, I figured out my original question. When working with several timelines in the viewport, I found it best to only pin ONE thing at a time, a container that all the animations were inside of. That way I didn't have to mess with the padding/spacing. Then I used .addPause() which essentially held the pin in place while I could animate other things. Kinda like this:

 

function rotateImac() {
    var tl = gsap.timeline({
        scrollTrigger: {
            trigger: ".imac-animation-wrapper",
            start: "top 20%",
            end: "+=9500",				// Long total scroll time
            scrub: true,
            pin: true,
        }
    });

    tl.to(".imac-animation-wrapper .img-wrapper img", { duration: 30, rotateY: 30} )
      // Short animation here (duration 30)

      .addPause(70) 		
    // Long pause. Keeps the section pinned while I can animate other stuff. =)

    return tl;
}

 

The only other hard part was adjusting start/end times for all the pieces so they ran within the duration (scroll distance) of the timeline.

 

Yeah. Good work gsap team. I'm sold.

  • Like 4
Link to comment
Share on other sites

Actually it is a strange thing, it works differently in Safari and on Windows etc. it doesn't even work consistently after reloading in Safari on Mac sometimes.

@skipper42 as well as my self experienced something akin to scroll pausing / blocking and it still 'feels like' most of the times I looked. It might be, that ist actually a fixed video container that is released after scrolling the (hidden) background for a while or it might be time triggered...

 

Also I never thought that it wouldn't be doable (probably better) with. Old persistent guy I am I still would love to have a simple resistance parameter for snapping in scrollTrigger - despite the fact that it might be tricky if someone can do tricky with grace and ease it is @GreenSock :-)

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