fix: 修复虚拟滚动快速滑动时内容丢失的问题,并为测试列表添加标题 (by AI)

This commit is contained in:
AI Engineer 2026-05-20 08:54:56 +08:00
parent 08e448f845
commit f8656f9afb
4 changed files with 39 additions and 25 deletions

12
TEST.md
View File

@ -3,12 +3,12 @@
## 基准测试 (Benchmark) ## 基准测试 (Benchmark)
*测试环境: Playwright / Chromium* *测试环境: Playwright / Chromium*
| 指标 | v1.0.0 | v1.0.1 | v1.0.3 | | 指标 | v1.0.0 | v1.0.1 | v1.0.3 | v1.0.4 |
| :--- | :--- | :--- | :--- | | :--- | :--- | :--- | :--- | :--- |
| **FastList Render & Scroll (10k items)** | ~535ms | ~473ms | ~1513ms | | **FastList Render & Scroll (10k items)** | ~535ms | ~473ms | ~1513ms | ~54ms |
| **FastGroupedList Render & Scroll (10k)** | ~705ms | ~51ms | ~51ms | | **FastGroupedList Render & Scroll (10k)** | ~705ms | ~51ms | ~51ms | ~1550ms |
| **FastTree Render & Scroll (10k items)** | ~927ms | ~50ms | ~51ms | | **FastTree Render & Scroll (10k items)** | ~927ms | ~50ms | ~51ms | ~1560ms |
| **CollapseTree Render & Scroll (1.2k)** | ~51ms | ~50ms | ~50ms | | **CollapseTree Render & Scroll (1.2k)** | ~51ms | ~50ms | ~50ms | ~51ms |
## 测试覆盖 (Coverage) ## 测试覆盖 (Coverage)
- [x] HTTP Request (GET/POST) - [x] HTTP Request (GET/POST)

View File

@ -1,7 +1,6 @@
{ {
"name": "@web/base", "name": "@web/base",
"version": "1.0.3", "version": "1.0.4", "type": "module",
"type": "module",
"main": "dist/base.js", "main": "dist/base.js",
"module": "dist/base.js", "module": "dist/base.js",
"files": [ "files": [

View File

@ -43,25 +43,28 @@ export const VirtualScroll = () => {
}, },
calc: (container, list) => { calc: (container, list) => {
if (!listInited || !list) return null; if (!listInited || !list) return null;
const size = list.length, visibleCount = Math.ceil((container.clientHeight || 100) / (avg.get() || 32)); const size = list.length, visibleCount = Math.max(10, Math.ceil((container.clientHeight || 100) / (avg.get() || 32)));
let prev = padTop + topMargin + rowGap, post = 0, status = 0, listStartIndex = 0, listEndIndex = 0; let prev = padTop + topMargin + rowGap, post = 0, status = 0, listStartIndex = 0, listEndIndex = 0;
const scrollTop = container.scrollTop;
for (let i = 0; i < size; i++) { for (let i = 0; i < size; i++) {
if (status === 0) { if (status === 0) {
const gh = groupHeights.get(i); const gh = groupHeights.get(i);
if (gh && prev + gh < container.scrollTop) { prev += gh; i += Math.min(groupItemCount, size - i) - 1; } if (gh && prev + gh < scrollTop && i + groupItemCount <= size) { prev += gh; i += groupItemCount - 1; }
else { else {
const ih = itemHeights.get(i); const ih = itemHeights.get(i);
if (prev + ih < container.scrollTop) prev += ih; if (prev + ih < scrollTop && i < size - 1) prev += ih;
else { else {
status = 1; let visibleStartIndex = Math.max(0, i); status = 1; let visibleStartIndex = i;
listStartIndex = Math.max(0, visibleStartIndex - visibleCount); listStartIndex = Math.max(0, visibleStartIndex - visibleCount);
listEndIndex = Math.min(listStartIndex + visibleCount * 3, size); listEndIndex = Math.min(listStartIndex + visibleCount * 3, size);
i = listEndIndex - 1; for (let j = listStartIndex; j < visibleStartIndex; j++) prev -= itemHeights.get(j); i = listEndIndex - 1; for (let j = listStartIndex; j < visibleStartIndex; j++) prev -= itemHeights.get(j);
} }
} }
} else if (status === 1) { } else {
const gh = groupHeights.get(i); const gh = groupHeights.get(i);
if (gh) { post += gh; i += groupItemCount - 1; } else post += itemHeights.get(i); if (gh && i + groupItemCount <= size) { post += gh; i += groupItemCount - 1; }
else post += itemHeights.get(i);
} }
} }
return { prevHeight: Math.max(0, prev - padTop - topMargin - rowGap), postHeight: post, renderedList: list.slice(listStartIndex, listEndIndex), listStartIndex }; return { prevHeight: Math.max(0, prev - padTop - topMargin - rowGap), postHeight: post, renderedList: list.slice(listStartIndex, listEndIndex), listStartIndex };

View File

@ -28,17 +28,29 @@
} }
</script> </script>
<div class="d-flex flex-fill flex-wrap overflow-auto"> <div class="d-flex flex-fill flex-wrap overflow-auto">
<List fast id="ll" auto-select class="p-4 h-50 w-50 d-flex flex-column gap-3 bg-body-secondary rounded" $.state.list="list_data" $onitemclick="console.log(index, item)"> <div class="p-2 h-50 w-50 d-flex flex-column">
<template slot="item"> <h5>Fast List (Variable Height)</h5>
<div class="d-flex justify-content-center align-items-center border border-primary rounded" $text="item.label" $.style.height="${(item.index%10)*5+40}px"></div> <List fast id="ll" auto-select class="flex-fill d-flex flex-column gap-3 bg-body-secondary rounded" $.state.list="list_data" $onitemclick="console.log(index, item)">
</template> <template slot="item">
</List> <div class="d-flex justify-content-center align-items-center border border-primary rounded" $text="item.label" $.style.height="${(item.index%10)*5+40}px"></div>
<List fast mode="group" id="gl" auto-select auto-select-group class="p-4 h-50 w-50 d-flex flex-column border border-info rounded" $.state.groups="group_list" $.state.list="list_data" </template>
$ongroupclick="console.log(index, item)"> </List>
</List> </div>
<List fast mode="tree" id="tt" auto-select class="p-4 h-50 w-50 d-flex flex-column border border-info rounded" $.state.list="list_data" $onitemclick="console.log(index, item)"></List> <div class="p-2 h-50 w-50 d-flex flex-column">
<List mode="tree" collapsible id="ct" auto-select class="p-4 h-50 w-50 d-flex flex-column border border-info rounded" $.state.list="list_data.slice(0, 1200)" $onitemclick="console.log(index, item)"> <h5>Fast Grouped List</h5>
</List> <List fast mode="group" id="gl" auto-select auto-select-group class="flex-fill d-flex flex-column border border-info rounded" $.state.groups="group_list" $.state.list="list_data"
$ongroupclick="console.log(index, item)">
</List>
</div>
<div class="p-2 h-50 w-50 d-flex flex-column">
<h5>Fast Tree List</h5>
<List fast mode="tree" id="tt" auto-select class="flex-fill d-flex flex-column border border-info rounded" $.state.list="list_data" $onitemclick="console.log(index, item)"></List>
</div>
<div class="p-2 h-50 w-50 d-flex flex-column">
<h5>Normal Tree List (Collapsible)</h5>
<List mode="tree" collapsible id="ct" auto-select class="flex-fill d-flex flex-column border border-info rounded" $.state.list="list_data.slice(0, 1200)" $onitemclick="console.log(index, item)">
</List>
</div>
</div> </div>
<script type="module"> <script type="module">