granularclouds Posted April 10, 2024 Posted April 10, 2024 Hello! I started working on a project in which I'm using both Lenis (ReactLenis) and Scrolltrigger on a different, slower machine and am noticing very poor framerates on scroll (whereas on the beefier machine, the scroll animations were fluid, fast). Commenting out ReactLenis restores performance. My question is: how to make scrolltrigger and lenis play nice in React/Next. This is not really a "help me achieve outcome X" in this sandbox/codepen question, moreso a higher-level, best practices "what is the right/recommended way to do Y" question For example, in the React Lenis docs, they suggest synchronizing Lenis and GSAP like this: function Component() { const lenisRef = useRef() useEffect(() => { function update(time) { lenisRef.current?.lenis?.raf(time * 1000) } gsap.ticker.add(update) return () => { gsap.ticker.remove(update) } }) return ( <ReactLenis ref={lenisRef} autoRaf={false}> { /* content */ } </ReactLenis> ) } While in their Next.js starter, synchronization appears to be spread across two files: this more scrollTrigger specific one; and this more generic GSAP one (in the combined setup featured these two files, there is no wrapping ReactLenis element, also). So, again. Just wondering if any GSAP people have a recommended/best practice approach for doing this. As I keep seeing different patterns. Totally fine if this question is ineligible because this isn't a Lenis Q&A board, but I feel like these two tools are used together enough that someone on here might have some clarity. Thanks!
Rodrigo Posted April 10, 2024 Posted April 10, 2024 Hi, I never used Lenis with GSAP and React so I couldn't really tell you about it. This does look odd to me TBH: useEffect(() => { function update(time) { lenisRef.current?.lenis?.raf(time * 1000) } gsap.ticker.add(update) return () => { gsap.ticker.remove(update) } }) That useEffect hook doesn't have any dependencies, so every time that component re-renders all that is called again, I would use an empty dependencies array, but they must have a reason for suggesting that. Also Lenis is not a GSAP product so we can't really offer support for it, we have our own smooth scrolling solution in ScrollSmoother: https://gsap.com/docs/v3/Plugins/ScrollSmoother/ Finally performance is a really deep topic and most likely this is tied to perhaps the Lenis react wrapper eating quite some resources and creating this rather than a GSAP specific problem and I don't recall other threads in the forums on this particular subject. Sorry I can't be of more assistance, hopefully other users with more experience with lenis and react can chime in. Happy Tweening!
granularclouds Posted April 10, 2024 Author Posted April 10, 2024 Thank you - I know about ScrollSmoother but between not wishing to tie a feature for a site to a subscription package and also not wishing to work around the position: fixed limitation, for this project in particular I would go with Lenis. I tried a bunch of stuff and the performance is just not ideal, so I think I'll stick with ScrollTrigger and scrub and ditch the smoothing for non-animation things. Thanks!
devshinthant Posted February 1 Posted February 1 I just use like this, I don't wrap my content in ReactLenis Wrapper, cause it makes too laggy on mobile,especially on ios devices import { useLayoutEffect } from 'react' import { useLenisStore } from '@/store/lenis-store' import { useGSAP } from '@gsap/react' import gsap from 'gsap' import Flip from 'gsap/dist/Flip' import ScrollTrigger from 'gsap/dist/ScrollTrigger' import TextPlugin from 'gsap/dist/TextPlugin' import Lenis from 'lenis' /* Plugins */ gsap.registerPlugin(ScrollTrigger) gsap.registerPlugin(useGSAP) gsap.registerPlugin(Flip) gsap.registerPlugin(TextPlugin) ScrollTrigger.normalizeScroll(true) gsap.config({ force3D: true }) ScrollTrigger.config({ ignoreMobileResize: true }) export default function mountLenis() { // eslint-disable-next-line react-hooks/rules-of-hooks const lenisStore = useLenisStore((state) => state) // eslint-disable-next-line react-hooks/rules-of-hooks useLayoutEffect(() => { const lenis = new Lenis({ duration: 1.2, syncTouch: true, smoothWheel: false, }) lenisStore.setLenis(lenis) lenis.on('scroll', ScrollTrigger.update) gsap.registerPlugin(ScrollTrigger) gsap.ticker.add((time) => { lenis.raf(time * 600) }) gsap.ticker.lagSmoothing(0) return () => { ScrollTrigger.getAll().forEach((trigger) => trigger.kill()) lenis.destroy() } // eslint-disable-next-line react-hooks/exhaustive-deps }, []) return null } import { Outlet } from 'react-router-dom' import mountLenis from '@/hooks/mountLenis' import Footer from './footer' export default function Layout() { mountLenis() return ( <div className='relative overflow-x-hidden bg-[#0F0F0F]'> <Outlet /> <Footer /> </div> ) } 1
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