Barra de desplazamiento personalizada en angular

Después de que Edge se unió a las valientes filas de los navegadores Chromium, la personalización de las barras de desplazamiento a través de CSS solo está ausente en Firefox. Esto es genial, pero aparte de Firefox, la solución CSS tiene muchas limitaciones. Mira qué magia negra tienes que usar para desvanecerte. Para obtener un control total sobre la apariencia, aún debe recurrir a JavaScript. Veamos cómo hacerlo a través del componente angular de una buena manera.



Magia de CSS


, , . -, . . 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