Skip to content

Commit 9304ab5

Browse files
committed
split calcuate process
1 parent 2f4c014 commit 9304ab5

File tree

2 files changed

+66
-31
lines changed

2 files changed

+66
-31
lines changed

src/List.tsx

Lines changed: 62 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
getScrollPercentage,
66
getNodeHeight,
77
getRangeIndex,
8-
getStartItemTop,
8+
getItemTop,
99
} from './util';
1010

1111
type RenderFunc<T> = (item: T) => React.ReactNode;
@@ -23,7 +23,9 @@ interface ListState {
2323
status: 'NONE' | 'MEASURE_START' | 'MEASURE_DONE';
2424

2525
scrollTop: number | null;
26+
/** Located item index */
2627
itemIndex: number;
28+
/** Located item bind its height percentage with the `scrollTop` */
2729
itemOffsetPtg: number;
2830
startIndex: number;
2931
endIndex: number;
@@ -44,6 +46,17 @@ interface ListState {
4446
* 3. [Render] Render visible items
4547
* 4. Get all the visible items height
4648
* 5. [Render] Update top item `margin-top` to fit the position
49+
*
50+
* Algorithm:
51+
* We split scroll bar into equal slice. An item with whatever height occupy the same range slice.
52+
* When `scrollTop` change,
53+
* it will calculate the item percentage position and move item to the position.
54+
* Then calculate other item position base on the located item.
55+
*
56+
* Concept:
57+
*
58+
* # located item
59+
* The base position item which other items position calculate base on.
4760
*/
4861
class List<T> extends React.Component<ListProps<T>, ListState> {
4962
static defaultProps = {
@@ -80,32 +93,21 @@ class List<T> extends React.Component<ListProps<T>, ListState> {
8093
* Phase 5: Trigger re-render to use correct position
8194
*/
8295
public componentDidUpdate(prevProps: ListProps<T>) {
83-
const { status, startIndex, endIndex, itemIndex, itemOffsetPtg } = this.state;
84-
const { dataSource } = this.props;
96+
const { status } = this.state;
97+
const { dataSource, height, itemHeight } = this.props;
8598

8699
if (status === 'MEASURE_START') {
100+
const { startIndex, endIndex, itemIndex, itemOffsetPtg } = this.state;
101+
87102
// Record here since measure item height will get warning in `render`
88103
for (let index = startIndex; index <= endIndex; index += 1) {
89104
const eleKey = this.getItemKey(index);
90105
this.itemElementHeights[eleKey] = getNodeHeight(this.itemElements[eleKey]);
91106
}
92107

93-
// // Calculate top visible item top offset
94-
// const scrollPtg = getScrollPercentage(this.listRef.current);
95-
// const locatedItemHeight = this.itemElementHeights[this.getItemKey(itemIndex)] || 0;
96-
// const locatedItemTop = scrollPtg * this.listRef.current.clientHeight;
97-
// const locatedItemOffset = itemOffsetPtg * locatedItemHeight;
98-
// const locatedItemMergedTop =
99-
// this.listRef.current.scrollTop + locatedItemTop - locatedItemOffset;
100-
101-
// let startItemTop = locatedItemMergedTop;
102-
// for (let index = itemIndex - 1; index >= startIndex; index -= 1) {
103-
// startItemTop -= this.itemElementHeights[this.getItemKey(index)] || 0;
104-
// }
105-
106-
const startItemTop = getStartItemTop({
108+
// Calculate top visible item top offset
109+
const locatedItemTop = getItemTop({
107110
itemIndex,
108-
startIndex,
109111
itemOffsetPtg,
110112
itemElementHeights: this.itemElementHeights,
111113
scrollTop: this.listRef.current.scrollTop,
@@ -114,12 +116,52 @@ class List<T> extends React.Component<ListProps<T>, ListState> {
114116
getItemKey: this.getItemKey,
115117
});
116118

119+
let startItemTop = locatedItemTop;
120+
for (let index = itemIndex - 1; index >= startIndex; index -= 1) {
121+
startItemTop -= this.itemElementHeights[this.getItemKey(index)] || 0;
122+
}
123+
117124
this.setState({ status: 'MEASURE_DONE', startItemTop });
118125
}
119126

120127
// Re-calculate the scroll position align with the current visible item position
121-
if (prevProps.dataSource.length !== dataSource.length) {
122-
console.log('!!!!!!');
128+
if (prevProps.dataSource.length !== dataSource.length && height) {
129+
// We will record all the visible item top for next loop match check
130+
const itemTops: { [key: string]: number } = {};
131+
const { startIndex: originStartIndex, itemIndex: originItemIndex } = this.state;
132+
let originStartItemTop = this.state.startItemTop;
133+
for (let index = originStartIndex; index <= originItemIndex; index += 1) {
134+
const key = this.getItemKey(index);
135+
itemTops[key] = originStartItemTop;
136+
originStartItemTop += this.itemElementHeights[key] || 0;
137+
}
138+
139+
console.log('Length changed. Origin top:', itemTops, this.itemElementHeights);
140+
const { scrollHeight, clientHeight } = this.listRef.current;
141+
const maxScrollTop = scrollHeight - clientHeight;
142+
for (let scrollTop = 0; scrollTop <= maxScrollTop; scrollTop += 1) {
143+
const scrollPtg = getScrollPercentage({ scrollTop, scrollHeight, clientHeight });
144+
const visibleCount = Math.ceil(height / itemHeight);
145+
146+
const { itemIndex, itemOffsetPtg, startIndex } = getRangeIndex(
147+
scrollPtg,
148+
dataSource.length,
149+
visibleCount,
150+
);
151+
152+
// const startItemTop = getStartItemTop({
153+
// itemIndex,
154+
// startIndex,
155+
// itemOffsetPtg,
156+
// itemElementHeights: this.itemElementHeights,
157+
// scrollTop: this.listRef.current.scrollTop,
158+
// scrollPtg,
159+
// clientHeight: this.listRef.current.clientHeight,
160+
// getItemKey: this.getItemKey,
161+
// });
162+
163+
// console.log('=>', scrollTop, startIndex, startItemTop);
164+
}
123165
}
124166
}
125167

src/util.ts

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,9 @@ export function getRangeIndex(scrollPtg: number, itemCount: number, visibleCount
8080
};
8181
}
8282

83-
interface ItemTopConfig<T> {
83+
interface ItemTopConfig {
8484
itemIndex: number;
8585
itemElementHeights: { [key: string]: number };
86-
startIndex: number;
8786
itemOffsetPtg: number;
8887

8988
scrollTop: number;
@@ -94,11 +93,10 @@ interface ItemTopConfig<T> {
9493
}
9594

9695
/**
97-
* Calculate virtual list start item top offset position.
96+
* Calculate the located item top.
9897
*/
99-
export function getStartItemTop({
98+
export function getItemTop({
10099
itemIndex,
101-
startIndex,
102100
itemOffsetPtg,
103101
itemElementHeights,
104102
scrollTop,
@@ -112,10 +110,5 @@ export function getStartItemTop({
112110
const locatedItemOffset = itemOffsetPtg * locatedItemHeight;
113111
const locatedItemMergedTop = scrollTop + locatedItemTop - locatedItemOffset;
114112

115-
let startItemTop = locatedItemMergedTop;
116-
for (let index = itemIndex - 1; index >= startIndex; index -= 1) {
117-
startItemTop -= itemElementHeights[getItemKey(index)] || 0;
118-
}
119-
120-
return startItemTop;
113+
return locatedItemMergedTop;
121114
}

0 commit comments

Comments
 (0)