使材料选项卡可滚动

Mal*_*wan 4 javascript angular-material angular

我在我的应用程序中使用材料选项卡(mat-tabs内部mat-tab-group)有多于可以显示的标签时,两个导航按钮显示为显示其他选项卡:

在此处输入图片说明

我的要求是让用户能够在选项卡栏上滚动,以便显示其他选项卡。

我试图做一些 css 更改,但无法解决。如果可以提供任何解决方案或建议,我们将不胜感激。

Kur*_*gor 6

[长文本解决方案] 我的目标是默认情况下使 mat-tabs 可滚动,无需控件,但能够在单击部分可见的 mat-tab 以将其移动到可见的中心视口时自动滚动。

1)此行为已在此示例“开箱即用”中部分实现 - https://stackblitz.com/edit/angular-mat-tabs-scrollalble-initial-behavior。问题是你不能正常向后滚动 - 有一些错误的行为将标签推到左边。所以它不能像我想要的那样工作。

2)下一个变体是滚动事件 -(wheel)="event"在 mat-tab-group 上进行 - 但它也不适用于移动设备。https://stackblitz.com/edit/angular-mat-tabs-scrollable-by-wheel-event上面这个很棒的评论中得到了它。

3)我自己的 mat-tabs 滚动,当您单击选项卡时,在移动设备上滚动并自动将单击的选项卡滚动到屏幕的视口中心并不太简单,但它确实有效!:)

首先,您需要在点击选项卡和分页按钮时禁用“从框中”滚动:

mat-tabs-override.scss:

$black: #121212;
@mixin media-query-for-mobile {
  @media (max-width: 768px) and (min-width: 1px) {
    @content;
  }
}
mat-tab-group {
  .mat-tab-header {
    .mat-tab-header-pagination {
      display: none !important; // <== disable pagination
    }
    .mat-tab-label-container {
      left: 0px; // if you need to use it on mobile - set left position to 0
      width: 100%;
      .mat-tab-list {
        overflow-x: auto !important; // <== set horisontal scroll bar imperatively
        // below rule prevents sliding of buttons' container - because it not sliding properly - to left it not slide as well
        transform: none !important;
        .mat-tab-labels {
          // some tweaks for tabs - up to you
          @include media-query-for-mobile {
            justify-content: unset !important; 
          }
          .mat-tab-label {
            // min-width: 20% !important;
            padding: 1.25% !important;
            margin: 0px !important;
            text-transform: uppercase;
            color: $black;
            font-weight: 600;
            min-width: 140px !important;
          }
        }
      }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,您会看到所有选项卡的宽度都相似,并且可以在移动设备上滚动。

接下来,您需要在单击选项卡时自动滚动选项卡并根据当前视口将它们的位置更改为屏幕中心 - 让我们来做吧!

我们可以创建一个指令,它将监听 的主容器<mat-tabs-group>,检查可滚动容器的宽度,.mat-tab-labels并通过自动滚动.mat-tabs-labels容器将选项卡移动到视口中可见的位置:

模板中的指令:

<mat-tab-group scrollToCenter>
    <mat-tab label="tab 1"></mat-tab>
    <mat-tab label="tab 2"></mat-tab>
    <mat-tab label="tab 3"></mat-tab>
    <mat-tab label="tab 4"></mat-tab>
    <mat-tab label="tab 5"></mat-tab>
    <mat-tab label="tab 6"></mat-tab>
    <mat-tab label="tab 7"></mat-tab>
    <mat-tab label="tab 8"></mat-tab>
</mat-tab-group>
Run Code Online (Sandbox Code Playgroud)

指令.ts:

import { Directive, ElementRef, OnDestroy } from '@angular/core';
import { fromEvent, Subscription } from 'rxjs';

interface DOMRectI {
  bottom: number;
  height: number;
  left: number; // position start of element
  right: number; // position end of element
  top: number;
  width: number; // width of element
  x?: number;
  y?: number;
}

@Directive({
  // tslint:disable-next-line:directive-selector
  selector: '[scrollToCenter]',
})
export class MatTabScrollToCenterDirective implements OnDestroy {
  isMobile: boolean;
  subs = new Subscription();
  constructor(
    private element: ElementRef
  ) {
    this.subs.add(
      fromEvent(this.element.nativeElement, 'click').subscribe((clickedContainer: MouseEvent) => {
        const scrollContainer = this.element.nativeElement.querySelector('.mat-tab-list');
        const currentScrolledContainerPosition: number = scrollContainer.scrollLeft;

        const newPositionScrollTo = this.calcScrollToCenterValue(clickedContainer, currentScrolledContainerPosition);
      })
    );
  }

/** calculate scroll position to center of viewport */
  calcScrollToCenterValue(clickedContainer, currentScrolledContainerPosition): number {
    const scrolledButton: DOMRectI = (clickedContainer.target as HTMLElement).getBoundingClientRect();
    const leftXOffset = (window.innerWidth - scrolledButton.width) / 2;
    const currentVisibleViewportLeft = scrolledButton.left;
    const neededLeftOffset = currentVisibleViewportLeft - leftXOffset;
    console.log(scrolledButton);
    const newValueToSCroll = currentScrolledContainerPosition + neededLeftOffset;
    return newValueToSCroll;
  }

  ngOnDestroy() {
    this.subs.unsubscribe();
  }
}


Run Code Online (Sandbox Code Playgroud)

它有效!:0 但不是在 ios 和 IE ... 为什么?因为ios和IE不支持Element.scroll()

解决方案 -npm i element-scroll-polyfill 并设置为 polyfills.ts

/** enable polufill for element.scroll() on IE and ios */
import 'element-scroll-polyfill';
Run Code Online (Sandbox Code Playgroud)

伟大的!但现在滚动不是那么流畅... IE 和 ios 不支持平滑滚动行为。

解决方案 -npm i smoothscroll-polyfill 并添加到 polyfills.ts

import smoothscroll from 'smoothscroll-polyfill';
// enable polyfill
smoothscroll.polyfill();
Run Code Online (Sandbox Code Playgroud)

最后它无处不在。希望它可以帮助某人修复 mat-tabs 自动滚动空虚:)

演示享受吧:)


Kim*_*ern 4

在此stackblitz 演示中尝试我的解决方案。

首先获取 matTabGroup 的引用:

应用程序组件.html

<mat-tab-group #tabGroup>
               ^^^^^^^^^  
Run Code Online (Sandbox Code Playgroud)

应用程序组件.ts

@ViewChild('tabGroup')
tabGroup;
Run Code Online (Sandbox Code Playgroud)

然后监听鼠标滚轮事件并触发自定义滚动函数:

应用程序组件.html

<mat-tab-group (wheel)="scrollTabs($event)" #tabGroup>
                        ^^^^^^^^^^^^^^^^^^  
Run Code Online (Sandbox Code Playgroud)

应用程序组件.ts

scrollTabs(event) {
  const children = this.tabGroup._tabHeader._elementRef.nativeElement.children;

  // get the tabGroup pagination buttons
  const back = children[0];
  const forward = children[2];

  // depending on scroll direction click forward or back
  if (event.deltaY > 0) {
    forward.click();
  } else {
    back.click();
  }
}
Run Code Online (Sandbox Code Playgroud)

免责声明:这个解决方案非常脆弱。Angular Material 选项卡不提供用于执行此操作的 API。该解决方案取决于内部引用,这些引用可能会在没有通知的情况下发生更改(例如,此私有变量this.tabGroup._tabHeader)。此外,它还不会停止页面滚动,并且仅适用于垂直滚动。(不过,这两个问题是可以解决的。)