bs.choi Posted March 1, 2022 Share Posted March 1, 2022 Hi. First of all, I apologize for my poor English using a translator. I am making a project using next.js. I implemented smooth-scroll using scrollTrigger while looking at the example in the official documentation. It was working fine, but an unexpected problem occurred when the subpage was added. // set(resize) height function refreshHeight() { const headerH = document.getElementById("header_wrap").offsetHeight; height = scrollContent.clientHeight + headerH; scrollHeightBox.style.height = height + "px"; } ScrollTrigger.addEventListener("refreshInit", refreshHeight); As set in the code above, the height of the scroll-wrapper (div) remains the same as the height set in the main page even after moving to the subpage. So I added the code below to fix this. const routeBtn = document.querySelectorAll(".route"); routeBtn.forEach((elem) => { elem.addEventListener("click", ScrollTrigger.refresh); }); As a result, it was confirmed that there is a slight delay when moving the page, but the height is reset properly. But this time, it doesn't change any more after moving once like Main Page > Sub Page, Sub Page > Main Page. Can you help me? There are three main problems for me. 1. When moving a page, I would like the scrollTrigger to be reset to match the content height of the moved page. (softly) 2. When routing using the Link tag provided by next.js, it automatically scrolls to the top of the page. Blocking that scroll doesn't make it look smooth, and leaving it on doesn't make it look smooth. I also want this to look soft. (Do you think page conversion can be solved through a third-party library?) 3. Is there a way to further optimize the overall performance of scrollTrigger in my code? I wanted to provide an example with codepen, but I don't know how to show the code of the next.js project in codepen, so I attach the code. Below is my whole code. // react hook import { useEffect, useRef } from "react"; // component import Header from "@/components/Header"; import Footer from "@/components/Footer"; // style import "@/styles/reset.css"; import "@/styles/globals.scss"; // library import { gsap } from "gsap"; import { ScrollTrigger } from "gsap/dist/ScrollTrigger"; gsap.registerPlugin(ScrollTrigger); // render export default function MyApp({ Component, pageProps }) { const mainRef = useRef(null); const scrollRef = useRef(null); function setHeight(mainRef) { const target = mainRef.current; const headerHeight = document.getElementById("header_wrap").offsetHeight; target.style.marginTop = `${headerHeight}px`; } useEffect(() => { setHeight(mainRef); smoothScroll(scrollRef); window.addEventListener("resize", () => { setHeight(mainRef); }); }, []); return ( <div> <Header /> <div className="scroll_wrap"> <div className="scroll_container" ref={scrollRef}> <main ref={mainRef}> <div> <Component {...pageProps} /> </div> </main> <Footer /> </div> </div> </div> ); } // smooth scroll function smoothScroll(ref) { const scrollContent = ref.current; const scrollHeightBox = scrollContent.parentElement.parentElement; let height; // set(resize) height function refreshHeight() { const headerH = document.getElementById("header_wrap").offsetHeight; height = scrollContent.clientHeight + headerH; scrollHeightBox.style.height = height + "px"; } ScrollTrigger.addEventListener("refreshInit", refreshHeight); gsap.to(scrollContent, { y: () => document.documentElement.clientHeight - height, ease: "none", onUpdate: ScrollTrigger.update, scrollTrigger: { invalidateOnRefresh: true, start: 0, end: () => height - document.documentElement.clientHeight, scrub: 0.6, }, }); // page route - ScrollTrigger height refresh const routeBtn = document.querySelectorAll(".route"); routeBtn.forEach((elem) => { elem.addEventListener("click", ScrollTrigger.refresh); }); } thank you. Link to comment Share on other sites More sharing options...
OSUblake Posted March 1, 2022 Share Posted March 1, 2022 Hi bs.choi, Can you provide a minimal demo of this issue, like on CodeSandbox? Thanks! Link to comment Share on other sites More sharing options...
bs.choi Posted March 2, 2022 Author Share Posted March 2, 2022 @OSUblake Sorry, I tried to post an example through JSFiddle, but I don't know how to make a demo of my source. I'll mention you again in the comments if I find a way to make an example, thanks Link to comment Share on other sites More sharing options...
OSUblake Posted March 2, 2022 Share Posted March 2, 2022 That's why I said to use CodeSandbox. You can use next.js with it. Link to comment Share on other sites More sharing options...
bs.choi Posted March 2, 2022 Author Share Posted March 2, 2022 @OSUblake https://codesandbox.io/s/awesome-clarke-r9m579?file=/pages/_app.js:1560-1573 Wow!! thank you. Thanks to you, I know how to create the next project example through 'codesandbox'. The code in that link is not exactly the same as my project, but I have made the structure as similar as possible. First of all, the first question was solved through _app.js 28~33 lines. (It was not found in this 'codesandbox' example, but in my actual project. When you first enter the main page > go to the sub page, the height is properly reset according to the content of the sub page. And when you go back to the main page, the smoothScroll function The height set by refreshHeight is different from when you first entered. // In that state, if you press the route button to the main page once more, it works normally again. // This part is not an issue found in 'codesandbox', so my project It seems to be a problem, but if there is something to be expected, please advise.) Regarding the second question, you can still see that the scrolling goes up when you move the page. I scroll to the top, but I hope the process is not visible to the user. The last question is, is there a part in my code related to scrollTrigger that I wrote wrong or should I fix it to improve performance? thank you. Link to comment Share on other sites More sharing options...
OSUblake Posted March 2, 2022 Share Posted March 2, 2022 1 hour ago, bs.choi said: When you first enter the main page > go to the sub page, the height is properly reset according to the content of the sub page. And when you go back to the main page, the smoothScroll function The height set by refreshHeight is different from when you first entered. I'm not sure without being to see the issue. Do you have images on any of the pages that might be affecting the layout after they load? 1 hour ago, bs.choi said: Regarding the second question, you can still see that the scrolling goes up when you move the page. I scroll to the top, but I hope the process is not visible to the user. Maybe you could do a transition to make the transition less noticeable. https://dev.to/anxiny/page-transition-effect-in-nextjs-9ch 1 hour ago, bs.choi said: The last question is, is there a part in my code related to scrollTrigger that I wrote wrong or should I fix it to improve performance? Looks good to me. 👍 Link to comment Share on other sites More sharing options...
bs.choi Posted March 3, 2022 Author Share Posted March 3, 2022 @OSUblake 7 hours ago, OSUblake said: 문제를 보지 않고는 확실하지 않습니다. 페이지가 로드된 후 레이아웃에 영향을 줄 수 있는 이미지가 있습니까? This part found the problematic component. Let's try to solve it ourselves! 7 hours ago, OSUblake said: 전환을 덜 눈에 띄게 만들기 위해 전환을 수행할 수 있습니다. https://dev.to/anxiny/page-transition-effect-in-nextjs-9ch I've been thinking about doing this for a while, but I didn't want to use a third-party library, so I was looking for another way, thank you! 7 hours ago, OSUblake said: 나에게 좋아 보인다. 👍 useEffect(() => { ScrollTrigger.refresh(); }); Is it ok to refresh the scrollTrigger every time the page is routed in _app.js like this? If you take a picture on the console, it does not operate repeatedly dozens of times, but only once. And maybe this is the last question. Thank you so much for the great answers so far. There are anchors like tabs that change their associated contents when clicked. Is there any performance problem with a method like below code?? When refreshed with onStart, it is sometimes not reset to the changed height, so onUpdate was used. function anchor(e) { const target = e.currentTarget; const tabNum = target.getAttribute("data-tab"); const tabCon = document.getElementsByClassName("tab_contents_box"); const offsetY = document.getElementById("header_wrap").offsetHeight; clear(target.parentElement); target.parentElement.classList.add(`${styles.active}`); gsap.to(window, { duration: 1, scrollTo: { y: tabCon, offsetY: offsetY - 1, }, ease: "power4.inOut", onUpdate: () => ScrollTrigger.refresh(), }); setTabNum(tabNum); } If you look at the console, it takes about 60 console shots while changing the contents to fit each anchor, and then stops. Thank you for your hard work over the past few days with my questions using the translator. Link to comment Share on other sites More sharing options...
OSUblake Posted March 3, 2022 Share Posted March 3, 2022 3 hours ago, bs.choi said: There are anchors like tabs that change their associated contents when clicked. Is there any performance problem with a method like below code??When refreshed with onStart, it is sometimes not reset to the changed height, so onUpdate was used. It's not ideal, but if the layout is changing duration scroll, then that's the only option. 3 hours ago, bs.choi said: I've been thinking about doing this for a while, but I didn't want to use a third-party library, so I was looking for another way, thank you! It's a small library. Without it you would have to manually do all the routing without the <Link> component. You would have to fade the current view out, then push the new path, and then in the next view, animate it in. It's probably going to be a lot of work. 3 hours ago, bs.choi said: useEffect(()=>{ScrollTrigger.refresh();}); Is it ok to refresh the scrollTrigger every time the page is routed in _app.js like this? Well, you should probably only do it on the route changes, kind of like this. import { useRouter } from "next/router"; function MyApp() { const router = useRouter(); const refresh = useCallback(() => ScrollTrigger.refresh(), []); useEffect(() => { router.events.on("routeChangeComplete", refresh); return () => router.events.off("routeChangeComplete", refresh); }, [router.events, refresh]); } Link to comment Share on other sites More sharing options...
bs.choi Posted March 3, 2022 Author Share Posted March 3, 2022 @OSUblake thank you so much. I found the cause of the issue with the first question. All images used in my project have a dynamic height (ex. width: 100%; height: auto;) The cause seems to be because the image is refreshed in a state where the height of the image is not recognized before the image is loaded. The reason is, if you do not set the height to be responsive and set it to 150px, the height will be set properly in any situation. So I mean. Is there no way to refresh the image after all images are loaded while maintaining the current state (height: auto) without fixing the height of the image? I made all the images into promise objects and refreshed them when they were all completed through promise.all, but this didn't work. And when I used 'ZachSaucier's solution in the link above, it worked fine, but when I have a lot of images, it seems to perform poorly because of too many calls. However, no matter how many images I have in my project, it won't go up to 100. So it doesn't really matter? When I applied all the work done so far and took a picture of the console, the maximum number of calls in ScrollTrigger.refresh() was about 60 times. (Since ssg build is carried out through next export, it cannot be preloaded using Image api.) Link to comment Share on other sites More sharing options...
OSUblake Posted March 3, 2022 Share Posted March 3, 2022 Have you tried using onLoadingComplete? <Image src={src} onLoadingComplete={() => ScrollTrigger.refresh()} /> Link to comment Share on other sites More sharing options...
bs.choi Posted March 3, 2022 Author Share Posted March 3, 2022 @OSUblake 16 minutes ago, bs.choi said: (Since ssg build is carried out through next export, it cannot be preloaded using Image api.) For this reason I can't use <Image /> Link to comment Share on other sites More sharing options...
OSUblake Posted March 3, 2022 Share Posted March 3, 2022 53 minutes ago, bs.choi said: For this reason I can't use <Image /> Sorry, I don't understand why this wouldn't work.https://nextjs.org/docs/api-reference/next/image#onloadingcomplete I'm not familiar with next.js Image, but that's what I saw as the solution here. https://github.com/vercel/next.js/issues/20368#issuecomment-985944527 1 hour ago, bs.choi said: And when I used 'ZachSaucier's solution in the link above, it worked fine, but when I have a lot of images, it seems to perform poorly because of too many calls. I wouldn't think you would need to call it if it's already complete, because that means it's height has been settled. document.querySelectorAll("img").forEach(img => { if (img.complete) { // ScrollTrigger.refresh(); } else { img.addEventListener('load', imgLoaded => ScrollTrigger.refresh()); } }); 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