From 60b1bbeee85f609674611c6f2e370464518c77bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E9=B9=8F=E6=9D=B0?= <260083304@qq.com> Date: Tue, 10 Dec 2024 12:33:10 +0800 Subject: [PATCH] =?UTF-8?q?fix:=E5=BD=93=E5=AD=90=E8=8A=82=E7=82=B9?= =?UTF-8?q?=E8=BF=87=E5=A4=9A=E6=97=B6,=E6=BB=9A=E5=8A=A8=E5=88=B0?= =?UTF-8?q?=E6=9C=80=E5=BA=95=E9=83=A8=E6=97=B6,=E4=BC=9A=E4=BA=A7?= =?UTF-8?q?=E7=94=9F=E9=97=AA=E7=83=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/useVirtualList.ts | 56 +++++++++++++++++++++++++++---------- 1 file changed, 41 insertions(+), 15 deletions(-) diff --git a/src/hooks/useVirtualList.ts b/src/hooks/useVirtualList.ts index 95884ee..af982b2 100644 --- a/src/hooks/useVirtualList.ts +++ b/src/hooks/useVirtualList.ts @@ -47,26 +47,37 @@ export const useVirtualList = (nonReactive: INonReactiveData, props: IUseVirtual /** * 计算渲染的节点,基于 scrollTop 计算当前应该渲染哪些节点 */ - const updateRenderNodes = (isScroll: boolean = false): void => { + function updateRenderNodes(isScroll: boolean = false): void { + // 添加边界检查,防止无效更新 + if (blockLength.value === 0) return; + if (blockLength.value > renderAmount.value) { - const scrollTop = Math.max(scrollArea.value.scrollTop, 0) - /** 当前滚动了多少节点 */ - const scrollNodeAmount = Math.floor(scrollTop / props.nodeMinHeight) - renderStart.value = - Math.floor(scrollNodeAmount / props.bufferNodeAmount) * - props.bufferNodeAmount + const scrollTop = scrollArea.value.scrollTop + const maxScrollTop = blockAreaHeight.value - scrollArea.value.clientHeight + + // 添加滚动到底部的判断 + if (scrollTop >= maxScrollTop) { + renderStart.value = Math.max(0, blockLength.value - renderAmount.value) + } else { + const scrollNodeAmount = Math.floor(scrollTop / props.nodeMinHeight) + renderStart.value = Math.floor(scrollNodeAmount / props.bufferNodeAmount) * props.bufferNodeAmount + } } else { renderStart.value = 0 } + + // 避免不必要的更新 if ( isScroll && renderAmountCache.value === renderAmount.value && renderStartCache.value === renderStart.value - ) + ) { return + } + renderNodes.value = nonReactive.blockNodes .slice(renderStart.value, renderStart.value + renderAmount.value) - .map(blockNode => { + .map((blockNode: TreeNode) => { return Object.assign({}, blockNode, { _parent: null, children: [] @@ -105,15 +116,30 @@ export const useVirtualList = (nonReactive: INonReactiveData, props: IUseVirtual blockAreaHeight.value = props.nodeMinHeight * blockLength.value } - const handleTreeScroll = (): void => { + //#endregion Calculate nodes + const isThrottled = ref(false) + + function handleTreeScroll(): void { if (debounceTimer.value) { window.cancelAnimationFrame(debounceTimer.value) } - renderAmountCache.value = renderAmount.value - renderStartCache.value = renderStart.value - debounceTimer.value = window.requestAnimationFrame( - updateRenderNodes.bind(null, true) - ) + + // 添加节流 + if (!isThrottled.value) { + isThrottled.value = true + + renderAmountCache.value = renderAmount.value + renderStartCache.value = renderStart.value + + debounceTimer.value = window.requestAnimationFrame(() => { + updateRenderNodes(true) + + // 重置节流状态 + setTimeout(() => { + isThrottled.value = false + }, 16) // 约60fps + }) + } } /**