@@ -85,6 +85,9 @@ export class AutoSizeVirtualScrollStrategy implements VirtualScrollStrategy {
85
85
/** The last measured size of the rendered content in the viewport. */
86
86
private _lastRenderedContentOffset : number ;
87
87
88
+ /** The number of consecutive cycles where removing extra items has failed. */
89
+ private _removalFailures = 0 ;
90
+
88
91
/**
89
92
* @param minBufferPx The minimum amount of buffer rendered beyond the viewport (in pixels).
90
93
* If the amount of buffer dips below this number, more items will be rendered.
@@ -182,6 +185,8 @@ export class AutoSizeVirtualScrollStrategy implements VirtualScrollStrategy {
182
185
if ( scrollMagnitude >= viewport . getViewportSize ( ) ) {
183
186
this . _setScrollOffset ( ) ;
184
187
} else {
188
+ // The currently rendered range.
189
+ const renderedRange = viewport . getRenderedRange ( ) ;
185
190
// The number of new items to render on the side the user is scrolling towards. Rather than
186
191
// just filling the underscan space, we actually fill enough to have a buffer size of
187
192
// `addBufferPx`. This gives us a little wiggle room in case our item size estimate is off.
@@ -192,11 +197,12 @@ export class AutoSizeVirtualScrollStrategy implements VirtualScrollStrategy {
192
197
const overscan = ( scrollDelta < 0 ? endBuffer : startBuffer ) - this . _minBufferPx +
193
198
scrollMagnitude ;
194
199
// The number of currently rendered items to remove on the side the user is scrolling away
195
- // from.
196
- const removeItems = Math . max ( 0 , Math . floor ( overscan / this . _averager . getAverageItemSize ( ) ) ) ;
200
+ // from. If removal has failed in recent cycles we are less aggressive in how much we try to
201
+ // remove.
202
+ const removeItems = Math . min ( renderedRange . end - renderedRange . start , Math . max ( 0 ,
203
+ Math . floor (
204
+ overscan / this . _averager . getAverageItemSize ( ) / ( this . _removalFailures + 1 ) ) ) ) ;
197
205
198
- // The currently rendered range.
199
- const renderedRange = viewport . getRenderedRange ( ) ;
200
206
// The new range we will tell the viewport to render. We first expand it to include the new
201
207
// items we want rendered, we then contract the opposite side to remove items we no longer
202
208
// want rendered.
@@ -215,19 +221,39 @@ export class AutoSizeVirtualScrollStrategy implements VirtualScrollStrategy {
215
221
let contentOffset : number ;
216
222
let contentOffsetTo : 'to-start' | 'to-end' ;
217
223
if ( scrollDelta < 0 ) {
218
- const removedSize = viewport . measureRangeSize ( {
224
+ let removedSize = viewport . measureRangeSize ( {
219
225
start : range . end ,
220
226
end : renderedRange . end ,
221
227
} ) ;
222
- contentOffset =
223
- this . _lastRenderedContentOffset + this . _lastRenderedContentSize - removedSize ;
228
+ // Check that we're not removing too much.
229
+ if ( removedSize <= overscan ) {
230
+ contentOffset =
231
+ this . _lastRenderedContentOffset + this . _lastRenderedContentSize - removedSize ;
232
+ this . _removalFailures = 0 ;
233
+ } else {
234
+ // If the removal is more than the overscan can absorb just undo it and record the fact
235
+ // that the removal failed so we can be less aggressive next time.
236
+ range . end = renderedRange . end ;
237
+ contentOffset = this . _lastRenderedContentOffset + this . _lastRenderedContentSize ;
238
+ this . _removalFailures ++ ;
239
+ }
224
240
contentOffsetTo = 'to-end' ;
225
241
} else {
226
242
const removedSize = viewport . measureRangeSize ( {
227
243
start : renderedRange . start ,
228
244
end : range . start ,
229
245
} ) ;
230
- contentOffset = this . _lastRenderedContentOffset + removedSize ;
246
+ // Check that we're not removing too much.
247
+ if ( removedSize <= overscan ) {
248
+ contentOffset = this . _lastRenderedContentOffset + removedSize ;
249
+ this . _removalFailures = 0 ;
250
+ } else {
251
+ // If the removal is more than the overscan can absorb just undo it and record the fact
252
+ // that the removal failed so we can be less aggressive next time.
253
+ range . start = renderedRange . start ;
254
+ contentOffset = this . _lastRenderedContentOffset ;
255
+ this . _removalFailures ++ ;
256
+ }
231
257
contentOffsetTo = 'to-start' ;
232
258
}
233
259
@@ -267,6 +293,7 @@ export class AutoSizeVirtualScrollStrategy implements VirtualScrollStrategy {
267
293
viewport . setScrollOffset ( scrollOffset ) ;
268
294
}
269
295
this . _lastScrollOffset = scrollOffset ;
296
+ this . _removalFailures = 0 ;
270
297
271
298
const itemSize = this . _averager . getAverageItemSize ( ) ;
272
299
const firstVisibleIndex =
0 commit comments