使“材质”选项卡可滚动

时间:2018-07-26 17:26:45

标签: javascript angular angular-material

我在我的应用程序中使用“材料”标签(mat-tab内的mat-tab-group) 当选项卡超出显示的数量时,将显示两个导航按钮以显示其他选项卡:

enter image description here

我的要求是使用户能够在标签栏上滚动,以便显示其他标签。

我尝试进行一些CSS更改,但无法解决。如有任何解决方案或建议,我们将不胜感激。

4 个答案:

答案 0 :(得分:3)

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

1)在本示例中,“从盒子里”-https://stackblitz.com/edit/angular-mat-tabs-scrollalble-initial-behavior已经部分实现了此行为。 问题是您无法正常向后滚动-某些错误的行为会将选项卡推向左侧。因此它无法按我的意愿工作。

2)下一个变体是滚动事件-在mat-tab-group上使用(wheel)="event"-但它也不适用于移动设备。 https://stackblitz.com/edit/angular-mat-tabs-scrollable-by-wheel-event从这个awesome comment above那里得到了。

3)我自己的mat-tab滚动,包括在移动设备上滚动以及在单击选项卡时将单击的选项卡自动滚动到屏幕视口的中心,这并不是太简单,但是可以! :)

首先,您需要在点击标签页和分页按钮时禁用“从盒子”滚动:

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;
          }
        }
      }
    }
  }
}

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

接下来,您需要在点击标签时对标签进行自动滚动,然后根据当前视口将其位置更改为屏幕中央-让我们开始吧!

我们可以创建一个指令,该指令将监听<mat-tabs-group>的主容器,检查可滚动容器.mat-tab-labels的宽度,并通过自动滚动{{1}来将选项卡移动到视口中}容器以所需的方式:

模板中的指令:

.mat-tabs-labels

directive.ts:

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

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

解决方案-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(); } } 并设置为polyfills.ts

npm i element-scroll-polyfill

太好了!但是现在滚动不是那么平滑... IE和ios不支持smooth-scroll-behavior。

解决方案-/** enable polufill for element.scroll() on IE and ios */ import 'element-scroll-polyfill'; 并添加到polyfills.ts

npm i smoothscroll-polyfill

最后,它在任何地方都可以使用。 希望它可以帮助某人修复mat-tabs自动滚动的空虚感:)

DEMO 享受它:)

答案 1 :(得分:1)

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

首先获取对matTabGroup的引用:

app.component.html

<mat-tab-group #tabGroup>
               ^^^^^^^^^  

app.component.ts

@ViewChild('tabGroup')
tabGroup;

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

app.component.html

<mat-tab-group (wheel)="scrollTabs($event)" #tabGroup>
                        ^^^^^^^^^^^^^^^^^^  

app.component.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();
  }
}

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

答案 2 :(得分:1)

尝试使用此纯CSS解决方案 StackBlitz

将此代码放在使用mat-tab的同一组件中

::ng-deep .mat-tab-header {
  overflow-x: scroll !important;
}

::ng-deep .mat-tab-label-container { 
  overflow: visible !important;
}

::ng-deep .mat-tab-header::-webkit-scrollbar { // TO Remove horizontal scrollbar in tabs
  display: none;
}

答案 3 :(得分:0)

在鼠标滚轮事件(上面发布)的顶部添加拖动事件和拖动开始事件以滚动 mat-tab-group 下的 strd mat-tab

onDrag(event) {
  if (event.clientX > 0) {
    let deltaX = this.previousX - event.clientX;
    const children = this.tabGroup._tabHeader._elementRef.nativeElement.children;

    // get the tabGroup pagination buttons
    const back = children[0];
    const forward = children[2];
    console.log('dragging' + deltaX);
    // depending on scroll direction click forward or back
    if (deltaX > 3) {
      forward.click();
    } else if (deltaX < -3) {
      back.click();
    }
  }
  this.previousX = event.clientX;
  event.target.style.opacity = 1;
}
onDragStart(event) {
  this.previousX = event.clientX;
  event.target.style.opacity = 0;
}
.noselect {
  user-select: none;
  /* Non-prefixed version, currently
                                supported by Chrome, Edge, Opera and Firefox */
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.min.js"></script>
<mat-card fxflex class="card component-right" *ngIf="editMode">
  <mat-card-content class="card-content">
    <mat-tab-group class="noselect" draggable="true" (drag)="onDrag($event)" (dragstart)="onDragStart($event)" (wheel)="scrollTabs($event)" #tabGroup>
      <mat-tab label="1st tab"> </mat-tab>
      <mat-tab label="2nd tab"> </mat-tab>
      <mat-tab label="3rd tab"> </mat-tab>
      <mat-tab label="4th tab"> </mat-tab>
      <mat-tab label="5th tab"> </mat-tab>
      <mat-tab label="6th tab"> </mat-tab>
    </mat-tab-group>
  </mat-card-content>
</mat-card>