Optimize scrolling. Add navbar auto hide and show.

This commit is contained in:
2023-09-10 01:02:48 +08:00
parent bbf587482e
commit ccf905bea1
4 changed files with 157 additions and 127 deletions

View File

@@ -1,12 +1,9 @@
import React from 'react'
interface FitFullscreenProps extends PropsWithChildren { interface FitFullscreenProps extends PropsWithChildren {
zIndex?: number zIndex?: number
backgroundColor?: string backgroundColor?: string
ref?: RefObject<HTMLDivElement>
} }
const FitFullScreen: React.FC<FitFullscreenProps> = (props) => { const FitFullScreen = forwardRef<HTMLDivElement, FitFullscreenProps>((props, ref) => {
return ( return (
<> <>
<div <div
@@ -15,11 +12,12 @@ const FitFullScreen: React.FC<FitFullscreenProps> = (props) => {
zIndex: props.zIndex, zIndex: props.zIndex,
backgroundColor: props.backgroundColor backgroundColor: props.backgroundColor
}} }}
ref={ref}
> >
{props.children} {props.children}
</div> </div>
</> </>
) )
} })
export default FitFullScreen export default FitFullScreen

View File

@@ -1,19 +1,130 @@
import React from 'react'
import '@/assets/css/hide-scrollbar.scss' import '@/assets/css/hide-scrollbar.scss'
interface HideScrollbarProps extends PropsWithChildren { export interface HideScrollbarElement {
getHideScrollbarRef: (hideScrollbarRef: RefObject<HTMLElement>) => void 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<K extends keyof HTMLElementEventMap>(
type: K,
listener: (this: HTMLDivElement, ev: HTMLElementEventMap[K]) => never,
options?: boolean | AddEventListenerOptions
): void
addEventListener(
type: string,
listener: EventListenerOrEventListenerObject,
options?: boolean | AddEventListenerOptions
): void
removeEventListenerWithType<K extends keyof HTMLElementEventMap>(
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<HideScrollbarProps> = (props) => { const HideScrollbar = forwardRef<HideScrollbarElement, PropsWithChildren>((props, ref) => {
const hideScrollbarRef = useRef<HTMLDivElement>(null) const rootRef = useRef<HTMLDivElement>(null)
const [verticalScrollbarWidth, setVerticalScrollbarWidth] = useState(0) const [verticalScrollbarWidth, setVerticalScrollbarWidth] = useState(0)
const [horizontalScrollbarWidth, setHorizontalScrollbarWidth] = useState(0) const [horizontalScrollbarWidth, setHorizontalScrollbarWidth] = useState(0)
props.getHideScrollbarRef(hideScrollbarRef) useImperativeHandle<HideScrollbarElement, HideScrollbarElement>(
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<K extends keyof HTMLElementEventMap>(
type: K,
listener: (this: HTMLDivElement, ev: HTMLElementEventMap[K]) => never,
options?: boolean | AddEventListenerOptions
): void {
rootRef.current?.addEventListener<K>(type, listener, options)
},
addEventListener(
type: string,
listener: EventListenerOrEventListenerObject,
options?: boolean | AddEventListenerOptions
): void {
rootRef.current?.addEventListener(type, listener, options)
},
removeEventListenerWithType<K extends keyof HTMLElementEventMap>(
type: K,
listener: (this: HTMLDivElement, ev: HTMLElementEventMap[K]) => never,
options?: boolean | EventListenerOptions
): void {
rootRef.current?.removeEventListener<K>(type, listener, options)
},
removeEventListener(
type: string,
listener: EventListenerOrEventListenerObject,
options?: boolean | EventListenerOptions
): void {
rootRef.current?.removeEventListener(type, listener, options)
}
}
},
[]
)
useEffect(() => { useEffect(() => {
const hideScrollbarElement = hideScrollbarRef.current const hideScrollbarElement = rootRef.current
const windowResizeListener = () => { const windowResizeListener = () => {
setVerticalScrollbarWidth( setVerticalScrollbarWidth(
@@ -41,7 +152,7 @@ const HideScrollbar: React.FC<HideScrollbarProps> = (props) => {
return ( return (
<> <>
<div <div
ref={hideScrollbarRef} ref={rootRef}
id={'hide-scrollbar'} id={'hide-scrollbar'}
style={{ style={{
width: `calc(100vw + ${verticalScrollbarWidth}px)`, width: `calc(100vw + ${verticalScrollbarWidth}px)`,
@@ -52,6 +163,6 @@ const HideScrollbar: React.FC<HideScrollbarProps> = (props) => {
</div> </div>
</> </>
) )
} })
export default HideScrollbar export default HideScrollbar

View File

@@ -7,7 +7,8 @@ import { MainFrameworkContext } from '@/pages/MainFramework.tsx'
const Home: React.FC = () => { const Home: React.FC = () => {
const { const {
controllers: { scrollY } hideScrollbarRef,
navbarHiddenState: { navbarHidden, setNavbarHidden }
} = useContext(MainFrameworkContext) } = useContext(MainFrameworkContext)
const fitFullScreenRef = useRef<HTMLDivElement>(null) const fitFullScreenRef = useRef<HTMLDivElement>(null)
@@ -33,9 +34,32 @@ const Home: React.FC = () => {
} }
const handleScrollDown = () => { 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 ( return (
<> <>
<FitFullScreen zIndex={100} backgroundColor={'#FBFBFB'} ref={fitFullScreenRef}> <FitFullScreen zIndex={100} backgroundColor={'#FBFBFB'} ref={fitFullScreenRef}>
@@ -55,6 +79,7 @@ const Home: React.FC = () => {
</FitCenter> </FitCenter>
</FitFullScreen> </FitFullScreen>
<FitFullScreen /> <FitFullScreen />
<FitFullScreen />
</> </>
) )
} }

View File

@@ -2,136 +2,32 @@ import React from 'react'
import '@/assets/css/header.scss' import '@/assets/css/header.scss'
import LoadingMask from '@/components/LoadingMask.tsx' import LoadingMask from '@/components/LoadingMask.tsx'
import router from '@/router' import router from '@/router'
import HideScrollbar from '@/components/HideScrollbar.tsx' import HideScrollbar, { HideScrollbarElement } from '@/components/HideScrollbar.tsx'
export const MainFrameworkContext = createContext<{ export const MainFrameworkContext = createContext<{
navbarHiddenState: { navbarHiddenState: {
navbarHidden: boolean navbarHidden: boolean
setNavbarHidden: (newValue: boolean) => void setNavbarHidden: (newValue: boolean) => void
} }
controllers: { hideScrollbarRef: RefObject<HideScrollbarElement>
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
}
}>({ }>({
navbarHiddenState: { navbarHiddenState: {
navbarHidden: false, navbarHidden: false,
setNavbarHidden: () => undefined setNavbarHidden: () => undefined
}, },
controllers: { hideScrollbarRef: createRef()
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.')
}
}
}) })
const MainFramework: React.FC = () => { const MainFramework: React.FC = () => {
const [navbarHidden, setNavbarHidden] = useState(false) const [navbarHidden, setNavbarHidden] = useState(true)
const [hideScrollbarRef, setHideScrollbarRef] = useState<RefObject<HTMLElement>>() const hideScrollbarRef = useRef<HideScrollbarElement>(null)
const routeId = useMatches()[1].id const routeId = useMatches()[1].id
const routeChildren = router.routes[0].children?.find((value) => value.id === routeId)?.children const routeChildren = router.routes[0].children?.find((value) => value.id === routeId)?.children
const getHideScrollbarRef = (ref: RefObject<HTMLElement>) => {
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 ( return (
<> <>
<HideScrollbar getHideScrollbarRef={getHideScrollbarRef}> <HideScrollbar ref={hideScrollbarRef}>
<div className={'body'}> <div className={'body'}>
<header className={'nav ' + (navbarHidden ? 'hide' : '')}> <header className={'nav ' + (navbarHidden ? 'hide' : '')}>
<a className={'logo'} href={'https://fatweb.top'}> <a className={'logo'} href={'https://fatweb.top'}>
@@ -160,7 +56,7 @@ const MainFramework: React.FC = () => {
<MainFrameworkContext.Provider <MainFrameworkContext.Provider
value={{ value={{
navbarHiddenState: { navbarHidden, setNavbarHidden }, navbarHiddenState: { navbarHidden, setNavbarHidden },
controllers: hideScrollbarRefController hideScrollbarRef
}} }}
> >
<Suspense <Suspense