Jump to content
Search Community

Element with Scroll Trigger inside sticky element doesn't apply end state when chrome is refreshed after scrolling.

Bonsai test
Moderator Tag

Go to solution Solved by Bonsai,

Recommended Posts

I've attached a video showing the issue I'm running into. It works great when scrolling from the top. The problem occurs when I refresh the page after scrolling past the end of the animation. Chrome brings me to the same scroll position, which is often helpful, but in this case throws off the scroll trigger because it's container starts out with position fixed and it isn't able to determine that it's already past the end of it's scroll endpoint. Once I scroll up to it's endpoint, it pops into position and the animation starts working.

 

I tried setting progress to 1 on its timeline, but it reverts that back to 0. I've tried setting the scroll position to 0 and then setting it back to its initial position. This works, but it requires a requestAnimationFrame between the two, which leads to a screen flicker. Is there any way to force scroll trigger to it's endstate without it being visible to the user?

 

Edit: Not sure the attached video uploaded correctly. It can also be seen here.

Edited by Bonsai
Not sure video attachment worked.
Link to comment
Share on other sites

It'd be super duper amazingly helpful if we could see a minimal demo, like in CodePen.io or something. The absolutely minimal code necessary to reproduce (you don't need to use your production code, assets, etc.) 

 

https://greensock.com/demo

 

I wasn't quite sure what you meant by forcing a ScrollTrigger to its end state, but if you mean its animation then yes, you should be able to simply set its progress() to 1, like animation.progress(1). Does that help at all? 

 

Also, please make sure you're using the latest version of ScrollTrigger. 

  • Like 1
Link to comment
Share on other sites

Thought about putting a demo together, but was pressed for time. As for what I meant by "end state," I just meant the end of the connected timeline. I already tried setting progress to 1 manually on the timeline, but scroll trigger immediately reset it to 0. Need to try it with a delay, but not sure that will help. I'm using version 3.5.0 of Scroll Trigger, so it's slightly out of date. Will give that a try.

 

For reference you can see a snippet of my current code below

//this is where I make changes to get scroll trigger to position correctly
if (ctrl._startsSticky) {
  ctrl._initScroll = ctrl._win.scrollTop();
  ctrl._win.scrollTop(0);
}
var headerHeight  = angular.element('.header')[0].offsetHeight;
var startY = ($window.innerHeight / 2) - headerHeight;

//animation defined here
var tl = gsap.timeline({
  scrollTrigger: {
    trigger: ctrl._shareIcons, // ctrl._shareIcons is just a div reference
    toggleActions: "play none reverse reset",
    start: `${startY}px center`,
    end: () => '+=298.8',
    scrub: true,
    snap: false,
    invalidateOnRefresh: true
  },
});
tl
  .to(ctrl._shareIcons, .25, {
  onUpdate: function () {
    var progress = this.progress();
    var rotate = 90 * progress;
    gsap.set(ctrl._shareDiv, {
      rotation: -rotate,
      x: -166 * Math.cos((rotate - 90) * Math.PI / 180)
    })
    gsap.set(ctrl._icons, {
      rotation: rotate
    });
  }
})
  .to(ctrl._shareDiv, .2, { borderBottomRightRadius: '0rem', borderTopRightRadius: '0rem' });

//this is where I reset the changes I made at the beginning of the code
if (ctrl._startsSticky) {
  $timeout(function () {
    ctrl._win.scrollTop(ctrl._initScroll);
  });
}

This code does fix the animation, but causes screen flicker because it scrolls back to the top, and then waits for the next cycle to scroll back down to the original position. I'll be busy with other work this week, so I likely won't post an update until the end of this week or early next week. Let me know if you have any other ideas on how to force a timeline to the end in the meantime.

Link to comment
Share on other sites

Very tough to troubleshoot blind, but I'll mention a few quick things: 

  1. You can EITHER have toggleActions OR scrub, not both. It isn't logically possible to have both. So if you have a scrub, it'll ignore toggleActions (so you can remove that from your code). 
  2. Did you try removing invalidateOnRefresh: true? I

Once we see a reduced test case, I'm sure we'll be able to offer more help. 

  • Like 1
Link to comment
Share on other sites

Noted on toggleActions. I'll try removing invalidateOnRefresh. If neither of these tweaks help I'll work on putting together a reduced test case.

 

It will take me a while to post though, as I'm busy with another project at the moment. I'll post an update in a week or two.

Link to comment
Share on other sites

  • 3 weeks later...
  • Solution

I figured out my problem. The issue was that the onUpdate callback does not fire if the scroll animation hasn't started or has already ended. To fix this, I needed to add the onStart and onEnd events to update the styles as well. The fixed version can be seen below.

ctrl._animateIconContainer = function () {
    var headerHeight  = angular.element('.header')[0].offsetHeight;
    var startY = ($window.innerHeight / 2) - headerHeight;
    var tl = gsap.timeline({
        scrollTrigger: {
            trigger: ctrl._shareIcons,
            toggleActions: "play none reverse reset",
            start: `${startY}px center`,
            end: () => '+=298.8',
            scrub: true,
            snap: false,
            invalidateOnRefresh: true
        },
    });
    tl
        .to(ctrl._shareIcons, .25, {
            onStart: function () { //added this to handle when the animation hasn't started yet
                ctrl._setRotation(0);
            },
            onUpdate: function () {
                ctrl._setRotation(this.progress()); //moved the code that was here into its own function
            },
            onEnd: function () { //added this to handle when the animation has already finished
                ctrl._setRotation(1);
            }
        })
        .to(ctrl._shareDiv, .2, { borderBottomRightRadius: '0rem', borderTopRightRadius: '0rem' });
}

//moved animation to separate function to allow it to be used in the 3 event callbacks
ctrl._setRotation = function (progress) {
    var rotate = 90 * progress;
    gsap.set(ctrl._shareDiv, {
        rotation: -rotate,
        x: -166 * Math.cos((rotate - 90) * Math.PI / 180)
    })
    gsap.set(ctrl._icons, {
        rotation: rotate
    });
}

 

Link to comment
Share on other sites

56 minutes ago, Bonsai said:

To fix this, I needed to add the onStart and onEnd events to update the styles as well.

I assume you meant onComplete, not onEnd, right? (There's no such thing as onEnd). Your tween is literally trying to animate an "onEnd" property of ctrl._shareIcons :)

 

And onUpdate gets called too when those occur, so I'm not quite sure why you thought it was necessary to add those. But I'm glad to hear you got things working the way you intended. 

  • Like 1
Link to comment
Share on other sites

  • 4 weeks later...

Missed your comment. I did have only onUpdate originally, but still encountered this problem (see code earlier in this thread here). OnUpdate was triggering, but its progress was set to 0 instead of 1 even though the browser was already scrolled past the end of the scroll trigger. Only things I changed to fix it was add the onStart and onEnd callbacks and moved the logic previously in onUpdate into its own function. Will need to double check whether "onEnd" is firing, since you are right the documentation just has "onComplete." Pretty sure that callback was firing when I tested this.

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