Skip to content

Commit fa0f3a8

Browse files
author
Tenny
committed
feat(VirtualList): 初始渲染
1 parent 6f82190 commit fa0f3a8

File tree

3 files changed

+79
-7
lines changed

3 files changed

+79
-7
lines changed

docs/components/virtuallist.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
<script setup>
1010
import { VirtualList } from "../../src"
11+
12+
1113
</script>
1214

1315
### 基础用法

src/components/VirtualList.vue

Lines changed: 70 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,77 @@
11
<template>
2-
<div class="nt-virtual-list">
2+
<div class="nt-virtual-list" ref="$list">
33
<!-- 占位元素, 用于撑开滚动条,达到滚动效果 -->
4-
<div class="nt-virtual-placeholder"></div>
4+
<div class="nt-virtual-placeholder" ref="$placeholder"></div>
55
<!-- 内容元素, 用于显示列表项 -->
6-
<div class="nt-virtual-content">
6+
<div class="nt-virtual-content" ref="$itemContent">
77
<!-- 列表项 -->
8-
<div class="nt-virtual-item"></div>
8+
<div
9+
v-for="(item, index) in visibleData"
10+
:style="{ height: props.itemSize + 'px' }"
11+
:key="keyField != null ? (item as any)[keyField] : index"
12+
:class="['nt-virtual-item', itemClass]"
13+
>
14+
<slot :item="item"></slot>
15+
</div>
916
</div>
1017
</div>
1118
</template>
12-
<script setup lang="ts"></script>
19+
<script setup lang="ts" generic="T">
20+
import { onMounted, ref } from 'vue';
21+
import { debounce } from 'ph-utils/web';
22+
23+
const props = withDefaults(
24+
defineProps<{
25+
/** 需要展示的数据 */
26+
items: T[];
27+
/** 列表项的高度,用于计算滚动大小和位置 */
28+
itemSize: number;
29+
itemClass?: string;
30+
/** 选项 key 的字段名, 用于 v-for 的 key */
31+
keyField?: string;
32+
}>(),
33+
{
34+
itemClass: '',
35+
},
36+
);
37+
38+
/** 可视区域内能显示的数据总数 */
39+
let visibleCount = 0;
40+
/** 是否正在处理数据 */
41+
let loading = false;
42+
const $list = ref<HTMLDivElement>();
43+
const $itemContent = ref<HTMLDivElement>();
44+
const $placeholder = ref<HTMLDivElement>();
45+
/** 实际显示的数据列表 */
46+
const visibleData = ref<T[]>([]);
47+
48+
function renderData() {
49+
if ($list.value != null) {
50+
// 计算可视区域数据的开始索引
51+
const startIndex = Math.floor($list.value.scrollTop / props.itemSize);
52+
if ($itemContent.value != null) {
53+
// 开始项距离容器顶部的距离, 保证在滚动时数据一直在可视区域内
54+
const top = `${startIndex * props.itemSize}px`;
55+
$itemContent.value.style.top = top;
56+
}
57+
// 生成可视区域数据
58+
visibleData.value = props.items.slice(
59+
startIndex,
60+
startIndex + visibleCount,
61+
) as any[];
62+
}
63+
}
64+
65+
onMounted(() => {
66+
if ($list.value != null) {
67+
visibleCount = Math.ceil($list.value.clientHeight / props.itemSize);
68+
69+
if ($placeholder.value != null) {
70+
const height = props.itemSize * props.items.length;
71+
$placeholder.value.style.height = `${height}px`;
72+
}
73+
74+
renderData(); // 初始化渲染数据
75+
}
76+
});
77+
</script>

style/virtual-list/index.css

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
.nt-virtual-list {
2-
height: 100%;
2+
max-height: 100%;
33
overflow-y: auto;
44
-webkit-overflow-scrolling: touch;
5+
position: relative;
6+
}
7+
.nt-virtual-content {
8+
position: absolute;
9+
top: 0;
10+
width: 100%;
511
}
6-
712
.nt-virtual-item {
813
overflow: hidden;
914
}

0 commit comments

Comments
 (0)