Skip to content

Commit a5cb6d9

Browse files
committed
minum shake algorithm
1 parent af36c09 commit a5cb6d9

File tree

1 file changed

+49
-40
lines changed

1 file changed

+49
-40
lines changed

src/List.tsx

Lines changed: 49 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -136,41 +136,46 @@ class List<T> extends React.Component<ListProps<T>, ListState> {
136136
this.setState({ status: 'MEASURE_DONE', startItemTop });
137137
}
138138

139+
// TODO: If is add node
139140
// Re-calculate the scroll position align with the current visible item position
140141
if (prevProps.dataSource.length !== dataSource.length && height) {
141-
// We will record all the visible item top for next loop match check
142-
const originItemTops: OriginValues = [];
142+
/**
143+
* When an item removed,
144+
* we should know the item (called as compare item) before the removed item position.
145+
* After loop re-calculation, we need keep the compare item position not change.
146+
*/
147+
// Find removed item key
148+
const removedItemIndex: number = prevProps.dataSource.findIndex((_, index) => {
149+
const key = this.getItemKey(index, prevProps);
150+
return dataSource.every((__, nextIndex) => key !== this.getItemKey(nextIndex));
151+
});
152+
153+
// 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+
143157
const { startIndex: originStartIndex, itemIndex: originItemIndex } = this.state;
144-
let originStartItemTop = this.state.startItemTop;
158+
let compareItemTop = this.state.startItemTop;
145159
for (let index = originStartIndex; index <= originItemIndex; index += 1) {
146160
const key = this.getItemKey(index, prevProps);
147-
originItemTops.push({
148-
key,
149-
top: originStartItemTop,
150-
});
151-
originStartItemTop += this.itemElementHeights[key] || 0;
152-
}
161+
if (key === compareItemKey) {
162+
break;
163+
}
153164

154-
console.log(
155-
'Length changed. Origin top:',
156-
originItemTops,
157-
this.state.startIndex,
158-
this.itemElementHeights,
159-
);
165+
compareItemTop += this.itemElementHeights[key] || 0;
166+
}
160167

161-
// Loop to get the adjusted item top
168+
// Loop to generate compared item top and find best one
162169
const { scrollHeight, clientHeight } = this.listRef.current;
163170
const maxScrollTop = scrollHeight - clientHeight;
164171

165-
let bestScrollTop: number | null = null;
166-
let bestSimilarity = Number.MAX_VALUE;
172+
let bestScrollTop: number = null;
173+
let bestSimilarity: number = Number.MAX_VALUE;
167174
let bestItemIndex = 0;
168175
let bestItemOffsetPtg = 0;
169176
let bestStartIndex = 0;
170177
let bestEndIndex = 0;
171178

172-
let debugItemTops: TargetValues = null;
173-
174179
for (let scrollTop = 0; scrollTop < maxScrollTop; scrollTop += 1) {
175180
const scrollPtg = getScrollPercentage({ scrollTop, scrollHeight, clientHeight });
176181
const visibleCount = Math.ceil(height / itemHeight);
@@ -191,34 +196,38 @@ class List<T> extends React.Component<ListProps<T>, ListState> {
191196
getItemKey: this.getItemKey,
192197
});
193198

194-
const itemTops: TargetValues = {};
195-
for (let index = itemIndex; index > startIndex; index -= 1) {
199+
let newCompareItemTop: number = null;
200+
for (let index = itemIndex; index >= startIndex; index -= 1) {
196201
const key = this.getItemKey(index);
197-
itemTops[key] = locatedItemTop;
202+
if (key === compareItemKey) {
203+
newCompareItemTop = locatedItemTop;
204+
break;
205+
}
206+
207+
if (index <= 0) {
208+
break;
209+
}
210+
198211
const prevItemKey = this.getItemKey(index - 1);
199212
locatedItemTop -= this.itemElementHeights[prevItemKey] || 0;
200213
}
201214

202-
const similarity = getSimilarity(originItemTops, itemTops);
203-
const absSimilarity = Math.abs(similarity);
204-
205-
if (absSimilarity < bestSimilarity) {
206-
bestSimilarity = absSimilarity;
207-
bestScrollTop = scrollTop;
208-
bestItemIndex = itemIndex;
209-
bestItemOffsetPtg = itemOffsetPtg;
210-
bestStartIndex = startIndex;
211-
bestEndIndex = endIndex;
212-
213-
debugItemTops = itemTops;
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;
224+
}
214225
}
215-
216-
console.log('COMPARE:', absSimilarity.toFixed(3), itemTops);
217226
}
218227

219-
if (bestScrollTop) {
220-
console.log('Best Top:', bestScrollTop, bestSimilarity, debugItemTops);
221-
console.log('StartIndex:', bestStartIndex, this.getItemKey(bestStartIndex));
228+
// Update `scrollTop` to new calculated position
229+
console.log('DONE:', bestScrollTop);
230+
if (bestScrollTop !== null) {
222231
this.lockScroll = true;
223232
this.listRef.current.scrollTop = bestScrollTop;
224233

0 commit comments

Comments
 (0)