垂直滚动时不允许水平滚动(反之亦然)

时间:2019-10-01 18:31:48

标签: javascript html css angular ionic-framework

我有一个列表,其中overflow-xoverflow-y设置为auto。此外,我已经设置了动量滚动,因此使用webkit-overflow-scrolling: true在移动设备中触摸滚动效果很好。

但是,问题是,我无法弄清楚在垂直滚动时如何禁用水平滚动。这会导致非常糟糕的用户体验,因为向左上方或右上方滑动会导致表格沿对角线滚动。当用户垂直滚动时,我绝对不希望任何水平滚动,直到用户停止垂直滚动为止。

我尝试了以下操作:

JS:

offsetX: number;
offsetY: number;
isScrollingHorizontally = false;
isScrollingVertically = false;

//Detect the scrolling events
ngOnInit() {
    this.scrollListener = this.renderer.listen(
      this.taskRows.nativeElement,
      'scroll',
      evt => {
        this.didScroll();
      }
    );

    fromEvent(this.taskRows.nativeElement, 'scroll')
      .pipe(
        debounceTime(100),
        takeUntil(this.destroy$)
      )
      .subscribe(() => {
        this.endScroll();
    });
}

didScroll() {
    if ((this.taskRows.nativeElement.scrollLeft != this.offsetX) && (!this.isScrollingHorizontally)){
        console.log("Scrolling horizontally")
        this.isScrollingHorizontally = true;
        this.isScrollingVertically = false;
        this.changeDetectorRef.markForCheck();
    }else if ((this.taskRows.nativeElement.scrollTop != this.offsetY) && (!this.isScrollingVertically)) {
        console.log("Scrolling Vertically")
        this.isScrollingHorizontally = false;
        this.isScrollingVertically = true;
        this.changeDetectorRef.markForCheck();
    }
}

endScroll() {
    console.log("Ended scroll")
    this.isScrollingVertically = false;
    this.isScrollingHorizontally = false;
    this.changeDetectorRef.markForCheck();
}

HTML:

<div
    class="cu-dashboard-table__scroll"
    [class.cu-dashboard-table__scroll_disable-x]="isScrollingVertically"
    [class.cu-dashboard-table__scroll_disable-y]="isScrollingHorizontally"
>

CSS:

&__scroll {
  display: flex;
  width: 100%;
  height: 100%;
  overflow-y: auto;
  overflow-x: auto;
  will-change: transform;
  -webkit-overflow-scrolling: touch;

  &_disable-x {
     overflow-x: hidden;
  }

  &_disable-y {
    overflow-y: hidden;
  }
}

但是,每次在滚动时将overflow-xoverflow-y设置为hidden时,滚动都会出现毛刺并跳回到顶部。我还注意到webkit-overflow-scrolling: true是发生这种情况的原因,当我将其删除时,该行为似乎停止了,但是我绝对需要这样做才能在移动设备中进行动量滚动。

垂直滚动时如何禁用水平滚动?

5 个答案:

答案 0 :(得分:15)

除非您正在显示大型数据表,否则通常需要在移动设备上进行多轴滚动对您的设计来说是一个坏习惯。话虽这么说,为什么要预防呢?如果用户想对角滚动,那对我来说似乎不是世界末日。默认情况下,某些浏览器(例如OSX上的Chrome)已经执行了您要描述的内容。

如果必须进行单轴滚动,则可能的解决方案可能是通过touchstarttouchmove事件来自己跟踪滚动位置。如果将拖动阈值设置为低于浏览器的阈值,则可以在开始滚动之前进行CSS填充,从而避免出现毛刺。同样,即使它仍然出现故障,您也可以触摸开始以及触摸的当前位置。从这些记录中,如果记录了div的开始滚动位置,则可以手动将div滚动到正确的位置以抵消它跳到顶部的麻烦。可能的算法如下所示:

// Touchstart handler
if (scrollState === ScrollState.Default) {
    // Record position and id of touch
    touchStart = touch.location
    touchStartId = touch.id.
    scrollState = ScrollState.Touching

    // If you have to manually scroll the div, first store its starting scroll position:
    containerTop = $('.myContainer').scrollTop();
    containerLeft = $('.myContainer').scrollLeft();
}

// Touchmove handler - If the user has dragged beyond a distance threshold,
// do the css classes swap.
if (touch.id === touchStartId && distance(touchStart, touch.location > threshold) {
    scrollState = ScrollState.Scrolling;
    swapCssClasses();

    // If you have to manually scroll the div to prevent jump:
    $('.myContainer').scrollTop(containerTop + (touch.location.y - touchStart.y));
    // Do the same for horizontal scroll.
}

// Then, listen for debounced scroll events, like you're already doing,
// to reset your state back to default.

第二个想法:在滚动处理程序中,不要更改css类,而是直接为要锁定的轴设置div的滚动位置。 IE,如果要水平滚动,请始终将scrollTop设置为其初始值。不确定,这也可能取消滚动。您必须尝试一下它是否有效。

答案 1 :(得分:3)

尝试一下

HTML
<div
  class="cu-dashboard-table__scroll-vertical"
>
  <div
    class="cu-dashboard-table__scroll-horizontal"
  >
    <!-- Scroll content here -->
  </div>
</div>


CSS
&__scroll {
  &-horizontal {
    overflow-x: auto;
    width: 100%;
    -webkit-overflow-scrolling: touch;
  }

  &-vertical {
    overflow-y: auto;
    height: 100%;
    -webkit-overflow-scrolling: touch;
  }
}

为什么不使用一个div滚动,您​​为什么不使用两个呢? X有一个,Y有一个

答案 2 :(得分:2)

有很多方法可以解决此问题。这里提供了一些好主意。

但是,正如其他人所提到的那样,依靠(或试图避免)多维滚动是一种不好的UX味道-表示UX是一个问题。我认为这不是合法的开发问题。最好退后一步,重新评估您要完成的工作。问题之一可能是,为了使UI更加可用,您将使用户感到困惑。这里描述的可用性可能会引起混乱。

  

为什么我不能水平滚动?

     

为什么当我停止垂直滚动时,才可以水平滚动?

这些是可能会被问到的(听不清)的问题。

如果您试图仅当用户选择了行时才允许浏览垂直数据列表的其他信息,那么默认情况下只具有一个垂直滚动的平面列表可能会更好。选择/激活“行”时切换垂直信息。折叠详细信息将使您回到平坦的垂直列表。

如果您必须跳过障碍来解决此类附带的技术挑战,则可以很好地表明,用户体验一开始的设计并不理想。

答案 3 :(得分:2)

需要使用三个容器。
在第一个容器中,我启用垂直滚动并禁止水平滚动。
在第二个过程中,反之亦然,我启用水平滚动并禁止垂直滚动。 请确保使用overflow-x: hidden;overflow-y: hidden;,否则子容器可能会超出当前容器。
还需要使用min-width: 100%; min-height: 100%;
对于第三个容器,我们需要使用display: inline-block;,然后内部内容将拉伸该容器,并且相应的滚动条将出现在两个父块上。

HTML

<div class="scroll-y">
  <div class="scroll-x">
    <div class="content-container">

      <!-- scrollable content here -->

    </div>
  </div>
</div>

CSS

.scroll-y {
  position: absolute;
  overflow-x: hidden;
  overflow-y: auto;
  width: 100%;
  height: 100%;
  min-width: 100%;
  min-height: 100%;
}

.scroll-x {
  overflow-y: hidden;
  overflow-x: auto;
  width: auto;
  min-width: 100%;
  min-height: 100%;
}

.content-container {
  min-width: 100%;
  min-height: 100%;
  display: inline-block;
}

您可以在iPhone上的Safari here中对其进行测试。

祝你好运! ?

答案 4 :(得分:0)

这看起来很有趣;)

我不会争论这是否合理。

我用RxJS尝试过:

  ngAfterViewInit() {
    const table = document.getElementById("table");

    const touchstart$ = fromEvent(table, "touchstart");
    const touchmove$ = fromEvent(table, "touchmove");
    const touchend$ = fromEvent(table, "touchend");

    touchstart$
      .pipe(
        switchMapTo(
          touchmove$.pipe(
            // tap(console.log),
            map((e: TouchEvent) => ({
              x: e.touches[0].clientX,
              y: e.touches[0].clientY
            })),
            bufferCount(4),
            map(calculateDirection),
            tap(direction => this.setScrollingStyles(direction)),
            takeUntil(touchend$)
          )
        )
      )
      .subscribe();
  }

我们每隔第4个touchemove事件进行缓冲,然后使用四个事件(map(calculateDirection))的坐标进行一些高度复杂的计算,从而输出RIGHTLEFT,{{ 1}}或UP,并据此尝试禁用垂直或水平滚动。在我的Chrome安卓手机上,它可以工作;)

我创建了一个playground,可以对其进行增强,重写等等。

给克里斯加油