diff --git a/src/common/Hooks.res b/src/common/Hooks.res index 5617bf2aa..e20fda83b 100644 --- a/src/common/Hooks.res +++ b/src/common/Hooks.res @@ -21,31 +21,48 @@ type scrollDir = | Up({scrollY: int}) | Down({scrollY: int}) +type action = + | Skip + | EnterTop + | DownToUp + | UpToDown + | KeepDown + | KeepUp + /** This will cause highly frequent events, so use it only once in a root as possible. And split the children components to prevent heavy ones from being re-rendered unnecessarily. */ -let useScrollDirection = () => { - let (_, startScrollEventTransition) = React.useTransition() - let (scrollDir, setScrollDir) = React.useState(() => Up({scrollY: %raw(`Infinity`)})) +let useScrollDirection = (~topMargin=80, ~threshold=20) => { + let (scrollDir, setScrollDir) = React.useState(() => Up({ + scrollY: 999999, // pseudo infinity + })) React.useEffect(() => { let onScroll = _e => { - startScrollEventTransition(() => { - setScrollDir( - prev => { - let Up({scrollY}) | Down({scrollY}) = prev - if scrollY === 0 || scrollY > Webapi.Window.scrollY { - Up({scrollY: Webapi.Window.scrollY}) - } else { - Down({scrollY: Webapi.Window.scrollY}) - } - }, - ) + setScrollDir(prev => { + let scrollY = Webapi.Window.scrollY + let enterTopMargin = scrollY <= topMargin + + let action = switch prev { + | Up(_) if enterTopMargin => Skip + | Down(_) if enterTopMargin => EnterTop + | Up({scrollY: prevScrollY}) if prevScrollY < scrollY => UpToDown + | Up({scrollY: prevScrollY}) if prevScrollY - threshold >= scrollY => KeepUp + | Down({scrollY: prevScrollY}) if scrollY < prevScrollY => DownToUp + | Down({scrollY: prevScrollY}) if scrollY - threshold >= prevScrollY => KeepDown + | _ => Skip + } + + switch action { + | Skip => prev + | EnterTop | DownToUp | KeepUp => Up({scrollY: scrollY}) + | UpToDown | KeepDown => Down({scrollY: scrollY}) + } }) } Webapi.Window.addEventListener("scroll", onScroll) Some(() => Webapi.Window.removeEventListener("scroll", onScroll)) - }, []) + }, [topMargin, threshold]) scrollDir }