@@ -14,6 +14,8 @@ import {
14
14
15
15
type RenderFunc < T > = ( item : T ) => React . ReactNode ;
16
16
17
+ const ITEM_SCALE_RATE = 1 ;
18
+
17
19
export interface ListProps < T > extends React . HTMLAttributes < any > {
18
20
children : RenderFunc < T > ;
19
21
dataSource : T [ ] ;
@@ -84,6 +86,12 @@ class List<T> extends React.Component<ListProps<T>, ListState> {
84
86
85
87
itemElementHeights : { [ index : number ] : number } = { } ;
86
88
89
+ /**
90
+ * Lock scroll process with `onScroll` event.
91
+ * This is used for `dataSource` length change and `scrollTop` restore
92
+ */
93
+ lockScroll : boolean = false ;
94
+
87
95
/**
88
96
* Phase 1: Initial should sync with default scroll top
89
97
*/
@@ -156,13 +164,18 @@ class List<T> extends React.Component<ListProps<T>, ListState> {
156
164
157
165
let bestScrollTop : number | null = null ;
158
166
let bestSimilarity = Number . MAX_VALUE ;
167
+ let bestItemIndex = 0 ;
168
+ let bestItemOffsetPtg = 0 ;
169
+ let bestStartIndex = 0 ;
170
+ let bestEndIndex = 0 ;
171
+
159
172
let debugItemTops : TargetValues = null ;
160
173
161
174
for ( let scrollTop = 0 ; scrollTop < maxScrollTop ; scrollTop += 1 ) {
162
175
const scrollPtg = getScrollPercentage ( { scrollTop, scrollHeight, clientHeight } ) ;
163
176
const visibleCount = Math . ceil ( height / itemHeight ) ;
164
177
165
- const { itemIndex, itemOffsetPtg, startIndex } = getRangeIndex (
178
+ const { itemIndex, itemOffsetPtg, startIndex, endIndex } = getRangeIndex (
166
179
scrollPtg ,
167
180
dataSource . length ,
168
181
visibleCount ,
@@ -187,18 +200,42 @@ class List<T> extends React.Component<ListProps<T>, ListState> {
187
200
}
188
201
189
202
const similarity = getSimilarity ( originItemTops , itemTops ) ;
190
- if ( similarity < bestSimilarity ) {
191
- bestSimilarity = similarity ;
203
+ const absSimilarity = Math . abs ( similarity ) ;
204
+
205
+ if ( absSimilarity < bestSimilarity ) {
206
+ bestSimilarity = absSimilarity ;
192
207
bestScrollTop = scrollTop ;
208
+ bestItemIndex = itemIndex ;
209
+ bestItemOffsetPtg = itemOffsetPtg ;
210
+ bestStartIndex = startIndex ;
211
+ bestEndIndex = endIndex ;
212
+
193
213
debugItemTops = itemTops ;
194
214
}
195
215
196
- console . log ( '=> ' , scrollTop , itemTops , getSimilarity ( originItemTops , itemTops ) ) ;
216
+ console . log ( 'COMPARE: ' , absSimilarity . toFixed ( 3 ) , itemTops ) ;
197
217
}
198
218
199
219
if ( bestScrollTop ) {
200
- console . log ( 'Best Top:' , bestScrollTop , debugItemTops ) ;
220
+ console . log ( 'Best Top:' , bestScrollTop , bestSimilarity , debugItemTops ) ;
221
+ console . log ( 'StartIndex:' , bestStartIndex , this . getItemKey ( bestStartIndex ) ) ;
222
+ this . lockScroll = true ;
201
223
this . listRef . current . scrollTop = bestScrollTop ;
224
+
225
+ this . setState ( {
226
+ status : 'MEASURE_START' ,
227
+ scrollTop : bestScrollTop ,
228
+ itemIndex : bestItemIndex ,
229
+ itemOffsetPtg : bestItemOffsetPtg ,
230
+ startIndex : bestStartIndex ,
231
+ endIndex : bestEndIndex ,
232
+ } ) ;
233
+
234
+ requestAnimationFrame ( ( ) => {
235
+ requestAnimationFrame ( ( ) => {
236
+ this . lockScroll = false ;
237
+ } ) ;
238
+ } ) ;
202
239
}
203
240
}
204
241
}
@@ -212,7 +249,7 @@ class List<T> extends React.Component<ListProps<T>, ListState> {
212
249
const { scrollTop } = this . listRef . current ;
213
250
214
251
// Skip if `scrollTop` not change to avoid shake
215
- if ( scrollTop === this . state . scrollTop ) {
252
+ if ( scrollTop === this . state . scrollTop || this . lockScroll ) {
216
253
return ;
217
254
}
218
255
@@ -298,7 +335,7 @@ class List<T> extends React.Component<ListProps<T>, ListState> {
298
335
}
299
336
300
337
const { status, startIndex, endIndex, startItemTop } = this . state ;
301
- const contentHeight = dataSource . length * itemHeight ;
338
+ const contentHeight = dataSource . length * itemHeight * ITEM_SCALE_RATE ;
302
339
303
340
return (
304
341
< Component style = { mergedStyle } { ...restProps } onScroll = { this . onScroll } ref = { this . listRef } >
0 commit comments