شريط التمرير المخصص في الزاوي

بعد انضمام Edge إلى الرتب الباسلة لمتصفحات Chromium ، فإن تخصيص أشرطة التمرير عبر CSS غير موجود إلا في Firefox. هذا أمر رائع ، ولكن بصرف النظر عن Firefox ، فإن حل CSS له الكثير من القيود. ترى ما السحر الأسود لديك لاستخدام لتتلاشى. للتحكم الكامل في المظهر ، ما زلت بحاجة إلى اللجوء إلى JavaScript. دعونا نرى كيفية القيام بذلك من خلال المكون الزاوي بطريقة جيدة.



سحر 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