Advertisement
Guest User

Untitled

a guest
Jan 21st, 2020
168
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. import { CdkVirtualScrollViewport, VirtualScrollStrategy } from '@angular/cdk/scrolling';
  2. import { Subject } from 'rxjs';
  3. import { distinctUntilChanged } from 'rxjs/operators';
  4.  
  5. const BUFFER = 500;
  6.  
  7. export abstract class CustomVirtualScrollStrategy<T> implements VirtualScrollStrategy {
  8.   private viewport: CdkVirtualScrollViewport;
  9.   private readonly index$ = new Subject<number>();
  10.   private readonly offsetByIndex: { [key: number]: number } = {};
  11.   private readonly indexByOffset: { [key: number]: number } = {};
  12.  
  13.   scrolledIndexChange = this.index$.pipe(distinctUntilChanged());
  14.  
  15.   protected constructor(protected readonly items: T[]) {
  16.     for (let index = 0, offset = 0; index < this.items.length; index++) {
  17.       const element = this.items[index];
  18.       const elementHeight = this.calculateItemHeight(element);
  19.  
  20.       offset += elementHeight;
  21.      
  22.       this.offsetByIndex[index] = offset;
  23.       this.indexByOffset[offset] = index;
  24.     }
  25.   }
  26.  
  27.   attach(viewport: CdkVirtualScrollViewport): void {
  28.     this.viewport = viewport;
  29.     this.viewport.setTotalContentSize(this.totalContentSize);
  30.     this.updateRenderedRange(this.viewport);
  31.   }
  32.  
  33.   detach(): void {
  34.     this.index$.complete();
  35.     this.viewport = null;
  36.   }
  37.  
  38.   onContentScrolled(): void {
  39.     if (this.viewport) {
  40.       this.updateRenderedRange(this.viewport);
  41.     }
  42.   }
  43.  
  44.   onDataLengthChanged(): void {}
  45.   onContentRendered(): void {}
  46.   onRenderedOffsetChanged(): void {}
  47.  
  48.   scrollToIndex(index: number, behavior: ScrollBehavior): void {
  49.     if (this.viewport) {
  50.       this.viewport.scrollToOffset(this.getOffsetForIndex(index), behavior);
  51.     }
  52.   }
  53.  
  54.   private get totalContentSize(): number {
  55.     return this.offsetByIndex[this.items.length - 2];
  56.   }
  57.  
  58.   protected abstract calculateItemHeight(item: T): number;
  59.  
  60.   private getOffsetForIndex(index: number): number {
  61.     if (!index) {
  62.       return 0;
  63.     }
  64.  
  65.     return this.offsetByIndex[index - 1];
  66.   }
  67.  
  68.   private getIndexForOffset(offset: number): number {
  69.     const offsets = Object.keys(this.indexByOffset);
  70.  
  71.     for (let index = 1; index < offsets.length; index++) {
  72.       const a = +offsets[index - 1];
  73.       const b = +offsets[index];
  74.  
  75.       if (offset >= a && offset <= b) {
  76.         return index - 2;
  77.       }
  78.     }
  79.  
  80.     return 0;
  81.   }
  82.  
  83.   private updateRenderedRange(viewport: CdkVirtualScrollViewport) {
  84.     const offset = viewport.measureScrollOffset();
  85.     const { start, end } = viewport.getRenderedRange();
  86.     const viewportSize = viewport.getViewportSize();
  87.     const dataLength = viewport.getDataLength();
  88.     const newRange = { start, end };
  89.     const firstVisibleIndex = this.getIndexForOffset(offset);
  90.     const startBuffer = offset - this.getOffsetForIndex(start);
  91.  
  92.     if (startBuffer < BUFFER && start !== 0) {
  93.       newRange.start = Math.max(0, this.getIndexForOffset(offset - BUFFER * 2));
  94.       newRange.end = Math.min(dataLength, this.getIndexForOffset(offset + viewportSize + BUFFER));
  95.     } else {
  96.       const endBuffer = this.getOffsetForIndex(end) - offset - viewportSize;
  97.  
  98.       if (endBuffer < BUFFER && end !== dataLength) {
  99.         newRange.start = Math.max(0, this.getIndexForOffset(offset - BUFFER));
  100.         newRange.end = Math.min(dataLength, this.getIndexForOffset(offset + viewportSize + BUFFER * 2));
  101.       }
  102.     }
  103.  
  104.     viewport.setRenderedRange(newRange);
  105.     viewport.setRenderedContentOffset(this.getOffsetForIndex(newRange.start));
  106.     this.index$.next(firstVisibleIndex);
  107.   }
  108. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement