web-dev-qa-db-ja.com

垂直方向にスクロールするときに水平方向のスクロールを許可しない(およびその逆)

overflow-xoverflow-yautoに設定したリストがあります。さらに、モメンタムスクロールを設定したので、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-xまたはoverflow-yhiddenに設定するたびに、スクロールはグリッチして先頭に戻ります。 webkit-overflow-scrolling: trueがこれが発生する理由であることにも気づきました。削除すると動作が停止したように見えますが、モバイルデバイスでの勢いのあるスクロールには絶対に必要です。

垂直にスクロールするときに水平スクロールを無効にするにはどうすればよいですか?

17
Josh O'Connor

これを試して

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

スクロールに1つのdivを使用する代わりに、2つを使用しないのはなぜですか? XとYに1つずつ

4
cup_of

データの大きなテーブルを表示している場合を除いて、モバイルで多軸スクロールを必要とすることは、一般的にデザインの悪い習慣です。とはいえ、なぜそれを防ぎたいのですか?ユーザーが斜めにスクロールしたい場合、それは私にとって世界の終わりのようには見えません。 Chromeなど)の一部のブラウザは、デフォルトで記述されていることをすでに実行しています。

単軸スクロールが必要な場合、可能な解決策は、touchstartおよびtouchmoveイベントを介して自分でスクロール位置を追跡することです。ドラッグしきい値をブラウザーのしきい値よりも低く設定すると、スクロールが始まる前に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.

2番目のアイデア:cssクラスを変更する代わりに、スクロールハンドラーで、ロックする軸のdivのスクロール位置を直接設定します。 IE、水平方向にスクロールしている場合は、常にscrollTopをその開始値に設定します。確かに、これによってスクロールがキャンセルされることもあります。それが機能するかどうかを確認するために、それを試す必要があります。

15
frodo2975

3つのコンテナーを使用する必要があります。
最初のコンテナで、垂直スクロールを有効にし、水平スクロールを無効にします。
2番目の方法では、その逆で、水平スクロールを有効にし、垂直スクロールを無効にします。必ずoverflow-x: hidden;overflow-y: hidden;を使用してください。そうしないと、子コンテナが現在のコンテナを超える可能性があります。
また、min-width: 100%; min-height: 100%;を使用する必要があります。
3番目のコンテナーの場合、display: inline-block;を使用する必要があります。そうすると、内部コンテンツがこのコンテナーを拡大し、対応するスクロールバーが2つの親ブロックに表示されます。

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 をテストできます。

幸運を!! ????

3

これを解決しようとする多くの方法があります。いくつかの良いアイデアがここに提供されています。

ただし、他の人が言及したように、多次元スクロールに依存する(または回避しようとする)ことはUXの悪臭であり、UXが問題であることを示しています。これは正当な開発者の問題ではないと思います。一歩下がって、達成しようとしていることを再評価する方が良いでしょう。問題の1つは、UIをより使いやすくするためにユーザーを混乱させることです。ここで説明する操作性は、混乱を招く可能性があります。

水平にスクロールできないのはなぜですか?

垂直方向のスクロールを停止すると、水平方向にしかスクロールできないのはなぜですか?

これらは、聞こえるかもしれないいくつかの質問です(聞き取れません)。

ユーザーが行を選択したときにのみ、垂直データリストの追加情報を参照可能にしようとする場合、デフォルトではフラットリストを垂直方向にのみスクロールし、垂直方向のみを切り替える方がはるかに良いでしょう。 「行」が選択/アクティブ化されたときの情報。詳細を折りたたむと、フラットな垂直リストに戻ります。

このような周辺の技術的な課題を解決するためにフープを飛び越えなければならない場合は、ユーザーエクスペリエンスがそもそもうまく設計されていないことを示しています。

3
Arthur K

これは楽しいようです;)

それが妥当であるかどうかについては議論しません。

私は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();
  }

touchemoveイベントを4番目ごとにバッファし、4つのイベント(map(calculateDirection))の座標を使用して高度な計算を行い、RIGHTLEFTUPまたはDOWNを出力し、それに基づいて垂直または水平スクロールを無効にしようとします。私のAndroid電話でchromeそれは一種の動作;)

私は少し playground を作成しました。

乾杯クリス

0
ChrisY