简体   繁体   中英

HammerJS swipe with Mat-Tabs blocking vertical scroll

SO I have a component in my Angular application that has tabs for categories of data with an array of values for the content of the tab. I want to use HammerJS to swipe between the tabs for a more native experience. I have installed this virtual scroll package also: https://github.com/rintoj/angular2-virtual-scroll to handle the amount of items I am preparing for the lists under each mat-tab . I'll start by showing the data and the markup here:

Here is building the dummyData just to show how it is structured:

  dummyData: any[] = [];
  selectedTab: number;

  constructor() {
    this.selectedTab = 0;
    for (let i = 0; i < 3; i++) {
      const x = {
        key: i,
        value: []
      }
      for (let j = 0; j < 100; j++) {
        x.value.push(j);
      }
      this.dummyData.push(x);
    }
    console.log(this.dummyData);
  }

and here is the markup that loops this data to show the tabs and the lists:

  <mat-tab-group dynamicHeight="true" mat-stretch-tabs [selectedIndex]="selectedTab">
    <mat-tab *ngFor="let cat of dummyData" [label]="cat.key">
      <ng-template matTabContent>
        <virtual-scroll [items]="cat.value" (update)="viewPortItems = $event" (swipeleft)="swipe($event)" (swiperight)="swipe($event)" (swipeup)="swipe($event)" (swipedown)="swipe($event)">
          <p *ngFor="let item of viewPortItems; let i = index;">{{ item }}</p>
        </virtual-scroll>
      </ng-template>
    </mat-tab>
  </mat-tab-group>

Here is the swipe($event) method that is triggered for the (swipedirection) outputs on the virtual-scroll component:

  swipe(event) {
    console.log(event);
    if (this.selectedTab === 0 && event.type === 'swiperight') { return; }
    if ((this.selectedTab + 1) === this.dummyData.length && event.type === 'swipeleft') { return; }
    switch (event.type) {
      case 'swipeleft':
        this.selectedTab += 1;
        break;
      case 'swiperight':
        this.selectedTab -= 1;
        break;
    }
  }

For clarity here is my Hammer Configuration class and imports:

import * as Hammer from 'hammerjs';
import { HammerGestureConfig, HAMMER_GESTURE_CONFIG } from '@angular/platform-browser';

export class HammerConfig extends HammerGestureConfig {
  overrrides = <any>{
    'swipe': { velocity: 0.4, threshold: 20, direction: Hammer.DIRECTION_ALL },
    'pinch': { enable: false },
    'rotate': { enable: false }
  }
}

Which is also provided: { provide: HAMMER_GESTURE_CONFIG, useClass: HammerConfig }

The purpose of this is for the user to be able to swipe the screen and move between tabs, like a user would on an Android or iOS application. This setup works correctly and does in fact swipe between the tabs and loads the new data next and also virtual scrolls the data to make sure that only a set amount of items load at first.

My problem is that this now blocks all vertical scrolling from a mobile device as Hammer sets touch-action: none on the virtual-scroll component. So I can still scroll with a mouse, but any vertical touch events do not do anything. One thing I also noticed is that the (swipeup) and (swipedown) do not trigger any event in my component code. I have also remvoed the swipeup and swipedown outputs just to make sure they aren't hindering the scroll, but still nothing when vertically dragging up or down on the screen.

I also can confirm this doesn't have an issue with using the virtual-scroll package as I have tried it with a normal div that is not trying to lazy load the items, but just loads them all at once. Still get the same issue blocking any vertical scroll direction. Since I will be using the virtual-scroll as that is more important that the gestures for tabs, I want to show my example here with that in the markup.

Anyone find a way to keep the native virtual scroll while enabling the horzontal swipe gestures?

I appreciate any and all help here, thanks in advance!

I figured out a way to make this work while retaining vertical touch actions. The hammer config I am using now looks like this:

export class HammerConfig extends HammerGestureConfig {
  overrrides = <any>{
    'swipe': { velocity: 0.4, threshold: 20, direction: Hammer.DIRECTION_ALL },
    'pinch': { enable: false },
    'rotate': { enable: false },
    'pan': { velocity: 0.4, threshold: 20, enable: true, direction: Hammer.DIRECTION_ALL }
  }
}

With the pan set for enabled.

Then I am using the (panend) output on my markup: (panend)="swipe($event)" , instead of the (swipedirection) output and this is my editted swipe method:

  swipe(event) {
    console.log(event);
    event.preventDefault();
    if (this.selectedTab === 0 && event.additionalEvent === 'panright') { return; }
    if ((this.selectedTab + 1) === this.menu$.length && event.additionalEvent === 'panleft') { return; }
    switch (event.additionalEvent) {
      case 'panleft':
        this.selectedTab += 1;
        break;
      case 'panright':
        this.selectedTab -= 1;
        break;
    }
  }

From here this still blocks any vertical scrolling, but the one thing that will reenable the vertical scrolling is setting the touch-action: pan-y style on my virtual-scroll element:

virtual-scroll {
    height: calc(100vh - 106px);
    padding: 0px 0px;
    will-change: transform;
    touch-action: pan-y !important;
}

From my understanding, this is telling the system to allow the native touch-action when scrolling in ay direction, but the x direction will then fire off the panend event and I can manage my tab index from there. I hope this can be of help to anyone else trying to use side-to-side gestures while retaining the vertical scrolling behavior.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM