Olav Posted April 1, 2023 Share Posted April 1, 2023 Hi everyone, I've been fighting with gsap and react all day and finally have got it to work properly. Now I'm in a very early stage of developing a website, and was wondering if my approach is 'correct', I've included a simple component using gsap which in functionality works fine, but I'd like to know where I can improve. 'use client' import Image from 'next/image' import { useEffect, useRef } from 'react' import { gsap } from 'gsap/dist/gsap' export default function Preloader() { const el = useRef<HTMLDivElement | null>(null) const tl = useRef() useEffect(() => { const ctx = gsap.context(() => { tl.current = gsap .timeline() .to( '#preloader-logo', { y: '160%', duration: 1, ease: 'power2.inOut' }, 1, ) .to( '#preloader-bg', { y: '-100%', duration: 0.5, ease: 'power2.inOut' }, '<+=.5', ) }, el) }, []) return ( <div ref={el} aria-busy="true" aria-label="Loading"> <div className="fixed inset-0 flex h-full w-full items-center justify-center bg-crftd-purple" id="preloader-bg" > <div className="overflow-hidden"> <Image priority src="/logos/crftd-letters-dark.svg" alt="CRFTD text logo" width={160} height={80} id="preloader-logo" /> </div> </div> </div> ) } Anyone have some tips? Live version is visible here btw; https://www.crftd.nl Thanks in advance, I really appreciate your time. Kind regards, Link to comment Share on other sites More sharing options...
Rodrigo Posted April 1, 2023 Share Posted April 1, 2023 Hi, In general everything looks good in the snippet you're showing. The only missing part is that you're not doing proper clean up in your effect hook: useLayoutEffect(() => { const ctx = gsap.context(); return () => ctx.revert(); // <- clean up!! }, []); Take a good look at this resources https://greensock.com/react Also we recommend using the layout effect since is better for animations. Considering that you're using Next you could run into issues using the layout effect so it's better to use a custom hook for checking which effect hook should be used depending if the code is being run on the server or the client https://greensock.com/react-advanced#useIsomorphicLayoutEffect Hopefully this helps. Happy Tweening! Link to comment Share on other sites More sharing options...
Olav Posted April 1, 2023 Author Share Posted April 1, 2023 Hi Rodrigo, Thanks for your quick reply! I've implemented your suggestions. Another question, maybe a bit off topic but it's probably a very short answer; In the installation guide I stumbled upon the following: If you are using server side rendering, you may need to add GSAP to your transpile property in your configuration settings (like nuxt.config): build: { ... transpile: ['gsap'], }, Now I've also implemented that in my project, the next.config.js, but am getting warnings; warn - Invalid next.config.js options detected: warn - The root value has an unexpected property, build, which is not in the list of allowed properties (amp, analyticsId, assetPrefix, basePath, cleanDistDir, compiler, compress, configOrigin, crossOrigin, devIndicators, distDir, env, eslint, excludeDefaultMomentLocales, experimental, exportPathMap, generateBuildId, generateEtags, headers, httpAgentOptions, i18n, images, modularizeImports, onDemandEntries, optimizeFonts, output, outputFileTracing, pageExtensions, poweredByHeader, productionBrowserSourceMaps, publicRuntimeConfig, reactStrictMode, redirects, rewrites, sassOptions, serverRuntimeConfig, skipMiddlewareUrlNormalize, skipTrailingSlashRedirect, staticPageGenerationTimeout, swcMinify, target, trailingSlash, transpilePackages, typescript, useFileSystemPublicRoutes, webpack). warn - See more info here: https://nextjs.org/docs/messages/invalid-next-config My next.config.js looks like this: const nextConfig = { experimental: { appDir: true, }, build: { transpile: ['gsap'], }, } module.exports = nextConfig Is this a known thing for next and do you know of any fix? Thanks in advance! Link to comment Share on other sites More sharing options...
Rodrigo Posted April 1, 2023 Share Posted April 1, 2023 Hi, No that extra config is needed just for Nuxt (Vue based SSR and static sites generator) not for Next so just remove it and leave your next config file as it was before. Happy Tweening! Link to comment Share on other sites More sharing options...
GreenSock Posted April 1, 2023 Share Posted April 1, 2023 If you're using a percentage-based x/y transform, I generally encourage use of xPercent/yPercent. More flexibility because you can layer on normal x/y later too, and it keeps things clean: // old y: '160%' // new yPercent: 160 Happy tweening! Link to comment Share on other sites More sharing options...
Olav Posted April 2, 2023 Author Share Posted April 2, 2023 Both Jack and Rodrigo, many thanks! Everything works exactly as expected, good to know that the extra config isn't necessary for nextjs. Link to comment Share on other sites More sharing options...
Olav Posted April 25, 2023 Author Share Posted April 25, 2023 Hi! I took another look at my code, since I want the scrollbar to be locked while the animation is playing. I made the following: 'use client' import Image from 'next/image' import { useEffect, useLayoutEffect, useRef } from 'react' import { gsap } from 'gsap/dist/gsap' export default function Preloader() { const el = useRef<HTMLDivElement | null>(null) const tl = useRef() const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect useIsomorphicLayoutEffect(() => { document.body.style.overflow = 'hidden' const ctx = gsap.context(() => { tl.current = gsap .timeline() .set('body', { overflow: 'hidden' }) .to( '#preloader-logo', { yPercent: 160, duration: 1, ease: 'power2.inOut' }, 1, ) .to( '#preloader-bg', { yPercent: -100, duration: 0.25, ease: 'power2.inOut' }, '<+=.5', ) }, el) return () => { document.body.style.removeProperty('overflow') ctx.revert() } }, []) return ( <div ref={el} aria-busy="true" aria-label="Loading"> <div className="fixed inset-0 z-50 flex h-full w-full items-center justify-center bg-crftd-purple" id="preloader-bg" > <div className="overflow-hidden"> <Image priority src="/logos/crftd-letters-dark.svg" alt="CRFTD text logo" width={160} height={80} id="preloader-logo" /> </div> </div> </div> ) } Just before the ctx, I add overflow: hidden to the body element, which works perfectly fine. Then just before the cleanup, I try to remove the overflow property, but this doesn't seem to work. Also, as you can see I tried adding a .set to for the overflow hidden at the beginning of my timeline, but that doesn't work either. For now I'm looking at the document.body.style lines. Then I tried adding a console.log to my return statement, which also never fires, which means that the 'cleanup' also never goes through. Can't seem to figure out what is going on here. Any clue's? Link to comment Share on other sites More sharing options...
Rodrigo Posted April 25, 2023 Share Posted April 25, 2023 Hi, Unfortunately without a minimal demo there is not much we can do. The one thing I can tell you is that your cleanup code is going to run only when that component is unmounted, whether you do that manually or when you navigate to another page using Next's routing. Right now the fact that your cleanup code is not running and the overflow is not removed from the body tag makes perfect sense based on the code you're posting here. What you could try is using an onComplete callback in order to remove the overflow: hidden from the body tag: useIsomorphicLayoutEffect(() => { document.body.style.overflow = 'hidden' const ctx = gsap.context(() => { tl.current = gsap .timeline({ onComplete: () => gsap.set('body', { overflow: 'auto' }), }) .set('body', { overflow: 'hidden' }) .to( '#preloader-logo', { yPercent: 160, duration: 1, ease: 'power2.inOut' }, 1, ) .to( '#preloader-bg', { yPercent: -100, duration: 0.25, ease: 'power2.inOut' }, '<+=.5', ) }, el) return () => ctx.revert(); }, []) Also there is no need to create the isomorphic layout hook inside the component definition, with that approach you'll have to create the custom hook on every page/component that needs it and that could become quite difficult to track. Just create a helper file and export it from there: // /helpers/isomorphicLayout.js import { useEffect, useLayoutEffect } from "react"; export const useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect; // /pages/index.js import { useIsomorphicLayoutEffect } from "../helper/isomorphicEffect"; export default function Home () { useIsomorphicLayoutEffect(() => { // your code here }, []); }; I created a simple example using Next and GSAP without any route transitions so you can see the approach and fork it if you need to create a minimal demo: https://stackblitz.com/edit/nextjs-hwpi1b Hopefully this helps. Happy Tweening! 1 Link to comment Share on other sites More sharing options...
Olav Posted April 25, 2023 Author Share Posted April 25, 2023 I changed my code to the following: useIsomorphicLayoutEffect(() => { const ctx = gsap.context(() => { tl.current = gsap .timeline({ onStart: () => { document.body.style.overflow = 'hidden' document.body.style.cursor = 'wait' }, }) .to( '#preloader-logo', { yPercent: 160, duration: 1, ease: 'power2.inOut' }, 1, ) .to( '#preloader-bg', { yPercent: -100, duration: 0.25, ease: 'power2.inOut', onStart: () => { document.body.style.overflow = 'auto' document.body.style.cursor = 'auto' }, }, '<+=.5', ) }, el) return () => ctx.revert() }, []) Which seems to work perfectly fine! Thanks for your suggestion @Rodrigo 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