From 1243182b386054811ff068342f062ce6c2ff6a46 Mon Sep 17 00:00:00 2001 From: Xaroz Date: Thu, 17 Oct 2024 16:08:29 -0400 Subject: [PATCH 1/6] fix: header looping and firefox issue --- src/components/nav/Header.tsx | 7 +++- src/utils/useScrollListener.ts | 74 +++++++++++++++++----------------- 2 files changed, 43 insertions(+), 38 deletions(-) diff --git a/src/components/nav/Header.tsx b/src/components/nav/Header.tsx index c945473..3e97afb 100644 --- a/src/components/nav/Header.tsx +++ b/src/components/nav/Header.tsx @@ -25,16 +25,19 @@ export function Header({ pathName }: { pathName: string }) { return (
Hyperlane diff --git a/src/utils/useScrollListener.ts b/src/utils/useScrollListener.ts index a23981a..579e33e 100644 --- a/src/utils/useScrollListener.ts +++ b/src/utils/useScrollListener.ts @@ -1,47 +1,49 @@ -import { useEffect, useState } from 'react'; +import { useCallback, useEffect, useState } from 'react'; -import { isFirefox } from './browser'; - -export function useScrollThresholdListener(threshold: number, debounce = 500) { +export function useScrollThresholdListener(threshold: number, debounceTime = 500) { const [isAboveThreshold, setIsAbove] = useState(false); const [isDebouncing, setIsDebouncing] = useState(false); + const handleScroll = useCallback(() => { + if (isDebouncing) return; // Skip handling scroll when disabled + + if (window.scrollY > threshold && !isAboveThreshold) { + setIsAbove(true); + setIsDebouncing(true); + } else if (window.scrollY <= threshold && isAboveThreshold) { + setIsAbove(false); + setIsDebouncing(true); + } + }, [threshold, isAboveThreshold, isDebouncing]); + useEffect(() => { - let timeoutId: NodeJS.Timeout | null; - - const listener = () => { - // TODO find a way to make this animation smooth in Firefox - if (isFirefox()) return; - - const handleScroll = () => { - if (window.scrollY > threshold && !isAboveThreshold) { - setIsAbove(true); - setIsDebouncing(true); - } else if (window.scrollY <= threshold && isAboveThreshold) { - setIsAbove(false); - setIsDebouncing(true); - } - }; - - if (isDebouncing) { - if (!timeoutId) { - setTimeout(() => { - setIsDebouncing(false); - timeoutId = null; - handleScroll(); - }, debounce); - } - } else { - handleScroll(); - } - }; + const debouncedHandleScroll = debounce(handleScroll, 30); + + if (!isDebouncing) { + window.addEventListener('scroll', debouncedHandleScroll); + } else { + // Disabling scroll completly if it stills debouncing to prevent looping + const timeoutId = setTimeout(() => { + setIsDebouncing(false); + }, debounceTime); + + return () => clearTimeout(timeoutId); + } - window.addEventListener('scroll', listener, { passive: true }); return () => { - window.removeEventListener('scroll', listener); - if (timeoutId) clearTimeout(timeoutId); + window.removeEventListener('scroll', debouncedHandleScroll); }; - }, [threshold, debounce, isAboveThreshold, isDebouncing]); + }, [handleScroll, isDebouncing, debounceTime]); return isAboveThreshold; } + +function debounce(fn: () => void, delay: number) { + let timeoutId: number; + return function () { + if (timeoutId) { + clearTimeout(timeoutId); + } + timeoutId = window.setTimeout(fn, delay); + }; +} From fa14879d9fe5166c03d60de2d603d4e63685f984 Mon Sep 17 00:00:00 2001 From: Xaroz Date: Thu, 17 Oct 2024 16:15:12 -0400 Subject: [PATCH 2/6] chore: remove will-change from child and use only on parent --- src/components/nav/Header.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/nav/Header.tsx b/src/components/nav/Header.tsx index 3e97afb..1c792d5 100644 --- a/src/components/nav/Header.tsx +++ b/src/components/nav/Header.tsx @@ -25,14 +25,14 @@ export function Header({ pathName }: { pathName: string }) { return (
Date: Thu, 17 Oct 2024 17:56:12 -0400 Subject: [PATCH 3/6] chore: test with rotate --- src/components/nav/Header.tsx | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/components/nav/Header.tsx b/src/components/nav/Header.tsx index 1c792d5..9c2d175 100644 --- a/src/components/nav/Header.tsx +++ b/src/components/nav/Header.tsx @@ -25,7 +25,7 @@ export function Header({ pathName }: { pathName: string }) { return (
@@ -33,11 +33,8 @@ export function Header({ pathName }: { pathName: string }) {
Hyperlane From ede538114d9fd07643de0a77af2ebf264a8b3d3a Mon Sep 17 00:00:00 2001 From: Xaroz Date: Fri, 18 Oct 2024 15:20:10 -0400 Subject: [PATCH 4/6] refactor: useScrollListener and fix firefox scrolling issue --- src/components/nav/Header.tsx | 2 ++ src/utils/useScrollListener.ts | 25 +++++++++++++------------ 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/components/nav/Header.tsx b/src/components/nav/Header.tsx index 9c2d175..1b6d726 100644 --- a/src/components/nav/Header.tsx +++ b/src/components/nav/Header.tsx @@ -31,6 +31,8 @@ export function Header({ pathName }: { pathName: string }) { >
+ {/* We are adding a minimal rotation here to trick the browser to go into hardware acceleration mode + this will this little animation smoother, specially for Firefox*/}
(null); + const handleScroll = useCallback(() => { if (isDebouncing) return; // Skip handling scroll when disabled @@ -16,24 +18,23 @@ export function useScrollThresholdListener(threshold: number, debounceTime = 500 } }, [threshold, isAboveThreshold, isDebouncing]); - useEffect(() => { - const debouncedHandleScroll = debounce(handleScroll, 30); + const debouncedHandleScroll = debounce(handleScroll, 20); - if (!isDebouncing) { - window.addEventListener('scroll', debouncedHandleScroll); - } else { - // Disabling scroll completly if it stills debouncing to prevent looping - const timeoutId = setTimeout(() => { + useEffect(() => { + if (isDebouncing && !timeoutId.current) { + timeoutId.current = setTimeout(() => { setIsDebouncing(false); + timeoutId.current = null; }, debounceTime); - - return () => clearTimeout(timeoutId); } + window.addEventListener('scroll', debouncedHandleScroll); + return () => { window.removeEventListener('scroll', debouncedHandleScroll); + if (timeoutId.current) clearTimeout(timeoutId.current); }; - }, [handleScroll, isDebouncing, debounceTime]); + }, [debouncedHandleScroll, isDebouncing, debounceTime]); return isAboveThreshold; } From 9eb22da366b3e38b29a9576a1e5ebf4999e0f53a Mon Sep 17 00:00:00 2001 From: Xaroz Date: Fri, 18 Oct 2024 15:27:21 -0400 Subject: [PATCH 5/6] chore: fix typo and add passive listener --- src/components/nav/Header.tsx | 2 +- src/utils/useScrollListener.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/nav/Header.tsx b/src/components/nav/Header.tsx index 1b6d726..64f8d96 100644 --- a/src/components/nav/Header.tsx +++ b/src/components/nav/Header.tsx @@ -32,7 +32,7 @@ export function Header({ pathName }: { pathName: string }) {
{/* We are adding a minimal rotation here to trick the browser to go into hardware acceleration mode - this will this little animation smoother, specially for Firefox*/} + this will make the animation a little smoother, specially for Firefox*/}
{ window.removeEventListener('scroll', debouncedHandleScroll); From 58b67b40fda2b7e7a793be3250636b5db7444499 Mon Sep 17 00:00:00 2001 From: Xaroz Date: Mon, 21 Oct 2024 10:26:03 -0400 Subject: [PATCH 6/6] refactor: previous logic fix and timeoutId refactor --- src/components/nav/Header.tsx | 2 +- src/utils/browser.ts | 3 -- src/utils/useScrollListener.ts | 68 ++++++++++++++++------------------ 3 files changed, 32 insertions(+), 41 deletions(-) delete mode 100644 src/utils/browser.ts diff --git a/src/components/nav/Header.tsx b/src/components/nav/Header.tsx index 64f8d96..5fea278 100644 --- a/src/components/nav/Header.tsx +++ b/src/components/nav/Header.tsx @@ -31,7 +31,7 @@ export function Header({ pathName }: { pathName: string }) { >
- {/* We are adding a minimal rotation here to trick the browser to go into hardware acceleration mode + {/* Add a minimal rotation here to trick the browser to go into hardware acceleration mode this will make the animation a little smoother, specially for Firefox*/}
(null); - - const handleScroll = useCallback(() => { - if (isDebouncing) return; // Skip handling scroll when disabled - - if (window.scrollY > threshold && !isAboveThreshold) { - setIsAbove(true); - setIsDebouncing(true); - } else if (window.scrollY <= threshold && isAboveThreshold) { - setIsAbove(false); - setIsDebouncing(true); - } - }, [threshold, isAboveThreshold, isDebouncing]); - - const debouncedHandleScroll = debounce(handleScroll, 20); + const timeoutId = useRef(null); useEffect(() => { - if (isDebouncing && !timeoutId.current) { - timeoutId.current = setTimeout(() => { - setIsDebouncing(false); - timeoutId.current = null; - }, debounceTime); - } - - window.addEventListener('scroll', debouncedHandleScroll, { passive: true }); + const listener = () => { + const handleScroll = () => { + if (isDebouncing) return; + + if (window.scrollY > threshold && !isAboveThreshold) { + setIsAbove(true); + setIsDebouncing(true); + } else if (window.scrollY <= threshold && isAboveThreshold) { + setIsAbove(false); + setIsDebouncing(true); + } + }; + + if (isDebouncing) { + if (!timeoutId.current) { + timeoutId.current = setTimeout(() => { + setIsDebouncing(false); + timeoutId.current = null; + handleScroll(); + }, debounce); + } + } else { + handleScroll(); + } + }; + window.addEventListener('scroll', listener, { passive: true }); return () => { - window.removeEventListener('scroll', debouncedHandleScroll); + window.removeEventListener('scroll', listener); if (timeoutId.current) clearTimeout(timeoutId.current); }; - }, [debouncedHandleScroll, isDebouncing, debounceTime]); + }, [threshold, debounce, isAboveThreshold, isDebouncing]); return isAboveThreshold; } - -function debounce(fn: () => void, delay: number) { - let timeoutId: number; - return function () { - if (timeoutId) { - clearTimeout(timeoutId); - } - timeoutId = window.setTimeout(fn, delay); - }; -}