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,16 @@ 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 ) {
70
+ if ( maxBufferPx < minBufferPx ) {
71
+ throw Error ( 'CDK virtual scroll: maxBufferPx must be greater than or equal to minBufferPx' ) ;
72
+ }
65
73
this . _itemSize = itemSize ;
66
- this . _bufferSize = bufferSize ;
74
+ this . _minBufferPx = minBufferPx ;
75
+ this . _maxBufferPx = maxBufferPx ;
67
76
this . _updateTotalContentSize ( ) ;
68
77
this . _updateRenderedRange ( ) ;
69
78
}
@@ -112,34 +121,33 @@ export class FixedSizeVirtualScrollStrategy implements VirtualScrollStrategy {
112
121
}
113
122
114
123
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 } ;
124
+ const firstVisibleIndex = scrollOffset / this . _itemSize ;
125
+ const renderedRange = this . _viewport . getRenderedRange ( ) ;
126
+ const newRange = { start : renderedRange . start , end : renderedRange . end } ;
127
+ const viewportSize = this . _viewport . getViewportSize ( ) ;
128
+ const dataLength = this . _viewport . getDataLength ( ) ;
129
+
130
+ const startBuffer = scrollOffset - newRange . start * this . _itemSize ;
131
+ if ( startBuffer < this . _minBufferPx && newRange . start != 0 ) {
132
+ const expandStart = Math . ceil ( ( this . _maxBufferPx - startBuffer ) / this . _itemSize ) ;
133
+ newRange . start = Math . max ( 0 , newRange . start - expandStart ) ;
134
+ newRange . end = Math . min ( dataLength ,
135
+ Math . ceil ( firstVisibleIndex + ( viewportSize + this . _minBufferPx ) / this . _itemSize ) ) ;
136
+ } else {
137
+ const endBuffer = newRange . end * this . _itemSize - ( scrollOffset + viewportSize ) ;
138
+ if ( endBuffer < this . _minBufferPx && newRange . end != dataLength ) {
139
+ const expandEnd = Math . ceil ( ( this . _maxBufferPx - endBuffer ) / this . _itemSize ) ;
140
+ if ( expandEnd > 0 ) {
141
+ newRange . end = Math . min ( dataLength , newRange . end + expandEnd ) ;
142
+ newRange . start = Math . max ( 0 ,
143
+ Math . floor ( firstVisibleIndex - this . _minBufferPx / this . _itemSize ) ) ;
144
+ }
145
+ }
138
146
}
139
147
140
- const start = Math . max ( 0 , range . start - expandStart ) ;
141
- const end = Math . min ( this . _viewport . getDataLength ( ) , range . end + expandEnd ) ;
142
- return { start , end } ;
148
+ this . _viewport . setRenderedRange ( newRange ) ;
149
+ this . _viewport . setRenderedContentOffset ( this . _itemSize * newRange . start ) ;
150
+ this . _scrolledIndexChange . next ( Math . floor ( firstVisibleIndex ) ) ;
143
151
}
144
152
}
145
153
@@ -172,18 +180,27 @@ export class CdkFixedSizeVirtualScroll implements OnChanges {
172
180
_itemSize = 20 ;
173
181
174
182
/**
175
- * The number of extra elements to render on either side of the scrolling viewport.
176
- * Defaults to 5 elements.
183
+ * The minimum amount of buffer rendered beyond the viewport (in pixels).
184
+ * If the amount of buffer dips below this number, more items will be rendered. Defaults to 100px.
185
+ */
186
+ @Input ( )
187
+ get minBufferPx ( ) : number { return this . _minBufferPx ; }
188
+ set minBufferPx ( value : number ) { this . _minBufferPx = coerceNumberProperty ( value ) ; }
189
+ _minBufferPx = 100 ;
190
+
191
+ /**
192
+ * The number of pixels worth of buffer to render for when rendering new items. Defaults to 200px.
177
193
*/
178
194
@Input ( )
179
- get bufferSize ( ) : number { return this . _bufferSize ; }
180
- set bufferSize ( value : number ) { this . _bufferSize = coerceNumberProperty ( value ) ; }
181
- _bufferSize = 5 ;
195
+ get maxBufferPx ( ) : number { return this . _maxBufferPx ; }
196
+ set maxBufferPx ( value : number ) { this . _maxBufferPx = coerceNumberProperty ( value ) ; }
197
+ _maxBufferPx = 200 ;
182
198
183
199
/** The scroll strategy used by this directive. */
184
- _scrollStrategy = new FixedSizeVirtualScrollStrategy ( this . itemSize , this . bufferSize ) ;
200
+ _scrollStrategy =
201
+ new FixedSizeVirtualScrollStrategy ( this . itemSize , this . minBufferPx , this . maxBufferPx ) ;
185
202
186
203
ngOnChanges ( ) {
187
- this . _scrollStrategy . updateItemAndBufferSize ( this . itemSize , this . bufferSize ) ;
204
+ this . _scrollStrategy . updateItemAndBufferSize ( this . itemSize , this . minBufferPx , this . maxBufferPx ) ;
188
205
}
189
206
}
0 commit comments