Skip to content

Commit 733e86e

Browse files
authored
virtual-scroll: add onContentRendered hook to VirtualScrollStrategy (#10290)
* virtual-scroll: add `onContentRendered` hook to `VirtualScrollStrategy` * address comemnts
1 parent ba64d89 commit 733e86e

File tree

4 files changed

+49
-9
lines changed

4 files changed

+49
-9
lines changed

src/cdk-experimental/scrolling/auto-size-virtual-scroll.ts

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,10 @@ export class ItemSizeAverager {
3939
* @param size The measured size of the given range in pixels.
4040
*/
4141
addSample(range: ListRange, size: number) {
42-
const weight = range.end - range.start;
43-
const newTotalWeight = this._totalWeight + weight;
42+
const newTotalWeight = this._totalWeight + range.end - range.start;
4443
if (newTotalWeight) {
4544
const newAverageItemSize =
46-
(size * weight + this._averageItemSize * this._totalWeight) / newTotalWeight;
45+
(size + this._averageItemSize * this._totalWeight) / newTotalWeight;
4746
if (newAverageItemSize) {
4847
this._averageItemSize = newAverageItemSize;
4948
this._totalWeight = newTotalWeight;
@@ -87,7 +86,6 @@ export class AutoSizeVirtualScrollStrategy implements VirtualScrollStrategy {
8786
*/
8887
attach(viewport: CdkVirtualScrollViewport) {
8988
this._viewport = viewport;
90-
this._updateTotalContentSize();
9189
this._renderContentForOffset(this._viewport.measureScrollOffset());
9290
}
9391

@@ -96,21 +94,27 @@ export class AutoSizeVirtualScrollStrategy implements VirtualScrollStrategy {
9694
this._viewport = null;
9795
}
9896

99-
/** Called when the viewport is scrolled. */
97+
/** Implemented as part of VirtualScrollStrategy. */
10098
onContentScrolled() {
10199
if (this._viewport) {
102100
this._renderContentForOffset(this._viewport.measureScrollOffset());
103101
}
104102
}
105103

106-
/** Called when the length of the data changes. */
104+
/** Implemented as part of VirtualScrollStrategy. */
107105
onDataLengthChanged() {
108106
if (this._viewport) {
109-
this._updateTotalContentSize();
110107
this._renderContentForOffset(this._viewport.measureScrollOffset());
111108
}
112109
}
113110

111+
/** Implemented as part of VirtualScrollStrategy. */
112+
onContentRendered() {
113+
if (this._viewport) {
114+
this._checkRenderedContentSize();
115+
}
116+
}
117+
114118
/**
115119
* Update the buffer parameters.
116120
* @param minBufferPx The minimum amount of buffer rendered beyond the viewport (in pixels).
@@ -122,6 +126,17 @@ export class AutoSizeVirtualScrollStrategy implements VirtualScrollStrategy {
122126
this._addBufferPx = addBufferPx;
123127
}
124128

129+
/**
130+
* Checks the size of the currently rendered content and uses it to update the estimated item size
131+
* and estimated total content size.
132+
*/
133+
private _checkRenderedContentSize() {
134+
const viewport = this._viewport!;
135+
const renderedContentSize = viewport.measureRenderedContentSize();
136+
this._averager.addSample(viewport.getRenderedRange(), renderedContentSize);
137+
this._updateTotalContentSize(renderedContentSize);
138+
}
139+
125140
/**
126141
* Render the content that we estimate should be shown for the given scroll offset.
127142
* Note: must not be called if `this._viewport` is null
@@ -179,9 +194,13 @@ export class AutoSizeVirtualScrollStrategy implements VirtualScrollStrategy {
179194
}
180195

181196
/** Update the viewport's total content size. */
182-
private _updateTotalContentSize() {
197+
private _updateTotalContentSize(renderedContentSize: number) {
183198
const viewport = this._viewport!;
184-
viewport.setTotalContentSize(viewport.getDataLength() * this._averager.getAverageItemSize());
199+
const renderedRange = viewport.getRenderedRange();
200+
const totalSize = renderedContentSize +
201+
(viewport.getDataLength() - (renderedRange.end - renderedRange.start)) *
202+
this._averager.getAverageItemSize();
203+
viewport.setTotalContentSize(totalSize);
185204
}
186205
}
187206

src/cdk-experimental/scrolling/fixed-size-virtual-scroll.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ export class FixedSizeVirtualScrollStrategy implements VirtualScrollStrategy {
7070
this._updateRenderedRange();
7171
}
7272

73+
/** Called when the range of items rendered in the DOM has changed. */
74+
onContentRendered() { /* no-op */ }
75+
7376
/** Update the viewport's total content size. */
7477
private _updateTotalContentSize() {
7578
if (!this._viewport) {

src/cdk-experimental/scrolling/virtual-scroll-strategy.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,7 @@ export interface VirtualScrollStrategy {
3131

3232
/** Called when the length of the data changes. */
3333
onDataLengthChanged();
34+
35+
/** Called when the range of items rendered in the DOM has changed. */
36+
onContentRendered();
3437
}

src/cdk-experimental/scrolling/virtual-scroll-viewport.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
import {Observable} from 'rxjs/Observable';
2424
import {fromEvent} from 'rxjs/observable/fromEvent';
2525
import {sampleTime} from 'rxjs/operators/sampleTime';
26+
import {take} from 'rxjs/operators/take';
2627
import {takeUntil} from 'rxjs/operators/takeUntil';
2728
import {animationFrame} from 'rxjs/scheduler/animationFrame';
2829
import {Subject} from 'rxjs/Subject';
@@ -99,6 +100,11 @@ export class CdkVirtualScrollViewport implements OnInit, OnDestroy {
99100
return this._viewportSize;
100101
}
101102

103+
/** Get the current rendered range of items. */
104+
getRenderedRange(): ListRange {
105+
return this._renderedRange;
106+
}
107+
102108
// TODO(mmalebra): Consider calling `detectChanges()` directly rather than the methods below.
103109

104110
/**
@@ -122,6 +128,9 @@ export class CdkVirtualScrollViewport implements OnInit, OnDestroy {
122128
this._ngZone.run(() => {
123129
this._renderedRangeSubject.next(this._renderedRange = range);
124130
this._changeDetectorRef.markForCheck();
131+
this._ngZone.runOutsideAngular(() => this._ngZone.onStable.pipe(take(1)).subscribe(() => {
132+
this._scrollStrategy.onContentRendered();
133+
}));
125134
});
126135
}
127136
}
@@ -169,6 +178,12 @@ export class CdkVirtualScrollViewport implements OnInit, OnDestroy {
169178
this.elementRef.nativeElement.scrollLeft : this.elementRef.nativeElement.scrollTop;
170179
}
171180

181+
/** Measure the combined size of all of the rendered items. */
182+
measureRenderedContentSize() {
183+
const contentEl = this._contentWrapper.nativeElement;
184+
return this.orientation === 'horizontal' ? contentEl.offsetWidth : contentEl.offsetHeight;
185+
}
186+
172187
ngOnInit() {
173188
Promise.resolve().then(() => {
174189
this._viewportSize = this.orientation === 'horizontal' ?

0 commit comments

Comments
 (0)