【问题标题】:Making Material tabs scrollable使材质选项卡可滚动
【发布时间】:2018-07-26 17:26:44
【问题描述】:

我在我的应用程序中使用 Material 选项卡(mat-tab 位于 mat-tab-group 中) 当标签页数超出显示范围时,会显示两个导航按钮以显示其他标签页:

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

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

【问题讨论】:

    标签: javascript angular angular-material


    【解决方案1】:

    [长文本解决方案] 我的目标是让 mat-tabs 在默认情况下可滚动,没有控件,但能够在单击部分可见的 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-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;
              }
            }
          }
        }
      }
    }
    

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

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

    我们可以创建一个指令,它将监听&lt;mat-tabs-group&gt; 的主容器,检查可滚动容器.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>
    

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

    而且它有效! :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';
    

    太棒了!但是现在滚动不是那么流畅... IE 和 ios 不支持smooth-scroll-behavior。

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

    import smoothscroll from 'smoothscroll-polyfill';
    // enable polyfill
    smoothscroll.polyfill();
    

    终于,它无处不在。 希望它可以帮助某人修复 mat-tabs 自动滚动空白:)

    DEMO享受吧:)

    【讨论】:

      【解决方案2】:

      试试这个纯 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;
      }

      【讨论】:

      • 此解决方案不启用垂直滚动而不单击按钮以显示其他选项卡。这只会隐藏导航@Bhavesh lalwani
      • 这个解决方案几乎可以完美运行。启用此选项后,当您单击屏幕上不完全可见的元素时发生的动画效果不再发生。这似乎与overflow:visible 设置有关。如果我删除它,它可以工作,但我不能再水平滚动。如果滚动菜单以使选项卡仅部分可见,则可以在 StackBlitz 上的示例中看到相同的内容。
      • 这对于窄屏幕上的 Angular Material (12.1.0) 滚动/选择问题非常有效。
      【解决方案3】:

      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)。此外,它还没有停止页面滚动,仅适用于垂直滚动。 (不过这两个可以解决。)

      【讨论】:

      • 谢谢。但这是一个商业级别的应用程序。所以我正在寻找合适的解决方案。
      • 如果没有来自材料库的官方 API,就无法“正确修复”。从某种意义上说,该解决方案很脆弱,它可能会因 libaray 更新(不仅是主要版本)而中断,因为它必须依赖内部(私有)引用。恐怕,唯一合适的解决方案是向 Angular Material 提出拉取请求
      【解决方案4】:

      在鼠标滚轮事件(上面发布)的顶部添加拖动事件和拖动开始事件以滚动 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>

      【讨论】:

        【解决方案5】:

        我们可以通过添加下面的 css 代码使 mat 选项卡可滚动

         .mat-tab-labels
         {
         display : inline-flex !important;
         } 
         .mat-tab-label 
        {
        display:inline-table !important;
        padding-top:10px !important;    
        min-width:0px !important;
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-12-21
          • 2018-06-10
          相关资源
          最近更新 更多