Share Posted April 3, 2021 I am using Gatsby JS and initializing smooth-scrollbar within a layout component and I've also configured the scroller proxy. Within a child component, I'm trying to setup an animation using scroll trigger but I'm not sure how to configure this. Also, the config does work after HMR fires but not after page refresh. I removed most of the markup from the page as I know the animation works - I think the issue is with syncing the scrollers. layout.js: import React, { useRef, useEffect } from "react" import Navbar from "./navbar" import { gsap } from "gsap"; import { ScrollTrigger } from "gsap/ScrollTrigger"; import Scrollbar from 'smooth-scrollbar' // import "../styles/locomotive-scroll.css" const Layout = ({ children, location }) => { const scrollerRef = useRef() useEffect(() => { gsap.registerPlugin(ScrollTrigger) const scroller = scrollerRef.current const bodyScrollBar = Scrollbar.init(scrollerRef.current, { damping: 0.1, delegateTo: document, alwaysShowTracks: true }) window.bodyScrollBar = bodyScrollBar ScrollTrigger.scrollerProxy(scrollerRef.current, { scrollTop(value) { if (arguments.length) { bodyScrollBar.scrollTop = value } return bodyScrollBar.scrollTop } }) window.scroller = scrollerRef.current bodyScrollBar.addListener(ScrollTrigger.update) ScrollTrigger.defaults({ scroller: scroller }) // ScrollTrigger.refresh() return () => { if (bodyScrollBar) bodyScrollBar.destroy() bodyScrollBar.removeListener(ScrollTrigger.update) } }, [location]) return ( <div className="global-wrapper scroller h-screen overflow-hidden" ref={scrollerRef}> <header className="global-header animate__animated animate__fadeIn"> <Navbar /> </header> <main>{children}</main> <footer> {/* © {new Date().getFullYear()}, Built with */} {` `} {/* <a href="https://www.gatsbyjs.com">Gatsby</a> */} </footer> </div> ) } export default Layout index.js: import React, { useEffect, useRef } from "react" import { gsap } from "gsap"; import { ScrollTrigger } from "gsap/ScrollTrigger"; function Index() { const outroRef = useRef() const outroContentRef = useRef() useEffect(() => { gsap.registerPlugin(ScrollTrigger) gsap.fromTo(outroContentRef.current, { yPercent: "-50" }, { yPercent: "0", scrollTrigger: { scroller: window.scroller, trigger: outroRef.current, end: "bottom bottom", scrub: true, markers: true }, ease: "none" }) }, []); return ( <> {/* Page content */} </> ) } export default Index Link to comment Share on other sites More sharing options...
Share Posted April 4, 2021 Hi, Have you tried this without the scroller proxy and just ScrollTrigger? Can you provide a minimal live sample in Codesandbox, so we can take a better look at this? Finally I'm not all that familiar with Gatsby (honestly if forced to work with a React site generator I prefer NextJS) and I could be wrong, but isn't the layout component the one that wraps virtually every page/route? Isn't a way to pass the references as props instead of attaching them to the window object? Considering that you can create SSR stuff with Gatsby that could throw an error during the build process since there is no window object in that particular phase. Happy Tweening!!! 2 Link to comment Share on other sites More sharing options...
Solution Author Solution Share Posted April 5, 2021 After some fiddling, the solution I came up with was to conditionally render the children. The scroll requires a ref to the scroll container so the init function is placed inside useEffect to guarantee the ref is defined. And because this component is at the "top level" we don't need to pass location to the useEffect callback. This seems to work but any improvements or suggestions are welcome 🤷♂️ import React, { useRef, useEffect, useState } from "react" import Navbar from "./navbar" import Scroll from './scroll' import '../styles/smooth-scrollbar.css' const Layout = ({ children }) => { let scrollerRef = useRef() let [scrollDefined, setScrollDefined] = useState(false) const initScroll = () => { Scroll(scrollerRef) setScrollDefined(true) } useEffect(() => { initScroll() }, []) return ( <div className="global-wrapper scroller h-screen overflow-hidden" ref={scrollerRef}> <header className="global-header animate__animated animate__fadeIn"> <Navbar /> </header> <main>{ scrollDefined && children }</main> <footer> {/* © {new Date().getFullYear()}, Built with */} {` `} {/* <a href="https://www.gatsbyjs.com">Gatsby</a> */} </footer> </div> ) } export default Layout import { gsap } from "gsap"; import { ScrollTrigger } from "gsap/ScrollTrigger"; import Scrollbar from 'smooth-scrollbar' import SoftScrollPlugin from './plugins/SoftScrollPlugin' const Scroll = (scrollerRef) => { const scroller = scrollerRef.current Scrollbar.use(SoftScrollPlugin) const bodyScrollBar = Scrollbar.init(scroller, { damping: 0.1, delegateTo: document, alwaysShowTracks: true }) window.bodyScrollBar = bodyScrollBar gsap.registerPlugin(ScrollTrigger) ScrollTrigger.defaults({ scroller: scroller }) ScrollTrigger.scrollerProxy(scroller, { scrollTop(value) { if (arguments.length) { bodyScrollBar.scrollTop = value } return bodyScrollBar.scrollTop } }) ScrollTrigger.refresh() bodyScrollBar.addListener(ScrollTrigger.update) } export default Scroll 1 Link to comment Share on other sites More sharing options...
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now