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
This commit is contained in:
mmalerba
2019-08-12 10:19:28 -07:00
committed by GitHub
parent 286038c4a7
commit b796b6eef0
3 changed files with 30 additions and 11 deletions
@@ -9,4 +9,5 @@
Spacer used to force the scrolling container to the correct size for the *total* number of items
so that the scrollbar captures the size of the entire data set.
-->
<div class="cdk-virtual-scroll-spacer" [style.transform]="_totalContentSizeTransform"></div>
<div class="cdk-virtual-scroll-spacer"
[style.width]="_totalContentWidth" [style.height]="_totalContentHeight"></div>
+26 -9
View File
@@ -71,7 +71,17 @@ export class CdkVirtualScrollViewport extends CdkScrollable implements OnInit, O
private _renderedRangeSubject = new Subject<ListRange>();
/** The direction the viewport scrolls. */
@Input() orientation: 'horizontal' | 'vertical' = 'vertical';
@Input()
get orientation() {
return this._orientation;
}
set orientation(orientation: 'horizontal' | 'vertical') {
if (this._orientation !== orientation) {
this._orientation = orientation;
this._calculateSpacerSize();
}
}
private _orientation: 'horizontal' | 'vertical' = 'vertical';
// Note: we don't use the typical EventEmitter here because we need to subscribe to the scroll
// 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
/** A stream that emits whenever the rendered range changes. */
renderedRangeStream: Observable<ListRange> = this._renderedRangeSubject.asObservable();
/**
* The transform used to scale the spacer to the same size as all content, including content that
* is not currently rendered.
*/
_totalContentSizeTransform = '';
/**
* The total size of all content (in pixels), including content that is not currently rendered.
*/
private _totalContentSize = 0;
/** A string representing the `style.width` property value to be used for the spacer element. */
_totalContentWidth = '';
/** A string representing the `style.height` property value to be used for the spacer element. */
_totalContentHeight = '';
/**
* The CSS transform applied to the rendered subset of items so that they appear within the bounds
* of the visible viewport.
@@ -238,8 +248,7 @@ export class CdkVirtualScrollViewport extends CdkScrollable implements OnInit, O
setTotalContentSize(size: number) {
if (this._totalContentSize !== size) {
this._totalContentSize = size;
const axis = this.orientation == 'horizontal' ? 'X' : 'Y';
this._totalContentSizeTransform = `scale${axis}(${this._totalContentSize})`;
this._calculateSpacerSize();
this._markChangeDetectionNeeded();
}
}
@@ -398,4 +407,12 @@ export class CdkVirtualScrollViewport extends CdkScrollable implements OnInit, O
fn();
}
}
/** Calculates the `style.width` and `style.height` for the spacer element. */
private _calculateSpacerSize() {
this._totalContentHeight =
this.orientation === 'horizontal' ? '' : `${this._totalContentSize}px`;
this._totalContentWidth =
this.orientation === 'horizontal' ? `${this._totalContentSize}px` : '';
}
}
+2 -1
View File
@@ -90,7 +90,8 @@ export declare type CdkVirtualForOfContext<T> = {
export declare class CdkVirtualScrollViewport extends CdkScrollable implements OnInit, OnDestroy {
_contentWrapper: ElementRef<HTMLElement>;
_totalContentSizeTransform: string;
_totalContentHeight: string;
_totalContentWidth: string;
elementRef: ElementRef<HTMLElement>;
orientation: 'horizontal' | 'vertical';
renderedRangeStream: Observable<ListRange>;