mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-17 08:05:19 +00:00
Improve scroll listener (#2692)
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'gitbook': patch
|
||||
---
|
||||
|
||||
Improve smoothness of scroll listener
|
||||
@@ -2,11 +2,13 @@
|
||||
|
||||
import { Icon } from '@gitbook/icons';
|
||||
import { usePathname } from 'next/navigation';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
|
||||
import { useLanguage, tString } from '@/intl/client';
|
||||
import { tcls } from '@/lib/tailwind';
|
||||
|
||||
import { useScrollListener } from '../hooks/useScrollListener';
|
||||
|
||||
const globalClassName = 'navigation-open';
|
||||
|
||||
/**
|
||||
@@ -28,26 +30,20 @@ export function HeaderMobileMenu(props: Partial<React.ButtonHTMLAttributes<HTMLB
|
||||
}
|
||||
};
|
||||
|
||||
const handleScroll = () => {
|
||||
const windowRef = useRef(window);
|
||||
useScrollListener(() => {
|
||||
if (window.scrollY >= scrollDistance) {
|
||||
setHasScrolled(true);
|
||||
} else {
|
||||
setHasScrolled(false);
|
||||
}
|
||||
};
|
||||
}, windowRef);
|
||||
|
||||
// Close the navigation when navigating to a page
|
||||
useEffect(() => {
|
||||
document.body.classList.remove(globalClassName);
|
||||
}, [pathname]);
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener('scroll', handleScroll);
|
||||
return () => {
|
||||
window.removeEventListener('scroll', handleScroll);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<button
|
||||
{...props}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
'use client';
|
||||
|
||||
import { type RefObject, useEffect, useLayoutEffect, useRef } from 'react';
|
||||
|
||||
/**
|
||||
* Listen for scroll events on an element in a performant way.
|
||||
*
|
||||
* @example
|
||||
* const Example = () => {
|
||||
* const ref = useRef(null);
|
||||
* useScrollListener((event) => {
|
||||
* console.log('scroll', event);
|
||||
* }, ref);
|
||||
*
|
||||
* return <div ref={ref} />;
|
||||
* }
|
||||
*/
|
||||
export function useScrollListener(
|
||||
listener: (event: Event) => void,
|
||||
elementRef: RefObject<HTMLElement | Window> | null,
|
||||
) {
|
||||
const listenerRef = useRef(listener);
|
||||
useLayoutEffect(() => {
|
||||
listenerRef.current = listener;
|
||||
});
|
||||
useEffect(() => {
|
||||
const element = elementRef?.current;
|
||||
if (!element) {
|
||||
return undefined;
|
||||
}
|
||||
let ticking = false;
|
||||
const listener = (ev: Event) => {
|
||||
if (ticking) {
|
||||
return;
|
||||
}
|
||||
requestAnimationFrame(() => {
|
||||
listenerRef.current(ev);
|
||||
ticking = false;
|
||||
});
|
||||
ticking = true;
|
||||
};
|
||||
element.addEventListener('scroll', listener, { passive: true });
|
||||
return () => {
|
||||
element.removeEventListener('scroll', listener);
|
||||
};
|
||||
}, [elementRef]);
|
||||
}
|
||||
Reference in New Issue
Block a user