Jangan izinkan gulir horizontal saat menggulir secara vertikal (dan sebaliknya)

17

Saya punya daftar, dengan overflow-xdan overflow-ydiatur ke auto. Selain itu, saya telah menyiapkan gulir momentum, sehingga gulir sentuh berfungsi dengan baik di seluler, menggunakan webkit-overflow-scrolling: true.

Masalahnya, bagaimanapun, adalah bahwa saya tidak tahu cara menonaktifkan gulir horizontal saat menggulir secara vertikal. Ini mengarah pada pengalaman pengguna yang benar-benar buruk, karena menggesekkan ke kiri atas atau kanan atas akan menyebabkan tabel bergulir secara diagonal. Ketika pengguna bergulir secara vertikal, saya benar-benar TIDAK ingin ada bergulir secara horizontal sampai pengguna telah berhenti menggulir secara vertikal.

Saya sudah mencoba yang berikut ini:

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

Tapi setiap kali saya set overflow-xatau overflow-yke hiddenketika yang telah menggulir, bergulir akan glitch dan melompat kembali ke atas. Saya juga memperhatikan bahwa webkit-overflow-scrolling: trueinilah alasan mengapa hal ini terjadi, ketika saya menghapusnya, perilaku tersebut tampaknya berhenti, tetapi saya benar-benar membutuhkan ini untuk menggulir momentum dalam perangkat seluler.

Bagaimana cara menonaktifkan gulir horizontal saat menggulir secara vertikal?

Josh O'Connor
sumber
Saya tidak tahu cara memperbaiki masalah pengguliran. Mungkin mundur selangkah dan mencoba mencegah salah satu dari dua sumbu tetap bergulir? Di luar topik: Tabel tidak dimaksudkan untuk layar yang sangat kecil.
Joep Kockelkorn
Ada braket keriting yang hilang dalam kode Anda. Jika Anda menambahkannya sebagai cuplikan, Anda akan melihat pesan kesalahan di konsol.
Razvan Zamfir
hanya ide bodoh. mengapa tidak menggunakan dua penggeser atau penggeser khusus untuk menggulir?
Thomas Ludewig
Konsep terkait: stackoverflow.com/questions/24594653/…
James Dunn

Jawaban:

4

Coba ini

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

Alih-alih menggunakan satu div untuk menggulir, mengapa Anda tidak menggunakan dua div? Memiliki satu untuk X dan satu untuk Y

secangkir
sumber
1
Ide yang hebat! Aku akan mencoba ini.
Josh O'Connor
1
Lol, saya harus mengatakan ini adalah ide yang cerdik.
frodo2975
15

Ini umumnya merupakan praktik buruk bagi desain Anda yang memerlukan pengguliran multiaxis di perangkat seluler, kecuali mungkin Anda menampilkan tabel data besar. Yang sedang berkata, mengapa Anda ingin mencegahnya? Jika seorang pengguna ingin menggulir secara diagonal, itu sepertinya bukan akhir dari dunia bagi saya. Beberapa browser, seperti Chrome di OSX, sudah melakukan apa yang Anda gambarkan secara default.

Jika Anda harus menggulirkan satu sumbu, solusi yang mungkin dilakukan adalah melacak sendiri posisi gulir touchstartdan touchmoveperistiwa. Jika Anda menetapkan ambang seret lebih rendah dari browser, Anda mungkin dapat melakukan hal-hal css sebelum mulai menggulir, menghindari kesalahan yang dirasakan. Juga, bahkan jika itu masih kesalahan, Anda memiliki sentuhan awal dan lokasi sentuhan saat ini. Dari ini, jika Anda merekam posisi gulir awal div Anda, Anda dapat secara manual menggulir div ke tempat yang tepat untuk menangkalnya melompat ke atas jika Anda harus. Algoritma yang mungkin terlihat seperti ini:

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

Gagasan kedua: di penangan gulir Anda, alih-alih mengubah kelas css, atur posisi gulir div secara langsung untuk sumbu yang ingin Anda kunci. IE, jika Anda menggulir secara horizontal, selalu atur scrollTop ke nilai awalnya. Ini mungkin juga membatalkan pengguliran, tidak yakin. Anda harus mencobanya untuk melihat apakah itu berhasil.

frodo2975
sumber
Saya mencoba ini tetapi itu sangat gelisah dan kinerjanya buruk.
Josh O'Connor
3

Ada banyak cara untuk mencoba menyelesaikan ini. Beberapa ide bagus ditawarkan di sini.

Namun, seperti yang disinggung orang lain, mengandalkan (atau mencoba menghindari) pengguliran multi-dimensi adalah bau UX yang buruk - indikasi UX menjadi masalah. Saya tidak berpikir ini adalah masalah dev yang sah. Akan lebih baik untuk mengambil langkah mundur dan mengevaluasi kembali apa yang ingin Anda capai. Salah satu masalah mungkin adalah bahwa dalam upaya membuat UI lebih bermanfaat, Anda akan membingungkan pengguna. Kegunaan yang dijelaskan di sini kemungkinan akan menyebabkan kebingungan.

Mengapa saya tidak bisa menggulir secara horizontal?

Mengapa ketika saya berhenti menggulir secara vertikal, hanya dengan demikian saya dapat menggulir secara horizontal?

Ini adalah beberapa pertanyaan yang mungkin ditanyakan (tidak terdengar).

Jika Anda mencoba untuk memungkinkan informasi tambahan dari daftar data vertikal hanya dapat dijelajahi ketika pengguna telah memilih baris, mungkin akan jauh lebih baik untuk hanya memiliki daftar datar secara default hanya dapat digulir secara vertikal, dan hanya beralih vertikal informasi ketika "baris" telah dipilih / diaktifkan. Mengosongkan detail akan membuat Anda kembali ke daftar vertikal rata.

Jika Anda harus melompat melewati rintangan untuk menyelesaikan tantangan teknis pinggiran seperti itu, itu adalah indikasi yang baik bahwa pengalaman pengguna belum dirancang dengan baik untuk memulai.

Arthur K.
sumber
3

Perlu menggunakan tiga kontainer.
Dalam wadah pertama saya mengaktifkan scrolling vertikal dan disallow horizontal.
Dalam yang kedua, sebaliknya, saya mengaktifkan pengguliran horizontal dan larang vertikal. Pastikan untuk menggunakan overflow-x: hidden;dan overflow-y: hidden;, jika tidak, wadah anak dapat melampaui wadah saat ini.
Juga perlu digunakan min-width: 100%; min-height: 100%;.
Untuk wadah ketiga kita perlu menggunakandisplay: inline-block; dan kemudian konten internal akan meregangkan wadah ini dan bilah gulir yang sesuai akan muncul di dua blok induk.

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

Anda bisa mengujinya di sini di di Safari di iPhone.

Semoga berhasil!! 😉

Konstantin Piliugin
sumber
: mengangkat tangan:: mengangkat tangan:
cup_of
0

ini terlihat menyenangkan;)

Saya tidak akan berdebat jika itu masuk akal.

Saya mencobanya dengan 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();
  }

Kami penyangga setiap 4 touchemoveacara dan kemudian membuat beberapa perhitungan yang sangat canggih dengan koordinat dari empat peristiwa ( map(calculateDirection)) yang output RIGHT, LEFT, UPatauDOWN dan berdasarkan itu saya mencoba untuk menonaktifkan bergulir vertikal atau horicontal. Di ponsel android saya di chrome itu berfungsi;)

Saya membuat taman bermain kecil , yang dapat ditingkatkan, ditulis ulang, apa pun ...

Cheers Chris

ChrisY
sumber