Skip to content

Commit c1f9d8a

Browse files
committed
more stable scroll
1 parent a5cb6d9 commit c1f9d8a

File tree

2 files changed

+63
-58
lines changed

2 files changed

+63
-58
lines changed

src/List.tsx

Lines changed: 63 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,6 @@ import {
77
getRangeIndex,
88
getItemTop,
99
GHOST_ITEM_KEY,
10-
OriginValues,
11-
TargetValues,
12-
getSimilarity,
1310
} from './util';
1411

1512
type RenderFunc<T> = (item: T) => React.ReactNode;
@@ -151,20 +148,27 @@ class List<T> extends React.Component<ListProps<T>, ListState> {
151148
});
152149

153150
// We use the item before removed item as base compare position to enhance the accuracy
154-
const compareItemIndex = removedItemIndex - 1;
155-
const compareItemKey = this.getItemKey(compareItemIndex, prevProps);
156-
157151
const { startIndex: originStartIndex, itemIndex: originItemIndex } = this.state;
158-
let compareItemTop = this.state.startItemTop;
152+
153+
let originCompareItemIndex = removedItemIndex - 1;
154+
if (originCompareItemIndex >= originItemIndex) originCompareItemIndex = originItemIndex;
155+
const compareItemKey = this.getItemKey(originCompareItemIndex, prevProps);
156+
157+
let originCompareItemTop = this.state.startItemTop;
159158
for (let index = originStartIndex; index <= originItemIndex; index += 1) {
160159
const key = this.getItemKey(index, prevProps);
161160
if (key === compareItemKey) {
162161
break;
163162
}
164163

165-
compareItemTop += this.itemElementHeights[key] || 0;
164+
originCompareItemTop += this.itemElementHeights[key] || 0;
166165
}
167166

167+
// Find current compare item index
168+
const compareItemIndex = dataSource.findIndex(
169+
(_, index) => this.getItemKey(index) === compareItemKey,
170+
);
171+
168172
// Loop to generate compared item top and find best one
169173
const { scrollHeight, clientHeight } = this.listRef.current;
170174
const maxScrollTop = scrollHeight - clientHeight;
@@ -186,47 +190,65 @@ class List<T> extends React.Component<ListProps<T>, ListState> {
186190
visibleCount,
187191
);
188192

189-
let locatedItemTop = getItemTop({
190-
itemIndex,
191-
itemOffsetPtg,
192-
itemElementHeights: this.itemElementHeights,
193-
scrollTop: this.listRef.current.scrollTop,
194-
scrollPtg,
195-
clientHeight: this.listRef.current.clientHeight,
196-
getItemKey: this.getItemKey,
197-
});
198-
199-
let newCompareItemTop: number = null;
200-
for (let index = itemIndex; index >= startIndex; index -= 1) {
201-
const key = this.getItemKey(index);
202-
if (key === compareItemKey) {
203-
newCompareItemTop = locatedItemTop;
204-
break;
205-
}
193+
/**
194+
* No need to check if compare item out of the index
195+
* to save performance.
196+
*/
197+
if (startIndex <= compareItemIndex && compareItemIndex <= endIndex) {
198+
let locatedItemTop = getItemTop({
199+
itemIndex,
200+
itemOffsetPtg,
201+
itemElementHeights: this.itemElementHeights,
202+
scrollTop: this.listRef.current.scrollTop,
203+
scrollPtg,
204+
clientHeight: this.listRef.current.clientHeight,
205+
getItemKey: this.getItemKey,
206+
});
206207

207-
if (index <= 0) {
208-
break;
208+
let newCompareItemTop: number = null;
209+
if (compareItemIndex <= itemIndex) {
210+
// Measure if compare item is before located item
211+
for (let index = itemIndex; index >= startIndex; index -= 1) {
212+
const key = this.getItemKey(index);
213+
if (key === compareItemKey) {
214+
newCompareItemTop = locatedItemTop;
215+
break;
216+
}
217+
218+
if (index <= 0) {
219+
break;
220+
}
221+
222+
const prevItemKey = this.getItemKey(index - 1);
223+
locatedItemTop -= this.itemElementHeights[prevItemKey] || 0;
224+
}
225+
} else {
226+
// Measure if compare item is after located item
227+
for (let index = itemIndex; index <= endIndex; index += 1) {
228+
const key = this.getItemKey(index);
229+
locatedItemTop += this.itemElementHeights[key] || 0;
230+
if (key === compareItemKey) {
231+
newCompareItemTop = locatedItemTop;
232+
break;
233+
}
234+
}
209235
}
210236

211-
const prevItemKey = this.getItemKey(index - 1);
212-
locatedItemTop -= this.itemElementHeights[prevItemKey] || 0;
213-
}
214-
215-
if (newCompareItemTop !== null) {
216-
const similarity = Math.abs(newCompareItemTop - compareItemTop);
217-
if (similarity < bestSimilarity) {
218-
bestScrollTop = scrollTop;
219-
bestSimilarity = similarity;
220-
bestItemIndex = itemIndex;
221-
bestItemOffsetPtg = itemOffsetPtg;
222-
bestStartIndex = startIndex;
223-
bestEndIndex = endIndex;
237+
if (newCompareItemTop !== null) {
238+
const similarity = Math.abs(newCompareItemTop - originCompareItemTop);
239+
if (similarity < bestSimilarity) {
240+
bestScrollTop = scrollTop;
241+
bestSimilarity = similarity;
242+
bestItemIndex = itemIndex;
243+
bestItemOffsetPtg = itemOffsetPtg;
244+
bestStartIndex = startIndex;
245+
bestEndIndex = endIndex;
246+
}
224247
}
225248
}
226249
}
227250

228251
// Update `scrollTop` to new calculated position
229-
console.log('DONE:', bestScrollTop);
230252
if (bestScrollTop !== null) {
231253
this.lockScroll = true;
232254
this.listRef.current.scrollTop = bestScrollTop;

src/util.ts

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -118,20 +118,3 @@ export function getItemTop({
118118

119119
return locatedItemMergedTop;
120120
}
121-
122-
export type OriginValues = {
123-
key: string;
124-
top: number;
125-
}[];
126-
export interface TargetValues {
127-
[key: string]: number;
128-
}
129-
export function getSimilarity(origin: OriginValues, target: TargetValues): number {
130-
for (let index = 0; index < origin.length; index += 1) {
131-
const { key, top } = origin[index];
132-
if (key in target) {
133-
return target[key] - top;
134-
}
135-
}
136-
return Number.MAX_VALUE;
137-
}

0 commit comments

Comments
 (0)