feat: optimize resizing performance

This commit is contained in:
EnixCoda 2021-09-07 23:54:51 +08:00
parent f88d7c0575
commit da930bc35f
4 changed files with 62 additions and 32 deletions

View file

@ -126,6 +126,7 @@ export function SideBar() {
})}
baseSize={baseSize}
onLeave={sidebarToggleMode === 'float' ? () => setShowSideBar(false) : undefined}
sizeVariableMountPoint={sidebarToggleMode === 'persistent' ? document.body : undefined}
>
<div className={'gitako-side-bar-body'}>
<div

View file

@ -4,25 +4,32 @@ import * as React from 'react'
import { useDebounce, useWindowSize } from 'react-use'
import { defaultConfigs } from 'utils/config/helper'
import { cx } from 'utils/cx'
import { setResizingState } from 'utils/DOMHelper'
import { setCSSVariable } from 'utils/DOMHelper'
import * as features from 'utils/features'
import { useCSSVariable } from 'utils/hooks/useCSSVariable'
export type Size = number
type Props = {
baseSize: Size
className?: string
onLeave?: React.HTMLAttributes<HTMLElement>['onMouseLeave']
sizeVariableMountPoint?: HTMLElement
}
const MINIMAL_CONTENT_VIEWPORT_WIDTH = 100
const MINIMAL_WIDTH = 240
function getSafeSize(size: number, width: number) {
if (size > width - MINIMAL_CONTENT_VIEWPORT_WIDTH) return width - MINIMAL_CONTENT_VIEWPORT_WIDTH
if (size < MINIMAL_WIDTH) return MINIMAL_WIDTH
return size
}
export function SideBarBodyWrapper({
baseSize,
className,
children,
onLeave,
sizeVariableMountPoint,
}: React.PropsWithChildren<Props>) {
const [size, setSize] = React.useState(baseSize)
const configContext = useConfigs()
@ -34,27 +41,50 @@ export function SideBarBodyWrapper({
const { width } = useWindowSize()
React.useEffect(() => {
if (size > width - MINIMAL_CONTENT_VIEWPORT_WIDTH)
setSize(width - MINIMAL_CONTENT_VIEWPORT_WIDTH)
else if (size < MINIMAL_WIDTH) setSize(MINIMAL_WIDTH)
const safeSize = getSafeSize(size, width)
if (safeSize !== size) setSize(safeSize)
}, [width, size])
React.useEffect(() => {
setResizingState(true)
const timer = setTimeout(() => setResizingState(false), 100)
return () => clearTimeout(timer)
}, [width, size])
useCSSVariable('--gitako-width', `${size}px`)
const bodyWrapperRef = React.useRef<HTMLDivElement | null>(null)
useDebounce(() => configContext.onChange({ sideBarWidth: size }), 100, [size])
const onResize = React.useCallback((size: number) => {
// do NOT merge this with the above similar effect, side bar will jump otherwise
if (size > width - MINIMAL_CONTENT_VIEWPORT_WIDTH)
setSize(width - MINIMAL_CONTENT_VIEWPORT_WIDTH)
else if (size < MINIMAL_WIDTH) setSize(MINIMAL_WIDTH)
else setSize(size)
}, [])
function apply(sizeVariableMountPoint: HTMLElement | undefined, size: number) {
if (sizeVariableMountPoint)
setCSSVariable(
'--gitako-width',
sizeVariableMountPoint ? `${size}px` : undefined,
sizeVariableMountPoint,
)
if (bodyWrapperRef.current)
setCSSVariable(
'--gitako-width',
sizeVariableMountPoint ? undefined : `${size}px`,
bodyWrapperRef.current,
)
}
// Update size using useEffect would cause delay
const onResize = React.useMemo(() => {
let sizeToApply: number,
applied = true
return (size: number) => {
// do NOT merge this with the above similar effect, side bar will jump otherwise
sizeToApply = getSafeSize(size, width)
setSize(sizeToApply)
if (applied) {
applied = false
requestAnimationFrame(() => {
applied = true
apply(sizeVariableMountPoint, sizeToApply)
})
}
}
}, [width, sizeVariableMountPoint])
React.useEffect(() => {
apply(sizeVariableMountPoint, size)
}, [sizeVariableMountPoint])
const onMouseLeave = React.useCallback(
e => {
@ -65,7 +95,11 @@ export function SideBarBodyWrapper({
)
return (
<div className={cx('gitako-side-bar-body-wrapper', className)} onMouseLeave={onMouseLeave}>
<div
ref={bodyWrapperRef}
className={cx('gitako-side-bar-body-wrapper', className)}
onMouseLeave={onMouseLeave}
>
<div className={'gitako-side-bar-body-wrapper-content'}>{children}</div>
{features.resize && (
<HorizontalResizeHandler

View file

@ -81,9 +81,6 @@ $minimal-z-index: max($github-header-z-index, $github-pull-request-float-header-
}
}
:root {
--gitako-width: #{$side-bar-base-width};
}
.#{$name}-ready {
// github

View file

@ -123,15 +123,13 @@ export function focusSearchInput() {
})
}
export function setResizingState(on: boolean) {
const target = document.querySelector('.gitako-toggle-show-button-wrapper')
if (!target) return
if (on) target.classList.add('resizing')
else target.classList.remove('resizing')
}
export function findNodeElement(node:TreeNode, rootElement: HTMLElement): HTMLElement | null {
export function findNodeElement(node: TreeNode, rootElement: HTMLElement): HTMLElement | null {
const nodeElement = rootElement.querySelector(`a[href="${node.url}"]`)
if (nodeElement instanceof HTMLElement) return nodeElement
return null
}
export function setCSSVariable(name: string, value: string | undefined, element: HTMLElement) {
if (value === undefined) element.style.removeProperty(name)
else element.style.setProperty(name, value)
}