Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import { CdkVirtualScrollViewport, VirtualScrollStrategy } from '@angular/cdk/scrolling';
- import { Subject } from 'rxjs';
- import { distinctUntilChanged } from 'rxjs/operators';
- const BUFFER = 500;
- export abstract class CustomVirtualScrollStrategy<T> implements VirtualScrollStrategy {
- private viewport: CdkVirtualScrollViewport;
- private readonly index$ = new Subject<number>();
- private readonly offsetByIndex: { [key: number]: number } = {};
- private readonly indexByOffset: { [key: number]: number } = {};
- scrolledIndexChange = this.index$.pipe(distinctUntilChanged());
- protected constructor(protected readonly items: T[]) {
- for (let index = 0, offset = 0; index < this.items.length; index++) {
- const element = this.items[index];
- const elementHeight = this.calculateItemHeight(element);
- offset += elementHeight;
- this.offsetByIndex[index] = offset;
- this.indexByOffset[offset] = index;
- }
- }
- attach(viewport: CdkVirtualScrollViewport): void {
- this.viewport = viewport;
- this.viewport.setTotalContentSize(this.totalContentSize);
- this.updateRenderedRange(this.viewport);
- }
- detach(): void {
- this.index$.complete();
- this.viewport = null;
- }
- onContentScrolled(): void {
- if (this.viewport) {
- this.updateRenderedRange(this.viewport);
- }
- }
- onDataLengthChanged(): void {}
- onContentRendered(): void {}
- onRenderedOffsetChanged(): void {}
- scrollToIndex(index: number, behavior: ScrollBehavior): void {
- if (this.viewport) {
- this.viewport.scrollToOffset(this.getOffsetForIndex(index), behavior);
- }
- }
- private get totalContentSize(): number {
- return this.offsetByIndex[this.items.length - 2];
- }
- protected abstract calculateItemHeight(item: T): number;
- private getOffsetForIndex(index: number): number {
- if (!index) {
- return 0;
- }
- return this.offsetByIndex[index - 1];
- }
- private getIndexForOffset(offset: number): number {
- const offsets = Object.keys(this.indexByOffset);
- for (let index = 1; index < offsets.length; index++) {
- const a = +offsets[index - 1];
- const b = +offsets[index];
- if (offset >= a && offset <= b) {
- return index - 2;
- }
- }
- return 0;
- }
- private updateRenderedRange(viewport: CdkVirtualScrollViewport) {
- const offset = viewport.measureScrollOffset();
- const { start, end } = viewport.getRenderedRange();
- const viewportSize = viewport.getViewportSize();
- const dataLength = viewport.getDataLength();
- const newRange = { start, end };
- const firstVisibleIndex = this.getIndexForOffset(offset);
- const startBuffer = offset - this.getOffsetForIndex(start);
- if (startBuffer < BUFFER && start !== 0) {
- newRange.start = Math.max(0, this.getIndexForOffset(offset - BUFFER * 2));
- newRange.end = Math.min(dataLength, this.getIndexForOffset(offset + viewportSize + BUFFER));
- } else {
- const endBuffer = this.getOffsetForIndex(end) - offset - viewportSize;
- if (endBuffer < BUFFER && end !== dataLength) {
- newRange.start = Math.max(0, this.getIndexForOffset(offset - BUFFER));
- newRange.end = Math.min(dataLength, this.getIndexForOffset(offset + viewportSize + BUFFER * 2));
- }
- }
- viewport.setRenderedRange(newRange);
- viewport.setRenderedContentOffset(this.getOffsetForIndex(newRange.start));
- this.index$.next(firstVisibleIndex);
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement