Не разрешать горизонтальную прокрутку при вертикальной прокрутке (и наоборот) - PullRequest
17 голосов
/ 01 октября 2019

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

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

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

Ответы [ 5 ]

15 голосов
/ 04 октября 2019

Обычно для вашего дизайна плохая практика - использовать многоосевую прокрутку на мобильном устройстве, если, возможно, вы не показываете большие таблицы данных. При этом, почему вы хотите предотвратить это? Если пользователь хочет прокрутить по диагонали, это не кажется мне концом света. Некоторые браузеры, такие как 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 в начальное значение. Это также может отменить прокрутку, не уверен. Вы должны попробовать его, чтобы увидеть, работает ли он.

3 голосов
/ 10 октября 2019

Попробуйте это

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 голосов
/ 11 октября 2019

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

Удачи! ?

2 голосов
/ 08 октября 2019

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

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

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

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

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

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

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

0 голосов
/ 09 октября 2019

это выглядит забавно;)

Я не буду спорить о том, разумно ли это.

Я попробовал это с 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 событие, а затем сделать очень сложный расчет с координатами четырех событий (map(calculateDirection)), который выводит RIGHT, LEFT, UP или DOWN и на основании этого я пытаюсь отключить вертикальное илигоризонтальная прокрутка. На моем Android-телефоне в Chrome это вроде работает;)

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

CheersChris

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...