Skip to content

Commit 97c9f63

Browse files
committed
feat: 添加virtual-scroller
1 parent a2cd5e6 commit 97c9f63

File tree

12 files changed

+590
-28
lines changed

12 files changed

+590
-28
lines changed

components/virtual-scroller/virtual-scroller.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,17 +123,20 @@ export default defineComponent({
123123
virtualRef.value?.scrollBy(offset);
124124
},
125125
getItemOffset: (index: number) => {
126-
virtualRef.value?.getItemOffset(index);
126+
return virtualRef.value?.getItemOffset(index);
127127
},
128128
getItemSize: (index: number) => {
129-
virtualRef.value?.getItemSize(index);
129+
return virtualRef.value?.getItemSize(index);
130130
},
131131
findStartIndex: () => {
132132
virtualRef.value?.findStartIndex();
133133
},
134134
findEndIndex: () => {
135135
virtualRef.value?.findEndIndex();
136136
},
137+
getOffset,
138+
getScrollSize,
139+
getClientSize,
137140
});
138141

139142
const onScroll = (e: Event) => {

docs/.vitepress/components/virtualList/common.vue

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
<template>
2-
<FVirtualScroller
2+
<FVirtualList
33
ref="virtualList"
44
class="virtual-scroll-list-vertical"
55
dataKey="id"
66
:dataSources="dataItems"
7-
:itemSize="100"
8-
:scrollbarProps="{ height: 400 }"
7+
:estimateSize="80"
8+
:height="height"
99
>
1010
<template #default="{ source, index }">
1111
<div :data-index="index" class="item-inner">
@@ -16,7 +16,7 @@
1616
<div class="desc">{{ source.desc }}</div>
1717
</div>
1818
</template>
19-
</FVirtualScroller>
19+
</FVirtualList>
2020
<FButton style="margin-top: 10px;" @click="addMessage">添加消息{{ dataItems.length }}</FButton>
2121
</template>
2222

@@ -76,8 +76,6 @@ export default {
7676
</script>
7777

7878
<style scoped>
79-
.virtual-scroll-list-vertical .item-inner {
80-
}
8179
.virtual-scroll-list-vertical .item-inner .head {
8280
font-weight: 500;
8381
}

docs/.vitepress/components/virtualList/index.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,10 @@ app.use(FVirtualList);
1212

1313
## 代码演示
1414

15-
### virtual-scroller
16-
17-
:::demo
18-
common.vue
19-
:::
20-
2115
### 不规则纵向滚动列表
2216

2317
:::demo
24-
vertical.vue
18+
common.vue
2519
:::
2620

2721
### 不规则横向滚动列表
@@ -36,6 +30,12 @@ horizontal.vue
3630
maxHeight.vue
3731
:::
3832

33+
### 无限滚动
34+
35+
:::demo
36+
infinite.vue
37+
:::
38+
3939
### 滚动操作
4040

4141
:::demo
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
<template>
2+
<FVirtualList
3+
ref="virtualList"
4+
class="virtual-scroll-list-vertical"
5+
dataKey="id"
6+
:dataSources="dataItems"
7+
:estimateSize="80"
8+
:height="height"
9+
@toTop="handleToTop"
10+
@toBottom="handleToBottom"
11+
>
12+
<template #default="{ source, index }">
13+
<div :data-index="index" class="item-inner">
14+
<div class="head">
15+
<span># {{ source.index }}</span>
16+
<span>{{ source.name }}</span>
17+
</div>
18+
<div class="desc">{{ source.desc }}</div>
19+
</div>
20+
</template>
21+
</FVirtualList>
22+
</template>
23+
24+
<script>
25+
import { ref } from 'vue';
26+
27+
export default {
28+
name: 'Vertical',
29+
setup() {
30+
const virtualList = ref(null);
31+
const height = ref(400);
32+
const sentence3 = [
33+
'BFC(Block formatting context)直译为"块级格式化上下文"。它是一个独立的渲染区域,只有Block-level box参与, 它规定了内部的Block-level Box如何布局,并且与这个区域外部毫不相干。',
34+
'IFC(Inline Formatting Contexts)直译为”内联格式化上下文”,IFC的line box(线框)高度由其包含行内元素中最高的实际高度计算而来(不受到竖直方向的padding/margin影响)IFC(Inline Formatting Contexts)直译为”内联格式化上下文”,IFC的line box(线框)高度由其包含行内元素中最高的实际高度计算而来(不受到竖直方向的padding/margin影响)',
35+
'margin 重合,margin 塌陷',
36+
'html5IFC(Inline Formatting Contexts)直译为”内联格式化上下文”,IFC的line box(线框)高度由其包含行内元素中最高的实际高度计算而来(不受到竖直方向的padding/margin影响)IFC(Inline Formatting Contexts)直译为”内联格式化上下文”,IFC的line box(线框)高度由其包含行内元素中最高的实际高度计算而来(不受到竖直方向的padding/margin影响)IFC(Inline Formatting Contexts)直译为”内联格式化上下文”,IFC的line box(线框)高度由其包含行内元素中最高的实际高度计算而来(不受到竖直方向的padding/margin影响)IFC(Inline Formatting Contexts)直译为”内联格式化上下文”,IFC的line box(线框)高度由其包含行内元素中最高的实际高度计算而来(不受到竖直方向的padding/margin影响)IFC(Inline Formatting Contexts)直译为”内联格式化上下文”,IFC的line box(线框)高度由其包含行内元素中最高的实际高度计算而来(不受到竖直方向的padding/margin影响)IFC(Inline Formatting Contexts)直译为”内联格式化上下文”,IFC的line box(线框)高度由其包含行内元素中最高的实际高度计算而来(不受到竖直方向的padding/margin影响)IFC(Inline Formatting Contexts)直译为”内联格式化上下文”,IFC的line box(线框)高度由其包含行内元素中最高的实际高度计算而来(不受到竖直方向的padding/margin影响)IFC(Inline Formatting Contexts)直译为”内联格式化上下文”,IFC的line box(线框)高度由其包含行内元素中最高的实际高度计算而来(不受到竖直方向的padding/margin影响)IFC(Inline Formatting Contexts)直译为”内联格式化上下文”,IFC的line box(线框)高度由其包含行内元素中最高的实际高度计算而来(不受到竖直方向的padding/margin影响)IFC(Inline Formatting Contexts)直译为”内联格式化上下文”,IFC的line box(线框)高度由其包含行内元素中最高的实际高度计算而来(不受到竖直方向的padding/margin影响)IFC(Inline Formatting Contexts)直译为”内联格式化上下文”,IFC的line box(线框)高度由其包含行内元素中最高的实际高度计算而来(不受到竖直方向的padding/margin影响)',
37+
];
38+
const genUniqueId = (prefix) => {
39+
return `${prefix}$${Math.random().toString(16).substr(9)}`;
40+
};
41+
const getSentences = () => {
42+
const index = Math.floor(Math.random() * (sentence3.length - 1));
43+
return sentence3[index];
44+
};
45+
const TOTAL_COUNT = 1000;
46+
const dataItems = ref([]);
47+
48+
const createData = (length, startIndex, isAdd = true) => {
49+
const result = [];
50+
let count = length;
51+
while (count--) {
52+
const index = isAdd ? startIndex + length - count - 1 : startIndex - count - 1;
53+
result.push({
54+
index,
55+
name: `${Math.random()}`,
56+
id: genUniqueId(index),
57+
desc: getSentences(),
58+
});
59+
}
60+
return result;
61+
};
62+
63+
dataItems.value = createData(TOTAL_COUNT, 1);
64+
65+
const handleToBottom = () => {
66+
dataItems.value = [...dataItems.value, ...createData(10, dataItems.value[dataItems.value.length - 1].index + 1)];
67+
};
68+
const handleToTop = () => {
69+
dataItems.value = [...createData(10, dataItems.value[0].index, false), ...dataItems.value];
70+
setTimeout(() => {
71+
virtualList.value.scrollToIndex(10);
72+
}, 0);
73+
};
74+
return {
75+
virtualList,
76+
dataItems,
77+
height,
78+
handleToBottom,
79+
handleToTop,
80+
};
81+
},
82+
};
83+
</script>
84+
85+
<style scoped>
86+
.virtual-scroll-list-vertical .item-inner .head {
87+
font-weight: 500;
88+
}
89+
.virtual-scroll-list-vertical .item-inner .head span:first-child {
90+
margin-right: 1em;
91+
}
92+
.virtual-scroll-list-vertical .item-inner .desc {
93+
margin-top: 0.5em;
94+
margin-bottom: 1em;
95+
text-align: justify;
96+
}
97+
</style>

docs/.vitepress/components/virtualList/maxHeight.vue

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,15 @@
3232
<FVirtualList
3333
class="virtual-scroll-list-max-height"
3434
wrapClass="virtual-scroll-list-wrap"
35-
:dataKey="(data) => data"
35+
dataKey="id"
3636
:dataSources="vals"
3737
:estimateSize="80"
3838
:height="height"
3939
:maxHeight="maxHeight"
4040
>
4141
<template #default="{ source }">
4242
<div class="virtual-scroll-item">
43-
{{ source }}
43+
{{ source.desc }}
4444
</div>
4545
</template>
4646
</FVirtualList>
@@ -57,9 +57,36 @@ export default {
5757
const maxHeight = ref(200);
5858
5959
const vals = ref([]);
60-
for (let i = 0; i < 6; ++i) {
61-
vals.value.push(i);
62-
}
60+
const sentence3 = [
61+
'BFC(Block formatting context)直译为"块级格式化上下文"。它是一个独立的渲染区域,只有Block-level box参与, 它规定了内部的Block-level Box如何布局,并且与这个区域外部毫不相干。',
62+
'IFC(Inline Formatting Contexts)直译为”内联格式化上下文”,IFC的line box(线框)高度由其包含行内元素中最高的实际高度计算而来(不受到竖直方向的padding/margin影响)IFC(Inline Formatting Contexts)直译为”内联格式化上下文”,IFC的line box(线框)高度由其包含行内元素中最高的实际高度计算而来(不受到竖直方向的padding/margin影响)',
63+
'margin 重合,margin 塌陷',
64+
'html5IFC(Inline Formatting Contexts)直译为”内联格式化上下文”,IFC的line box(线框)高度由其包含行内元素中最高的实际高度计算而来(不受到竖直方向的padding/margin影响)IFC(Inline Formatting Contexts)直译为”内联格式化上下文”,IFC的line box(线框)高度由其包含行内元素中最高的实际高度计算而来(不受到竖直方向的padding/margin影响)IFC(Inline Formatting Contexts)直译为”内联格式化上下文”,IFC的line box(线框)高度由其包含行内元素中最高的实际高度计算而来(不受到竖直方向的padding/margin影响)IFC(Inline Formatting Contexts)直译为”内联格式化上下文”,IFC的line box(线框)高度由其包含行内元素中最高的实际高度计算而来(不受到竖直方向的padding/margin影响)IFC(Inline Formatting Contexts)直译为”内联格式化上下文”,IFC的line box(线框)高度由其包含行内元素中最高的实际高度计算而来(不受到竖直方向的padding/margin影响)IFC(Inline Formatting Contexts)直译为”内联格式化上下文”,IFC的line box(线框)高度由其包含行内元素中最高的实际高度计算而来(不受到竖直方向的padding/margin影响)IFC(Inline Formatting Contexts)直译为”内联格式化上下文”,IFC的line box(线框)高度由其包含行内元素中最高的实际高度计算而来(不受到竖直方向的padding/margin影响)IFC(Inline Formatting Contexts)直译为”内联格式化上下文”,IFC的line box(线框)高度由其包含行内元素中最高的实际高度计算而来(不受到竖直方向的padding/margin影响)IFC(Inline Formatting Contexts)直译为”内联格式化上下文”,IFC的line box(线框)高度由其包含行内元素中最高的实际高度计算而来(不受到竖直方向的padding/margin影响)IFC(Inline Formatting Contexts)直译为”内联格式化上下文”,IFC的line box(线框)高度由其包含行内元素中最高的实际高度计算而来(不受到竖直方向的padding/margin影响)IFC(Inline Formatting Contexts)直译为”内联格式化上下文”,IFC的line box(线框)高度由其包含行内元素中最高的实际高度计算而来(不受到竖直方向的padding/margin影响)',
65+
];
66+
const genUniqueId = (prefix) => {
67+
return `${prefix}$${Math.random().toString(16).substr(9)}`;
68+
};
69+
const getSentences = () => {
70+
const index = Math.floor(Math.random() * (sentence3.length - 1));
71+
return sentence3[index];
72+
};
73+
74+
const createData = (length, startIndex, isAdd = true) => {
75+
const result = [];
76+
let count = length;
77+
while (count--) {
78+
const index = isAdd ? startIndex + length - count - 1 : startIndex - count - 1;
79+
result.push({
80+
index,
81+
name: `${Math.random()}`,
82+
id: genUniqueId(index),
83+
desc: getSentences(),
84+
});
85+
}
86+
return result;
87+
};
88+
89+
vals.value = createData(1000, 1);
6390
6491
watch(
6592
heightType,
@@ -97,9 +124,7 @@ export default {
97124
width: 1000px;
98125
}
99126
.virtual-scroll-list-max-height .virtual-scroll-list-wrap .virtual-scroll-item {
100-
height: 36px;
101-
background: rgba(83, 132, 255, 0.06);
102-
border-bottom: 2px solid #fff;
127+
margin: 0.5em;
103128
}
104129
.virtual-scroll-list-max-height
105130
.virtual-scroll-list-wrap

docs/.vitepress/components/virtualList/vertical.vue renamed to docs/.vitepress/components/virtualScroller/common.vue

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
<template>
2-
<FVirtualList
2+
<FVirtualScroller
33
ref="virtualList"
44
class="virtual-scroll-list-vertical"
5-
dataKey="id"
65
:dataSources="dataItems"
7-
:estimateSize="80"
6+
:itemSize="80"
87
:height="height"
98
>
109
<template #default="{ source, index }">
@@ -16,7 +15,7 @@
1615
<div class="desc">{{ source.desc }}</div>
1716
</div>
1817
</template>
19-
</FVirtualList>
18+
</FVirtualScroller>
2019
<FButton style="margin-top: 10px;" @click="addMessage">添加消息{{ dataItems.length }}</FButton>
2120
</template>
2221

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<template>
2+
<FVirtualScroller
3+
class="virtual-scroll-list-horizontal"
4+
:dataSources="items"
5+
:itemSize="110"
6+
direction="horizontal"
7+
>
8+
<template #default="{ source }">
9+
<div
10+
class="item-inner-horizontal"
11+
:style="{ width: `${source.size}px` }"
12+
>
13+
<div class="index"># {{ source.index }}</div>
14+
<div class="size">{{ source.size }}</div>
15+
</div>
16+
</template>
17+
</FVirtualScroller>
18+
</template>
19+
20+
<script>
21+
const TOTAL_COUNT = 100;
22+
const sizes = [60, 80, 100, 150, 180];
23+
24+
const genUniqueId = (prefix) => {
25+
return `${prefix}$${Math.random().toString(16).substr(9)}`;
26+
};
27+
28+
const dataItems = [];
29+
let count = TOTAL_COUNT;
30+
while (count--) {
31+
const index = TOTAL_COUNT - count;
32+
dataItems.push({
33+
index,
34+
id: genUniqueId(index),
35+
size: sizes[Math.floor(Math.random() * 5)],
36+
});
37+
}
38+
39+
export default {
40+
name: 'Horizontal',
41+
setup() {
42+
return {
43+
items: dataItems,
44+
};
45+
},
46+
};
47+
</script>
48+
49+
<style scoped>
50+
.virtual-scroll-list-horizontal {
51+
width: 100%;
52+
height: 120px;
53+
}
54+
.virtual-scroll-list-horizontal .item-inner-horizontal {
55+
display: flex;
56+
align-items: center;
57+
flex-direction: column;
58+
padding: 2em 0;
59+
}
60+
.virtual-scroll-list-horizontal .item-inner-horizontal .index {
61+
width: 100%;
62+
text-align: center;
63+
}
64+
.virtual-scroll-list-horizontal .item-inner-horizontal .size {
65+
text-align: right;
66+
color: darkgray;
67+
font-size: 16px;
68+
}
69+
</style>

0 commit comments

Comments
 (0)