Jump to content
Search Community

ScrollTrigger used for dynamic pages in react, only firing on first visit.

Vivodionisio test
Moderator Tag

Recommended Posts

Hi,

I'm using react-router-dom to render out ArtPiece pages dynamically. In the ArtPiece component I have a ScrollTrigger animation (along side other tweens) inside a useLayoutEffect() with react-router-dom's location object passed as a dependancy:
 

useLayoutEffect(() => {
    const ctx = gsap.context(() => {

      // Some other tweens that don't use ScrollTigger (these work fine)..
      
      // Said tweens (with ScrollTrigger)
      // Detail Image Overlay
      gsap.fromTo(
        q('.detail-image-inner h4'),
        { opacity: 0 },
        {
          opacity: 1,
          scrollTrigger: {
            trigger: q('.detail'),
            start: 'top 60%',
            toggleActions: 'play none none none'
          }
        }
      )
      
      gsap.fromTo(
        q('.detail-image .overlay'),
        {
          scaleY: 1
        },
        {
          scaleY: 0,
          duration: 1.5,
          ease: CustomEase.create(
            'custom',
            'M0,0,C0.05,0,0.149,0.279,0.19,0.374,0.36,0.772,0.528,0.988,1,1'
          ),
          scrollTrigger: {
            id: 'detailReveal',
            trigger: q('.detail .overlay'),
            start: '10% 80%'
            // toggleActions: 'restart none none revert'
          }
        }
      )
    })

    return () => {
      ctx.revert()
    }
  }, [location])

I also have ScrollTrigger at work on this page in a separate useEffect() should it be relevant:
 

useEffect(() => {
    smoother.current = ScrollSmoother.create({
      wrapper: '#smooth-wrapper-artpiece',
      content: '#smooth-content-artpiece',
      smooth: 1
      // smoothTouch: 0.1
      // normalizeScroll: true
    })

    return () => {
      smoother.current.revert()
    }
  }, [])

The location object in the dependancy array is doing its job of triggering the effect every time an ArtPiece page mounts. But for some reason the ScrollTrigger is only doing it's thing the first time an ArtPiece page is navigated to (after an initial refresh). On subsequent navigation to the same or other ArtPiece pages, the ScrollTrigger has no effect. Does anyone know if there is an obvious reason for this? Or could this be a limitation where it comes to using gsap with React? 

Thanks in advance for any help offered :)

Link to comment
Share on other sites

It's pretty difficult to troubleshoot without a minimal demo (you're only providing a few excerpts from the code, but there could be other factors that are causing the problems), but my guess is that it's something with your scoped selector. I see that you're doing an excellent job of using gsap.context() in that first piece of code, but there's no reason for you to be using a separate scoped selector (whatever your "q()" variable refers to). gsap.context() allows you to pass in the scope object as the 2nd parameter and then it automatically affects all your selector text inside the context. 

 

In other words...

// BEFORE
let q = gsap.utils.selector(someRef);

let ctx = gsap.context(() => {
	gsap.to(q('.detail-image .overlay'), {...})
    ...
});
  
// AFTER
let ctx = gsap.context(() => {
	gsap.to('.detail-image .overlay', {...})
    ...
}, someRef);

You didn't show how/where you're creating your scoped selector, so my best guess is that the element you scoped it to got re-rendered, thus subsequent calls to q(...) were looking in the wrong element. Again, it's impossible for us to know without seeing the problem in context inside a minimal demo, but I'd recommend ditching your scoped selector altogether and just leveraging the features of gsap.context(). 

 

Also, you should be killing your smoother in your cleanup function: 

// before
return () => {
  smoother.current.revert()
}

// after
return () => {
  smoother.current.kill()
}

If those tips don't help, please provide a minimal demo, like in Stackblitz or CodeSandbox. Here are links to the React/Next/Nuxt/Gatsby starter templates. 

Link to comment
Share on other sites

Hi Jack,

So, I've made the changes, thanks for that direction. And I'm working on a minimal demo.


I realise I failed to mention the issue has only occurred in production (deployed to Netlify). It's been working fine in development.  After some more investigation I have a clearer picture: the ScrollTrigger was actually running too soon. This was possible to see by quickly scrolling down a given artPiece page after loading. Navigating to an artPiece initially doesn't cause this since it navigates from the collectionPage which is only 100vh, so that the scroll position is above the trigger element on the next page. Subsequent navigation to artPiece pages from the picker at the bottom of the artPiece page means that the scroll position will be past the start of the trigger. That's where the issue lies. I missed this because all pages are wrapped by a component which calls window.scrollTo(0, 0) each time the location object changes.

I have no idea why the problem wasn't an issue in the dev environment. I've gotten around it for now by adding a window.scrollTo() to the onComplete callback of the artPiece exit animation, so that the scroll position is above the trigger when the next page loads. This works just fine in both dev and production. 

However a new problem occurs at this point when adding scrollSmoother to the picture. Again, when navigating from collectionPage to artPiece page all is well, but when navigating between artPiece pages, scrollTrigger does't appear to fire at all (the overlay doesn't scale to 0 and reveal the image behind).

I'm not sure whats happening. 

Anyway, here's how I scoped the variables:

// Artpiece component
const ArtPiece = () => {
  const { slug, id } = useParams()

  const artpiece = useRef()
  const q = gsap.utils.selector(artpiece)

  const smoother = useRef(null)


And here's my exit animation: 
 

function handleExit(path) {
    gsap.set('body', { overflowY: 'hidden' })
    gsap.to([q('.detail'), q('.main-image')], {
      opacity: 0,
      duration: 1,
      delay: 0.8,
      onComplete: () => {
        // smoother.current.paused(true)
        smoother.current.scrollTop(0)
        // window.scrollTo(0, 0)
        navigate(path)
      }
    })
  }

As stated, if I comment out scrollSmoother and use window.scrollTo instead of gsap.scrollTop or  gsap.scrollTo, every thing works beautifully. 

Hopefully I can replicate this issue in a minimal demo.

Thanks,

Saul


 

Link to comment
Share on other sites

Hi @GreenSock, I've created a minimal demo and stumbled on a different problem when the ScollSmoother is added: which is that the page no longer scrolls. Currently I have ScrollSmoother commented out and it works as expected: When going between artpiece pages the exit animation runs, the onComplete callback fires navigate() and the next page mounts with enter animations that include the set up of scrollTrigger for the second img. That then fires when you scroll to the appropriate position. But if you uncomment the effect with the smoother in, you should see the issue arise.   
https://codesandbox.io/s/hungry-water-i2gf6w?file=/src/ArtPiece.js
 

Many thanks!

 

Link to comment
Share on other sites

Hi,

 

The issue here could be the fact that you are setting the ScrollSmoother instance at a component level. I would recommend you to instantiate ScrollSmoother at the top level element of your app, be done with it and not worry about it in any other page/view/route. Something like this:

https://stackblitz.com/edit/react-ebvhnb?file=src%2Findex.js,src%2FApp.js,src%2Frouter%2Findex.js,src%2Fviews%2FHome.js,src%2Fviews%2FAbout.js

 

Hopefully that's enough to get you back on track. Let us know if you have more questions.

 

Happy Tweening!

Link to comment
Share on other sites

  • 1 month later...

Hi, sorry for the delay. My project proved a bit overwhelming whilst everything in life happened at once. Thanks for your suggestion. This put me on the right track. In the end I followed this example of moving the instantiation of ScrollSmoother to its own component:
 

 

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