7
7
*/
8
8
9
9
import { coerceNumberProperty } from '@angular/cdk/coercion' ;
10
- import { ListRange } from '@angular/cdk/collections' ;
11
10
import { Directive , forwardRef , Input , OnChanges } from '@angular/core' ;
12
11
import { Observable , Subject } from 'rxjs' ;
13
12
import { distinctUntilChanged } from 'rxjs/operators' ;
@@ -28,16 +27,21 @@ export class FixedSizeVirtualScrollStrategy implements VirtualScrollStrategy {
28
27
/** The size of the items in the virtually scrolling list. */
29
28
private _itemSize : number ;
30
29
31
- /** The number of buffer items to render beyond the edge of the viewport. */
32
- private _bufferSize : number ;
30
+ /** The minimum amount of buffer rendered beyond the viewport (in pixels). */
31
+ private _minBufferPx : number ;
32
+
33
+ /** The number of buffer items to render beyond the edge of the viewport (in pixels). */
34
+ private _maxBufferPx : number ;
33
35
34
36
/**
35
37
* @param itemSize The size of the items in the virtually scrolling list.
36
- * @param bufferSize The number of buffer items to render beyond the edge of the viewport.
38
+ * @param minBufferPx The minimum amount of buffer (in pixels) before needing to render more
39
+ * @param maxBufferPx The amount of buffer (in pixels) to render when rendering more.
37
40
*/
38
- constructor ( itemSize : number , bufferSize : number ) {
41
+ constructor ( itemSize : number , minBufferPx : number , maxBufferPx : number ) {
39
42
this . _itemSize = itemSize ;
40
- this . _bufferSize = bufferSize ;
43
+ this . _minBufferPx = minBufferPx ;
44
+ this . _maxBufferPx = maxBufferPx ;
41
45
}
42
46
43
47
/**
@@ -59,11 +63,13 @@ export class FixedSizeVirtualScrollStrategy implements VirtualScrollStrategy {
59
63
/**
60
64
* Update the item size and buffer size.
61
65
* @param itemSize The size of the items in the virtually scrolling list.
62
- * @param bufferSize he number of buffer items to render beyond the edge of the viewport.
66
+ * @param minBufferPx The minimum amount of buffer (in pixels) before needing to render more
67
+ * @param maxBufferPx The amount of buffer (in pixels) to render when rendering more.
63
68
*/
64
- updateItemAndBufferSize ( itemSize : number , bufferSize : number ) {
69
+ updateItemAndBufferSize ( itemSize : number , minBufferPx : number , maxBufferPx : number ) {
65
70
this . _itemSize = itemSize ;
66
- this . _bufferSize = bufferSize ;
71
+ this . _minBufferPx = minBufferPx ;
72
+ this . _maxBufferPx = maxBufferPx ;
67
73
this . _updateTotalContentSize ( ) ;
68
74
this . _updateRenderedRange ( ) ;
69
75
}
@@ -112,34 +118,32 @@ export class FixedSizeVirtualScrollStrategy implements VirtualScrollStrategy {
112
118
}
113
119
114
120
const scrollOffset = this . _viewport . measureScrollOffset ( ) ;
115
- const firstVisibleIndex = Math . floor ( scrollOffset / this . _itemSize ) ;
116
- const firstItemRemainder = scrollOffset % this . _itemSize ;
117
- const range = this . _expandRange (
118
- { start : firstVisibleIndex , end : firstVisibleIndex } ,
119
- this . _bufferSize ,
120
- Math . ceil ( ( this . _viewport . getViewportSize ( ) + firstItemRemainder ) / this . _itemSize ) +
121
- this . _bufferSize ) ;
122
- this . _viewport . setRenderedRange ( range ) ;
123
- this . _viewport . setRenderedContentOffset ( this . _itemSize * range . start ) ;
124
-
125
- this . _scrolledIndexChange . next ( firstVisibleIndex ) ;
126
- }
127
-
128
- /**
129
- * Expand the given range by the given amount in either direction.
130
- * @param range The range to expand
131
- * @param expandStart The number of items to expand the start of the range by.
132
- * @param expandEnd The number of items to expand the end of the range by.
133
- * @return The expanded range.
134
- */
135
- private _expandRange ( range : ListRange , expandStart : number , expandEnd : number ) : ListRange {
136
- if ( ! this . _viewport ) {
137
- return { ...range } ;
121
+ const firstVisibleIndex = scrollOffset / this . _itemSize ;
122
+ const range = { ...this . _viewport . getRenderedRange ( ) } ;
123
+
124
+ const startBuffer = scrollOffset - range . start * this . _itemSize ;
125
+ if ( startBuffer < this . _minBufferPx && range . start != 0 ) {
126
+ const expandStart = Math . ceil ( ( this . _maxBufferPx - startBuffer ) / this . _itemSize ) ;
127
+ range . start = Math . max ( 0 , range . start - expandStart ) ;
128
+ range . end = Math . min ( this . _viewport . getDataLength ( ) ,
129
+ Math . ceil ( firstVisibleIndex +
130
+ ( this . _viewport . getViewportSize ( ) + this . _minBufferPx ) / this . _itemSize ) ) ;
131
+ } else {
132
+ const endBuffer =
133
+ range . end * this . _itemSize - ( scrollOffset + this . _viewport . getViewportSize ( ) ) ;
134
+ if ( endBuffer < this . _minBufferPx && range . end != this . _viewport . getDataLength ( ) ) {
135
+ const expandEnd = Math . ceil ( ( this . _maxBufferPx - endBuffer ) / this . _itemSize ) ;
136
+ if ( expandEnd > 0 ) {
137
+ range . end = Math . min ( this . _viewport . getDataLength ( ) , range . end + expandEnd ) ;
138
+ range . start = Math . max ( 0 ,
139
+ Math . floor ( firstVisibleIndex - this . _minBufferPx / this . _itemSize ) ) ;
140
+ }
141
+ }
138
142
}
139
143
140
- const start = Math . max ( 0 , range . start - expandStart ) ;
141
- const end = Math . min ( this . _viewport . getDataLength ( ) , range . end + expandEnd ) ;
142
- return { start , end } ;
144
+ this . _viewport . setRenderedRange ( range ) ;
145
+ this . _viewport . setRenderedContentOffset ( this . _itemSize * range . start ) ;
146
+ this . _scrolledIndexChange . next ( Math . floor ( firstVisibleIndex ) ) ;
143
147
}
144
148
}
145
149
@@ -172,18 +176,27 @@ export class CdkFixedSizeVirtualScroll implements OnChanges {
172
176
_itemSize = 20 ;
173
177
174
178
/**
175
- * The number of extra elements to render on either side of the scrolling viewport.
176
- * Defaults to 5 elements.
179
+ * The minimum amount of buffer rendered beyond the viewport (in pixels).
180
+ * If the amount of buffer dips below this number, more items will be rendered. Defaults to 100px.
181
+ */
182
+ @Input ( )
183
+ get minBufferPx ( ) : number { return this . _minBufferPx ; }
184
+ set minBufferPx ( value : number ) { this . _minBufferPx = coerceNumberProperty ( value ) ; }
185
+ _minBufferPx = 100 ;
186
+
187
+ /**
188
+ * The number of pixels worth of buffer to render for when rendering new items. Defaults to 200px.
177
189
*/
178
190
@Input ( )
179
- get bufferSize ( ) : number { return this . _bufferSize ; }
180
- set bufferSize ( value : number ) { this . _bufferSize = coerceNumberProperty ( value ) ; }
181
- _bufferSize = 5 ;
191
+ get maxBufferPx ( ) : number { return this . _maxBufferPx ; }
192
+ set maxBufferPx ( value : number ) { this . _maxBufferPx = coerceNumberProperty ( value ) ; }
193
+ _maxBufferPx = 200 ;
182
194
183
195
/** The scroll strategy used by this directive. */
184
- _scrollStrategy = new FixedSizeVirtualScrollStrategy ( this . itemSize , this . bufferSize ) ;
196
+ _scrollStrategy =
197
+ new FixedSizeVirtualScrollStrategy ( this . itemSize , this . minBufferPx , this . maxBufferPx ) ;
185
198
186
199
ngOnChanges ( ) {
187
- this . _scrollStrategy . updateItemAndBufferSize ( this . itemSize , this . bufferSize ) ;
200
+ this . _scrollStrategy . updateItemAndBufferSize ( this . itemSize , this . minBufferPx , this . maxBufferPx ) ;
188
201
}
189
202
}
0 commit comments