From ccf905bea1c9801be7df17682dfd2fac72b30648 Mon Sep 17 00:00:00 2001 From: FatttSnake Date: Sun, 10 Sep 2023 01:02:48 +0800 Subject: [PATCH] Optimize scrolling. Add navbar auto hide and show. --- src/components/FitFullScreen.tsx | 8 +- src/components/HideScrollbar.tsx | 129 ++++++++++++++++++++++++++++--- src/components/Home.tsx | 29 ++++++- src/pages/MainFramework.tsx | 118 ++-------------------------- 4 files changed, 157 insertions(+), 127 deletions(-) diff --git a/src/components/FitFullScreen.tsx b/src/components/FitFullScreen.tsx index 35e44ac..f4e0402 100644 --- a/src/components/FitFullScreen.tsx +++ b/src/components/FitFullScreen.tsx @@ -1,12 +1,9 @@ -import React from 'react' - interface FitFullscreenProps extends PropsWithChildren { zIndex?: number backgroundColor?: string - ref?: RefObject } -const FitFullScreen: React.FC = (props) => { +const FitFullScreen = forwardRef((props, ref) => { return ( <>
= (props) => { zIndex: props.zIndex, backgroundColor: props.backgroundColor }} + ref={ref} > {props.children}
) -} +}) export default FitFullScreen diff --git a/src/components/HideScrollbar.tsx b/src/components/HideScrollbar.tsx index 8cce6c0..2d61017 100644 --- a/src/components/HideScrollbar.tsx +++ b/src/components/HideScrollbar.tsx @@ -1,19 +1,130 @@ -import React from 'react' import '@/assets/css/hide-scrollbar.scss' -interface HideScrollbarProps extends PropsWithChildren { - getHideScrollbarRef: (hideScrollbarRef: RefObject) => void +export interface HideScrollbarElement { + scrollTo(x: number, y: number): void + scrollX(x: number): void + scrollY(y: number): void + scrollLeft(length: number): void + scrollRight(length: number): void + scrollUp(length: number): void + scrollDown(length: number): void + getX(): number + getY(): number + addEventListenerWithType( + type: K, + listener: (this: HTMLDivElement, ev: HTMLElementEventMap[K]) => never, + options?: boolean | AddEventListenerOptions + ): void + addEventListener( + type: string, + listener: EventListenerOrEventListenerObject, + options?: boolean | AddEventListenerOptions + ): void + removeEventListenerWithType( + type: K, + listener: (this: HTMLDivElement, ev: HTMLElementEventMap[K]) => never, + options?: boolean | EventListenerOptions + ): void + removeEventListener( + type: string, + listener: EventListenerOrEventListenerObject, + options?: boolean | EventListenerOptions + ): void } -const HideScrollbar: React.FC = (props) => { - const hideScrollbarRef = useRef(null) +const HideScrollbar = forwardRef((props, ref) => { + const rootRef = useRef(null) const [verticalScrollbarWidth, setVerticalScrollbarWidth] = useState(0) const [horizontalScrollbarWidth, setHorizontalScrollbarWidth] = useState(0) - props.getHideScrollbarRef(hideScrollbarRef) + useImperativeHandle( + ref, + () => { + return { + scrollTo(x, y) { + rootRef.current?.scrollTo({ + left: x, + top: y, + behavior: 'smooth' + }) + }, + scrollX(x) { + rootRef.current?.scrollTo({ + left: x, + behavior: 'smooth' + }) + }, + scrollY(y) { + rootRef.current?.scrollTo({ + top: y, + behavior: 'smooth' + }) + }, + scrollLeft(length) { + rootRef.current?.scrollTo({ + left: rootRef.current?.scrollLeft - length, + behavior: 'smooth' + }) + }, + scrollRight(length) { + rootRef.current?.scrollTo({ + left: rootRef.current?.scrollLeft + length, + behavior: 'smooth' + }) + }, + scrollUp(length) { + rootRef.current?.scrollTo({ + top: rootRef.current?.scrollTop - length, + behavior: 'smooth' + }) + }, + scrollDown(length) { + rootRef.current?.scrollTo({ + top: rootRef.current?.scrollTop + length, + behavior: 'smooth' + }) + }, + getX() { + return rootRef.current?.scrollLeft ?? 0 + }, + getY() { + return rootRef.current?.scrollTop ?? 0 + }, + addEventListenerWithType( + type: K, + listener: (this: HTMLDivElement, ev: HTMLElementEventMap[K]) => never, + options?: boolean | AddEventListenerOptions + ): void { + rootRef.current?.addEventListener(type, listener, options) + }, + addEventListener( + type: string, + listener: EventListenerOrEventListenerObject, + options?: boolean | AddEventListenerOptions + ): void { + rootRef.current?.addEventListener(type, listener, options) + }, + removeEventListenerWithType( + type: K, + listener: (this: HTMLDivElement, ev: HTMLElementEventMap[K]) => never, + options?: boolean | EventListenerOptions + ): void { + rootRef.current?.removeEventListener(type, listener, options) + }, + removeEventListener( + type: string, + listener: EventListenerOrEventListenerObject, + options?: boolean | EventListenerOptions + ): void { + rootRef.current?.removeEventListener(type, listener, options) + } + } + }, + [] + ) useEffect(() => { - const hideScrollbarElement = hideScrollbarRef.current + const hideScrollbarElement = rootRef.current const windowResizeListener = () => { setVerticalScrollbarWidth( @@ -41,7 +152,7 @@ const HideScrollbar: React.FC = (props) => { return ( <>
= (props) => {
) -} +}) export default HideScrollbar diff --git a/src/components/Home.tsx b/src/components/Home.tsx index ff100c6..060ae4f 100644 --- a/src/components/Home.tsx +++ b/src/components/Home.tsx @@ -7,7 +7,8 @@ import { MainFrameworkContext } from '@/pages/MainFramework.tsx' const Home: React.FC = () => { const { - controllers: { scrollY } + hideScrollbarRef, + navbarHiddenState: { navbarHidden, setNavbarHidden } } = useContext(MainFrameworkContext) const fitFullScreenRef = useRef(null) @@ -33,9 +34,32 @@ const Home: React.FC = () => { } const handleScrollDown = () => { - scrollY(1000) + hideScrollbarRef.current?.scrollY(fitFullScreenRef.current?.offsetHeight ?? 0) } + useEffect(() => { + const hideScrollbarDOM = hideScrollbarRef.current + const scrollListener = () => { + if ( + hideScrollbarDOM && + fitFullScreenRef.current && + hideScrollbarDOM.getY() < fitFullScreenRef.current?.offsetHeight + ) { + if (!navbarHidden) { + setNavbarHidden(true) + } + } else { + if (navbarHidden) { + setNavbarHidden(false) + } + } + } + hideScrollbarDOM?.addEventListener('scroll', scrollListener) + return () => { + hideScrollbarDOM?.removeEventListener('scroll', scrollListener) + } + }, [hideScrollbarRef, navbarHidden, setNavbarHidden]) + return ( <> @@ -55,6 +79,7 @@ const Home: React.FC = () => { + ) } diff --git a/src/pages/MainFramework.tsx b/src/pages/MainFramework.tsx index 5d08abb..1d69c5e 100644 --- a/src/pages/MainFramework.tsx +++ b/src/pages/MainFramework.tsx @@ -2,136 +2,32 @@ import React from 'react' import '@/assets/css/header.scss' import LoadingMask from '@/components/LoadingMask.tsx' import router from '@/router' -import HideScrollbar from '@/components/HideScrollbar.tsx' +import HideScrollbar, { HideScrollbarElement } from '@/components/HideScrollbar.tsx' export const MainFrameworkContext = createContext<{ navbarHiddenState: { navbarHidden: boolean setNavbarHidden: (newValue: boolean) => void } - controllers: { - scrollX(x: number): void - scrollY(y: number): void - scrollTo(x: number, y: number): void - getX(): number | undefined - getY(): number | undefined - scrollLeft(length: number): void - scrollRight(length: number): void - scrollUp(length: number): void - scrollDown(length: number): void - } + hideScrollbarRef: RefObject }>({ navbarHiddenState: { navbarHidden: false, setNavbarHidden: () => undefined }, - controllers: { - scrollX: function (): void { - throw new Error('Function not implemented.') - }, - scrollY: function (): void { - throw new Error('Function not implemented.') - }, - scrollTo: function (): void { - throw new Error('Function not implemented.') - }, - getX: function (): number | undefined { - throw new Error('Function not implemented.') - }, - getY: function (): number | undefined { - throw new Error('Function not implemented.') - }, - scrollLeft: function (): void { - throw new Error('Function not implemented.') - }, - scrollRight: function (): void { - throw new Error('Function not implemented.') - }, - scrollUp: function (): void { - throw new Error('Function not implemented.') - }, - scrollDown: function (): void { - throw new Error('Function not implemented.') - } - } + hideScrollbarRef: createRef() }) const MainFramework: React.FC = () => { - const [navbarHidden, setNavbarHidden] = useState(false) + const [navbarHidden, setNavbarHidden] = useState(true) - const [hideScrollbarRef, setHideScrollbarRef] = useState>() + const hideScrollbarRef = useRef(null) const routeId = useMatches()[1].id const routeChildren = router.routes[0].children?.find((value) => value.id === routeId)?.children - const getHideScrollbarRef = (ref: RefObject) => { - setHideScrollbarRef(ref) - } - - const hideScrollbarRefController = { - scrollX(x: number): void { - hideScrollbarRef?.current?.scrollTo({ - left: x, - top: hideScrollbarRef?.current?.scrollTop, - behavior: 'smooth' - }) - }, - - scrollY(y: number): void { - hideScrollbarRef?.current?.scrollTo({ - left: hideScrollbarRef?.current?.scrollLeft, - top: y, - behavior: 'smooth' - }) - }, - - scrollLeft(length: number): void { - hideScrollbarRef?.current?.scrollTo({ - left: (hideScrollbarRef?.current?.scrollLeft ?? 0) - length, - behavior: 'smooth' - }) - }, - - scrollRight(length: number): void { - hideScrollbarRef?.current?.scrollTo({ - left: (hideScrollbarRef?.current?.scrollLeft ?? 0) + length, - behavior: 'smooth' - }) - }, - - scrollUp(length: number): void { - hideScrollbarRef?.current?.scrollTo({ - top: (hideScrollbarRef?.current?.scrollTop ?? 0) - length, - behavior: 'smooth' - }) - }, - - scrollDown(length: number): void { - hideScrollbarRef?.current?.scrollTo({ - top: (hideScrollbarRef?.current?.scrollTop ?? 0) + length, - behavior: 'smooth' - }) - }, - - scrollTo(x: number, y: number): void { - hideScrollbarRef?.current?.scrollTo({ - left: x, - top: y, - behavior: 'smooth' - }) - }, - - getX(): number | undefined { - return hideScrollbarRef?.current?.scrollLeft - }, - - getY(): number | undefined { - return hideScrollbarRef?.current?.scrollTop - } - } - return ( <> - +