Benutzerdefinierte Bildlaufleiste in Angular

Nachdem Edge zu den tapferen Reihen der Chromium-Browser gehört hat, fehlt die Anpassung der Bildlaufleisten über CSS nur in Firefox. Es ist großartig, aber neben Firefox hat die CSS-Lösung eine Menge Einschränkungen. Sehen Sie, welche schwarze Magie Sie zum Ausblenden verwenden müssen. Um die volle Kontrolle über das Erscheinungsbild zu erhalten, müssen Sie immer noch auf JavaScript zurückgreifen. Mal sehen, wie man es auf gute Weise durch die Angular-Komponente macht.



CSS-Magie


, , . -, . . scrollbar-width, Firefox. Edge IE -ms-overflow-style. IE , position: sticky. Chrome Safari ::-webkit-scrollbar.


-, , . , - position: fixed . : .


position: sticky display: flex . :


<div class="bars">...</div>
<div class="content"><ng-content></ng-content></div>

. , - z-index: 1000, 1001, 9999. , position: relative, z-index: 0. - .


z-index: 1, , 100%. :



, . . margin-right: -100%, «» .


, , float, 100%, (max-height, flex: 1 ).

Angular


Angular, , . . . , . :


<div class="bars">
    <div *ngIf="hasVerticalBar" class="bar">
        <div
            class="thumb"
            [class.thumb_active]="verticalThumbActive"
            [style.height.%]="verticalView"
            [style.top.%]="verticalThumb"
        ></div>
    </div>
</div>

:


  //     
  get verticalScrolled(): number {
    const {
      scrollTop,
      scrollHeight,
      clientHeight
    } = this.elementRef.nativeElement;

    return scrollTop / (scrollHeight - clientHeight);
  }

  //    
  get verticalSize(): number {
    const { clientHeight, scrollHeight } = this.elementRef.nativeElement;

    return Math.ceil(clientHeight / scrollHeight * 100);
  }

  //     
  get verticalPosition(): number {
    return this.verticalScrolled * (100 - this.verticalSize);
  }

  //   ,  
  get hasVerticalBar(): boolean {
    return this.verticalSize < 100;
  }

, , — . . , .


mousedown , mousemove mouseup .

:


        <div
            #vertical
            class="thumb"
            [class.thumb_active]="verticalThumbActive"
            [style.height.%]="verticalSize"
            [style.top.%]="verticalPosition"
            (mousedown)="onVerticalStart($event)"
            (document:mousemove)="onVerticalMove($event, vertical)"
        ></div>

mouseup:


  @HostListener('document:mouseup)
  onDragEnd() {
    this.verticalThumbActive = false;
  }

  onVerticalStart(event: MouseEvent) {
    event.preventDefault();

    const { target, clientY } = event;
    const { top, height } = target.getBoundingClientRect();

    this.verticalThumbDragOffset = (clientY - top) / height;
    this.verticalThumbActive = true;
  }

  onVerticalMove(
    { clientY }: MouseEvent,
    { offsetHeight }: HTMLElement
  ) {
    if (!this.verticalThumbActive) {
      return;
    }

    const { nativeElement } = this.elementRef;
    const { top, height } = nativeElement.getBoundingClientRect();
    const maxScrollTop = nativeElement.scrollHeight - height;
    const scrolled =
      (clientY - top - offsetHeight * this.verticalThumbDragOffset) /
      (height - offsetHeight);

    nativeElement.scrollTop = maxScrollTop * scrolled;
  }

.


Angular


, . mousemove . . , Angular , . @tinkoff/ng-event-plugins, . .silent @shouldCall :


(document:mousemove.silent)="onVerticalMove($event, vertical)"

  @shouldCall(isActive)
  @HostListener('init.end', ['$event'])
  @HostListener('document:mouseup.silent')
  onDragEnd() {
    this.verticalThumbActive = false;
  }

  @shouldCall(isActive)
  @HostListener('init.move', ['$event'])
  onVerticalMove(
    { clientY }: MouseEvent,
    { offsetHeight }: HTMLElement
  ) {
    const { nativeElement } = this.elementRef;
    const { top, height } = nativeElement.getBoundingClientRect();
    const maxScrollTop = nativeElement.scrollHeight - height;
    const scrolled =
      (clientY - top - offsetHeight * this.verticalThumbDragOffset) /
      (height - offsetHeight);

    nativeElement.scrollTop = maxScrollTop * scrolled;
  }

: Angular 10 markDirty(this) @shouldCall @HostListener(‘init.xxx’, [‘$event’]), , — .

, . . , . ResizeObserver @ng-web-apis/resize-observer, . Stackblitz.


Edit:


, , . . Output fromEvent, :


  @Output()
  dragged = fromEvent(
    this.elementRef.nativeElement,
    'mousedown'
  ).pipe(
    switchMap(event => {
      event.preventDefault();

      const clientRect = event.target.getBoundingClientRect();
      const offsetVertical = getOffsetVertical(event, clientRect);
      const offsetHorizontal = getOffsetHorizontal(event, clientRect);

      return fromEvent(this.documentRef, 'mousemove').pipe(
        map(event => this.getScrolled(event, offsetVertical, offsetHorizontal)),
        takeUntil(fromEvent(this.documentRef, 'mouseup'))
      );
    })
  );

. , .


All Articles