Skip to content

Commit af36c09

Browse files
committed
avoid shake
1 parent 13a64fb commit af36c09

File tree

3 files changed

+46
-9
lines changed

3 files changed

+46
-9
lines changed

examples/animate.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ function genItem() {
1616
}
1717

1818
const originDataSource: Item[] = [];
19-
for (let i = 0; i < 10; i += 1) {
19+
for (let i = 0; i < 20; i += 1) {
2020
originDataSource.push(genItem());
2121
}
2222

src/List.tsx

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import {
1414

1515
type RenderFunc<T> = (item: T) => React.ReactNode;
1616

17+
const ITEM_SCALE_RATE = 1;
18+
1719
export interface ListProps<T> extends React.HTMLAttributes<any> {
1820
children: RenderFunc<T>;
1921
dataSource: T[];
@@ -84,6 +86,12 @@ class List<T> extends React.Component<ListProps<T>, ListState> {
8486

8587
itemElementHeights: { [index: number]: number } = {};
8688

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+
8795
/**
8896
* Phase 1: Initial should sync with default scroll top
8997
*/
@@ -156,13 +164,18 @@ class List<T> extends React.Component<ListProps<T>, ListState> {
156164

157165
let bestScrollTop: number | null = null;
158166
let bestSimilarity = Number.MAX_VALUE;
167+
let bestItemIndex = 0;
168+
let bestItemOffsetPtg = 0;
169+
let bestStartIndex = 0;
170+
let bestEndIndex = 0;
171+
159172
let debugItemTops: TargetValues = null;
160173

161174
for (let scrollTop = 0; scrollTop < maxScrollTop; scrollTop += 1) {
162175
const scrollPtg = getScrollPercentage({ scrollTop, scrollHeight, clientHeight });
163176
const visibleCount = Math.ceil(height / itemHeight);
164177

165-
const { itemIndex, itemOffsetPtg, startIndex } = getRangeIndex(
178+
const { itemIndex, itemOffsetPtg, startIndex, endIndex } = getRangeIndex(
166179
scrollPtg,
167180
dataSource.length,
168181
visibleCount,
@@ -187,18 +200,42 @@ class List<T> extends React.Component<ListProps<T>, ListState> {
187200
}
188201

189202
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;
192207
bestScrollTop = scrollTop;
208+
bestItemIndex = itemIndex;
209+
bestItemOffsetPtg = itemOffsetPtg;
210+
bestStartIndex = startIndex;
211+
bestEndIndex = endIndex;
212+
193213
debugItemTops = itemTops;
194214
}
195215

196-
console.log('=>', scrollTop, itemTops, getSimilarity(originItemTops, itemTops));
216+
console.log('COMPARE:', absSimilarity.toFixed(3), itemTops);
197217
}
198218

199219
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;
201223
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+
});
202239
}
203240
}
204241
}
@@ -212,7 +249,7 @@ class List<T> extends React.Component<ListProps<T>, ListState> {
212249
const { scrollTop } = this.listRef.current;
213250

214251
// Skip if `scrollTop` not change to avoid shake
215-
if (scrollTop === this.state.scrollTop) {
252+
if (scrollTop === this.state.scrollTop || this.lockScroll) {
216253
return;
217254
}
218255

@@ -298,7 +335,7 @@ class List<T> extends React.Component<ListProps<T>, ListState> {
298335
}
299336

300337
const { status, startIndex, endIndex, startItemTop } = this.state;
301-
const contentHeight = dataSource.length * itemHeight;
338+
const contentHeight = dataSource.length * itemHeight * ITEM_SCALE_RATE;
302339

303340
return (
304341
<Component style={mergedStyle} {...restProps} onScroll={this.onScroll} ref={this.listRef}>

src/util.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ export function getSimilarity(origin: OriginValues, target: TargetValues): numbe
130130
for (let index = 0; index < origin.length; index += 1) {
131131
const { key, top } = origin[index];
132132
if (key in target) {
133-
return Math.abs(target[key] - top);
133+
return target[key] - top;
134134
}
135135
}
136136
return Number.MAX_VALUE;

0 commit comments

Comments
 (0)