5
5
getScrollPercentage ,
6
6
getNodeHeight ,
7
7
getRangeIndex ,
8
- getStartItemTop ,
8
+ getItemTop ,
9
9
} from './util' ;
10
10
11
11
type RenderFunc < T > = ( item : T ) => React . ReactNode ;
@@ -23,7 +23,9 @@ interface ListState {
23
23
status : 'NONE' | 'MEASURE_START' | 'MEASURE_DONE' ;
24
24
25
25
scrollTop : number | null ;
26
+ /** Located item index */
26
27
itemIndex : number ;
28
+ /** Located item bind its height percentage with the `scrollTop` */
27
29
itemOffsetPtg : number ;
28
30
startIndex : number ;
29
31
endIndex : number ;
@@ -44,6 +46,17 @@ interface ListState {
44
46
* 3. [Render] Render visible items
45
47
* 4. Get all the visible items height
46
48
* 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.
47
60
*/
48
61
class List < T > extends React . Component < ListProps < T > , ListState > {
49
62
static defaultProps = {
@@ -80,32 +93,21 @@ class List<T> extends React.Component<ListProps<T>, ListState> {
80
93
* Phase 5: Trigger re-render to use correct position
81
94
*/
82
95
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 ;
85
98
86
99
if ( status === 'MEASURE_START' ) {
100
+ const { startIndex, endIndex, itemIndex, itemOffsetPtg } = this . state ;
101
+
87
102
// Record here since measure item height will get warning in `render`
88
103
for ( let index = startIndex ; index <= endIndex ; index += 1 ) {
89
104
const eleKey = this . getItemKey ( index ) ;
90
105
this . itemElementHeights [ eleKey ] = getNodeHeight ( this . itemElements [ eleKey ] ) ;
91
106
}
92
107
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 ( {
107
110
itemIndex,
108
- startIndex,
109
111
itemOffsetPtg,
110
112
itemElementHeights : this . itemElementHeights ,
111
113
scrollTop : this . listRef . current . scrollTop ,
@@ -114,12 +116,52 @@ class List<T> extends React.Component<ListProps<T>, ListState> {
114
116
getItemKey : this . getItemKey ,
115
117
} ) ;
116
118
119
+ let startItemTop = locatedItemTop ;
120
+ for ( let index = itemIndex - 1 ; index >= startIndex ; index -= 1 ) {
121
+ startItemTop -= this . itemElementHeights [ this . getItemKey ( index ) ] || 0 ;
122
+ }
123
+
117
124
this . setState ( { status : 'MEASURE_DONE' , startItemTop } ) ;
118
125
}
119
126
120
127
// 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
+ }
123
165
}
124
166
}
125
167
0 commit comments