Не разрешать горизонтальную прокрутку при вертикальной прокрутке (и наоборот)

17

У меня есть список, с overflow-xи overflow-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;
  }
}

Но каждый раз, когда множество I overflow-xили overflow-yк hiddenкогда его прокручивается, прокрутка будет глюк и перейти обратно к вершине. Я также заметил, что webkit-overflow-scrolling: trueэто причина, по которой это происходит, когда я удаляю его, поведение, кажется, останавливается, но мне это абсолютно необходимо для прокрутки импульса на мобильных устройствах.

Как отключить горизонтальную прокрутку при вертикальной прокрутке?

Джош О'Коннор
источник
Я не знаю, как исправить проблему с прокруткой. Может быть, сделать один шаг назад и попытаться предотвратить прокрутку одной из двух осей? Не по теме: таблицы не предназначены для действительно маленьких экранов.
Joep
В вашем коде отсутствует фигурная скобка. Если вы добавите его как фрагмент, вы увидите сообщение об ошибке в консоли.
Разван Замфир
просто глупая идея. почему бы не использовать два ползунка или пользовательские ползунки для прокрутки?
Томас
Понятие, связанное с этим: stackoverflow.com/questions/24594653/…
Джеймс Данн

Ответы:

4

Попробуй это

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

чашка
источник
1
Отличная идея! Я дам этому шанс.
Джош О'Коннор
1
Лол, я должен сказать, что это гениальная идея.
frodo2975
15

Как правило, для вашего дизайна плохая практика - использовать многоосевую прокрутку на мобильном устройстве, если, возможно, вы не показываете большие таблицы данных. При этом, почему вы хотите предотвратить это? Если пользователь хочет прокрутить по диагонали, это не кажется мне концом света. Некоторые браузеры, такие как Chrome на OSX, уже делают то, что вы описываете по умолчанию.

Если вам нужна прокрутка по одной оси, возможное решение может заключаться в том, чтобы самостоятельно отслеживать положение прокрутки 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.

Вторая идея: в вашем обработчике прокрутки вместо изменения классов CSS установите положение прокрутки div для оси, которую вы хотите заблокировать. IE, если вы прокручиваете горизонтально, всегда устанавливайте scrollTop в начальное значение. Это также может отменить прокрутку, не уверен. Вы должны попробовать это, чтобы увидеть, работает ли это.

frodo2975
источник
Я попробовал это, но это было супер нервно, и выступление было ужасным.
Джош О'Коннор
3

Есть множество способов решить эту проблему. Некоторые хорошие идеи предлагаются здесь.

Однако, как уже упоминали другие, полагаться на (или пытаться избегать) многомерную прокрутку - это неприятный запах UX, что указывает на проблему UX. Я не думаю, что это законная проблема разработчиков. Было бы лучше сделать шаг назад и пересмотреть то, что вы пытаетесь достичь. Одной из проблем может быть то, что в целях повышения удобства использования пользовательского интерфейса вы будете вводить пользователей в заблуждение. Описанное здесь удобство использования может вызвать путаницу.

Почему я не могу прокрутить по горизонтали?

Почему, когда я прекращаю прокручивать по вертикали, только тогда я могу прокручивать по горизонтали?

Вот некоторые вопросы, которые можно задать (неразборчиво).

Если вы пытаетесь разрешить просмотр дополнительной информации о вертикальном списке данных только тогда, когда пользователь выбрал строку, было бы гораздо лучше просто иметь плоский список по умолчанию с возможностью прокрутки только по вертикали и только переключать по вертикали информация, когда «строка» была выбрана / активирована. Свернув детали, вы вернетесь к плоскому вертикальному списку.

Если вам приходится прыгать через обручи, чтобы решить такую ​​незначительную техническую задачу, это хороший признак того, что пользовательский интерфейс изначально не был продуман.

Артур К
источник
3

Нужно использовать три контейнера.
В первом контейнере я включаю вертикальную прокрутку и запрещаю горизонтальную.
Во втором, наоборот, я включаю горизонтальную прокрутку и запрещаю вертикальную. Обязательно используйте 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;
}

Вы можете проверить это здесь, в Safari на iPhone.

Удачи!! 😉

Константин Пилюгин
источник
: поднятые руки:: поднятые руки:
cup_of
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)) , которая выводит RIGHT, LEFT, UPили DOWNи на основание , что я пытаюсь отключить вертикальную или horicontal прокрутки. На моем андроид телефоне в хроме это вроде работает;)

Я создал маленькую игровую площадку , которую можно улучшить, переписать, что угодно ...

Ура Крис

Криси
источник