Jump to content
GreenSock

dodoynet

scrolltrigger lose scrolltop on history back

Recommended Posts

Hi everybody!

I've started working with Gsap and Scrolltrigger and I cannot manage to keep the scroll on previous page when I press the back button.

even the (empty) code

let tlf = gsap.timeline({scrollTrigger: {}});

makes the page jump to top when returning to it by pressing back button..

I noticed that even at  

https://greensock.com/scrolltrigger/

if you scroll to bottom, press for example "download" and then the back button the page is jumping to top

I'm getting crazy but I cannot think this is a scrolltrigger bug!

where do I mistake??

thanks a lot for any help in solving this drama

d

 

 

 

 

 

 

Link to comment
Share on other sites

Hi @dodoynet and welcome to the GreenSock forums!

 

Can you please provide a minimal demo that shows in a simple way the issue you're having? Also are you using a specific framework (React/Vue/Svelte etc.) or is a vanilla js project?

 

Finally take a look at this thread and use the beta version of 3.11.2 mentioned there to see if your issue is fixed:

Happy Tweening!!!

Link to comment
Share on other sites

Hi, Rodrigo!

first of all thank you for your quick answer!

here is a super simple demo: 

See the Pen yLjPzRB by dodolone (@dodolone) on CodePen

you will note that clicking a link and then going back to the page the scolltop goes lost

d

Link to comment
Share on other sites

no way..

I've tested 

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

 with chrome (FAIL), Opera (FAIL), Firefox (FAIL) and Safari (SUCCESS)

Link to comment
Share on other sites

I don't think it's possible to 100% accommodate that because in order to function properly, ScrollTrigger needs to set the scroll position to 0 when doing all of its measurements (during refresh()) and then set it back to what it was before the refresh() started but if the browser attempts to restore the scroll position WHILE in the midst of a refresh, that can't really work well. See what I mean? I've put in place some code that will attempt it as best it can, and it seems to work in most situations at least in my tests, but due to the nature of scroll-related calculations I just don't see right now a clean way to accommodate it perfectly in every situation. The browser doesn't fire off an event that's specific to that scroll restoration event, so we can't distinguish it from other scroll events. If you know of a way to do it, please let me know. 

Link to comment
Share on other sites

oh I see

rarely I've got the chance to work with a powerful (let's say the true: it's so cool!!) and versatile library like scrolltrigger

it is a great pity not to be able to manage to back to previous page (and its scrolltop)

I wish you could find the solution soon

thanks a lot for your work!

d

Link to comment
Share on other sites

I've modified the native smoothscroll function playing with pushstate, popstate and localstorage

the result is that the scroll position is maintained on every browser I tested

d

 

function smoothScroll(content, viewport, smoothness) {
    content = gsap.utils.toArray(content)[0];
    smoothness = smoothness || 1.4;

    gsap.set(viewport || content.parentNode, {overflow: "hidden", position: "fixed", height: "100%", width: "100%", top: 0, left: 0, right: 0, bottom: 0});
    gsap.set(content, {overflow: "visible", width: "100%"});

    function pushHistory(e){
        e = e || window.event;
        var target;
        target = e.target || e.srcElement;
        history.pushState({scrollTop:document.body.scrollTop},document.title,document.location.pathname);
    }

    if (document.body.addEventListener)
    {
        document.body.addEventListener('click',pushHistory,false);
    }
    else
    {
        document.body.attachEvent('onclick',pushHistory);
    }
    
    window.addEventListener('popstate', (event) => {
        var scrolltop = localStorage.getItem("scrolltop");
        if (scrolltop != undefined && scrolltop > 0){
            gsap.to(window, {duration: 0, scrollTo: {y: scrolltop, autoKill: true}});
        }
    });
    window.onpopstate = (event) => {
        var scrolltop = localStorage.getItem("scrolltop");
        if (scrolltop != undefined && scrolltop > 0){
            gsap.to(window, {duration: 0, scrollTo: {y: scrolltop, autoKill: true}});
        }
    };    

        
    let getProp = gsap.getProperty(content),
        setProp = gsap.quickSetter(content, "y", "px"),
        setScroll = ScrollTrigger.getScrollFunc(window),
        removeScroll = () => content.style.overflow = "visible",
        killScrub = trigger => {
            let scrub = trigger.getTween ? trigger.getTween() : gsap.getTweensOf(trigger.animation)[0]; // getTween() was added in 3.6.2
            scrub && scrub.pause();
            trigger.animation.progress(trigger.progress);
        },
        height, isProxyScrolling;
    
    function refreshHeight() {
        height = content.clientHeight;
        content.style.overflow = "visible"
        document.body.style.height = height + "px";
        return height - document.documentElement.clientHeight;
    }    
    
    ScrollTrigger.addEventListener("refresh", () => {
        removeScroll();
        requestAnimationFrame(removeScroll);
    })
    ScrollTrigger.defaults({scroller: content});
    ScrollTrigger.prototype.update = p => p; // works around an issue in ScrollTrigger 3.6.1 and earlier (fixed in 3.6.2, so this line could be deleted if you're using 3.6.2 or later)
        
    ScrollTrigger.scrollerProxy(content, {
        scrollTop(value) {
            if (arguments.length) {
                isProxyScrolling = true; // otherwise, if snapping was applied (or anything that attempted to SET the scroll proxy's scroll position), we'd set the scroll here which would then (on the next tick) update the content tween/ScrollTrigger which would try to smoothly animate to that new value, thus the scrub tween would impede the progress. So we use this flag to respond accordingly in the ScrollTrigger's onUpdate and effectively force the scrub to its end immediately.
                setProp(-value);
                setScroll(value);
                return;
            }
            return -getProp("y");
        },
        scrollHeight: () => document.body.scrollHeight,
        getBoundingClientRect() {
            return {top: 0, left: 0, width: window.innerWidth, height: window.innerHeight};
        }
    });

    return ScrollTrigger.create({
        animation: gsap.fromTo(content, {y:0}, {
            y: () => document.documentElement.clientHeight - height,
            ease: "none",
            onUpdate: ScrollTrigger.update
        }),
        scroller: window,
        //invalidateOnRefresh: true,
        start: 0,
        end: refreshHeight,
        refreshPriority: -999,
        scrub: smoothness,
        onUpdate: self => {
            localStorage.setItem("scrolltop", self.scroll());  //save the value  
            if (isProxyScrolling) {
                killScrub(self);
                isProxyScrolling = false;
            }
        },
        onRefresh: killScrub // when the screen resizes, we just want the animation to immediately go to the appropriate spot rather than animating there, so basically kill the scrub.
    }); 
        
}
 

Link to comment
Share on other sites

Thanks for sharing your solution! 

 

I wouldn't recommend applying that in the onUpdate() because that fires VERY frequently. Wouldn't it be better to only save that when the user is about to get rerouted to a different page? That seems like it'd be much less CPU-intensive. 

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