Skip to content

Commit b796b6e

Browse files
authored
fix(cdk/virtual-scroll): fix subpixel rounding errors on hdpi… (#16269)
* fix(cdk/virtual-scroll): fix subpixel rounding errors on hdpi screens Using `transform` appears to cause rounding errors that can accumulate and become significant after scrolling through a large number of items. Switching to `width` / `height` corrects this. * don't recalulate height/width during every change detection
1 parent 286038c commit b796b6e

File tree

3 files changed

+30
-11
lines changed

3 files changed

+30
-11
lines changed

src/cdk/scrolling/virtual-scroll-viewport.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,5 @@
99
Spacer used to force the scrolling container to the correct size for the *total* number of items
1010
so that the scrollbar captures the size of the entire data set.
1111
-->
12-
<div class="cdk-virtual-scroll-spacer" [style.transform]="_totalContentSizeTransform"></div>
12+
<div class="cdk-virtual-scroll-spacer"
13+
[style.width]="_totalContentWidth" [style.height]="_totalContentHeight"></div>

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

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,17 @@ export class CdkVirtualScrollViewport extends CdkScrollable implements OnInit, O
7171
private _renderedRangeSubject = new Subject<ListRange>();
7272

7373
/** The direction the viewport scrolls. */
74-
@Input() orientation: 'horizontal' | 'vertical' = 'vertical';
74+
@Input()
75+
get orientation() {
76+
return this._orientation;
77+
}
78+
set orientation(orientation: 'horizontal' | 'vertical') {
79+
if (this._orientation !== orientation) {
80+
this._orientation = orientation;
81+
this._calculateSpacerSize();
82+
}
83+
}
84+
private _orientation: 'horizontal' | 'vertical' = 'vertical';
7585

7686
// Note: we don't use the typical EventEmitter here because we need to subscribe to the scroll
7787
// strategy lazily (i.e. only if the user is actually listening to the events). We do this because
@@ -89,17 +99,17 @@ export class CdkVirtualScrollViewport extends CdkScrollable implements OnInit, O
8999
/** A stream that emits whenever the rendered range changes. */
90100
renderedRangeStream: Observable<ListRange> = this._renderedRangeSubject.asObservable();
91101

92-
/**
93-
* The transform used to scale the spacer to the same size as all content, including content that
94-
* is not currently rendered.
95-
*/
96-
_totalContentSizeTransform = '';
97-
98102
/**
99103
* The total size of all content (in pixels), including content that is not currently rendered.
100104
*/
101105
private _totalContentSize = 0;
102106

107+
/** A string representing the `style.width` property value to be used for the spacer element. */
108+
_totalContentWidth = '';
109+
110+
/** A string representing the `style.height` property value to be used for the spacer element. */
111+
_totalContentHeight = '';
112+
103113
/**
104114
* The CSS transform applied to the rendered subset of items so that they appear within the bounds
105115
* of the visible viewport.
@@ -238,8 +248,7 @@ export class CdkVirtualScrollViewport extends CdkScrollable implements OnInit, O
238248
setTotalContentSize(size: number) {
239249
if (this._totalContentSize !== size) {
240250
this._totalContentSize = size;
241-
const axis = this.orientation == 'horizontal' ? 'X' : 'Y';
242-
this._totalContentSizeTransform = `scale${axis}(${this._totalContentSize})`;
251+
this._calculateSpacerSize();
243252
this._markChangeDetectionNeeded();
244253
}
245254
}
@@ -398,4 +407,12 @@ export class CdkVirtualScrollViewport extends CdkScrollable implements OnInit, O
398407
fn();
399408
}
400409
}
410+
411+
/** Calculates the `style.width` and `style.height` for the spacer element. */
412+
private _calculateSpacerSize() {
413+
this._totalContentHeight =
414+
this.orientation === 'horizontal' ? '' : `${this._totalContentSize}px`;
415+
this._totalContentWidth =
416+
this.orientation === 'horizontal' ? `${this._totalContentSize}px` : '';
417+
}
401418
}

tools/public_api_guard/cdk/scrolling.d.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,8 @@ export declare type CdkVirtualForOfContext<T> = {
9090

9191
export declare class CdkVirtualScrollViewport extends CdkScrollable implements OnInit, OnDestroy {
9292
_contentWrapper: ElementRef<HTMLElement>;
93-
_totalContentSizeTransform: string;
93+
_totalContentHeight: string;
94+
_totalContentWidth: string;
9495
elementRef: ElementRef<HTMLElement>;
9596
orientation: 'horizontal' | 'vertical';
9697
renderedRangeStream: Observable<ListRange>;

0 commit comments

Comments
 (0)