Não permita rolagem horizontal ao rolar verticalmente (e vice-versa)

17

Eu tenho uma lista, com overflow-xoe overflow-ydefinido como auto. Além disso, configurei a rolagem de momento, para que a rolagem por toque funcione bem no celular, usando webkit-overflow-scrolling: true.

O problema, no entanto, é que não consigo descobrir como desativar a rolagem horizontal ao rolar verticalmente. Isso leva a uma péssima experiência do usuário, pois o deslizar para o canto superior esquerdo ou o canto superior direito fará com que a tabela role na diagonal. Quando o usuário está rolando verticalmente, NÃO quero absolutamente nenhuma rolagem horizontal até que o usuário pare de rolar verticalmente.

Eu tentei o seguinte:

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

Mas a cada vez que eu jogo overflow-xou overflow-ypara hiddenquando o seu foi rolado, rolando vai pulso aleatório e saltar de volta ao topo. Também notei que webkit-overflow-scrolling: trueé a razão pela qual isso ocorre. Quando removo, o comportamento parece parar, mas eu absolutamente preciso disso para a rolagem de momento em dispositivos móveis.

Como desabilito a rolagem horizontal ao rolar verticalmente?

Josh O'Connor
fonte
Não sei como corrigir o problema de rolagem. Talvez dê um passo para trás e tente impedir que um dos dois eixos role de qualquer maneira? Fora do tópico: as tabelas não se destinam a telas realmente pequenas.
Joep Kockelkorn 01/10/19
Há um colchete ausente no seu código. Se você adicioná-lo como um trecho, você verá uma mensagem de erro no console.
Razvan Zamfir
apenas uma ideia estúpida. por que não usar dois controles deslizantes ou personalizados para rolar?
Thomas Ludewig
Conceito relacionado: stackoverflow.com/questions/24594653/…
James Dunn

Respostas:

4

Tente isto

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

Em vez de usar uma div para rolar, por que você não usa duas? Tenha um para X e um para Y

cup_of
fonte
11
Boa ideia! mal vou tentar.
Josh O'Connor
11
Lol, eu tenho que dizer que esta é uma idéia engenhosa.
Frodo2975 11/11/19
15

Geralmente, é uma prática ruim para o seu projeto precisar de rolagem multiaxis no celular, a menos que você esteja mostrando grandes tabelas de dados. Dito isto, por que você quer impedi-lo? Se um usuário quiser rolar na diagonal, isso não parece o fim do mundo para mim. Alguns navegadores, como o Chrome no OSX, já fazem o que você está descrevendo por padrão.

Se você precisar ter a rolagem de eixo único, uma solução possível pode ser acompanhar a posição da rolagem por meio de touchstarte touchmoveeventos. Se você definir seu limite de arrasto mais baixo que o do navegador, poderá fazer suas coisas em CSS antes que ele comece a rolar, evitando a falha percebida. Além disso, mesmo se ele ainda apresentar falhas, você terá o toque inicial e a localização atual do toque. Nestas, se você gravar a posição de rolagem inicial do seu div, poderá rolar manualmente o div para o local correto para neutralizá-lo pulando para o topo se necessário. Um possível algoritmo pode ser assim:

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

Segunda ideia: no seu manipulador de rolagem, em vez de alterar as classes css, defina a posição de rolagem da div diretamente para o eixo que você deseja bloquear. IE, se você estiver rolando horizontalmente, sempre defina o scrollTop como seu valor inicial. Isso também pode cancelar a rolagem, não tenho certeza. Você precisaria tentar para ver se funciona.

frodo2975
fonte
Eu tentei isso, mas foi super nervoso e o desempenho foi horrível.
Josh O'Connor
3

Existem várias maneiras de tentar resolver isso. Algumas boas idéias são oferecidas aqui.

No entanto, como outros aludiram, contar com (ou tentar evitar) a rolagem multidimensional é um mau cheiro de UX - indicativo de que o UX é um problema. Eu não acho que isso seja uma questão legítima para desenvolvedores. Seria melhor dar um passo atrás e reavaliar o que você está tentando realizar. Um dos problemas pode ser que, em um esforço para tornar a interface do usuário mais utilizável, você confunda os usuários. A usabilidade descrita aqui provavelmente causaria confusão.

Por que não consigo rolar horizontalmente?

Por que quando paro de rolar verticalmente, só então posso rolar horizontalmente?

Estas são algumas perguntas que podem ser feitas (inaudíveis).

Se você estiver tentando permitir que informações adicionais da lista de dados verticais sejam navegáveis ​​somente quando o usuário selecionou a linha, provavelmente seria muito melhor simplesmente ter uma lista simples por padrão, apenas rolável na vertical e alternar apenas a vertical informações quando a "linha" foi selecionada / ativada. Recolher os detalhes levaria você de volta à lista vertical plana.

Se você estiver tendo que passar por obstáculos para resolver um desafio técnico tão marginal, é uma boa indicação de que a experiência do usuário não foi bem projetada para começar.

Arthur K
fonte
3

Precisa usar três recipientes.
No primeiro contêiner, habilito a rolagem vertical e desabilito a horizontal.
No segundo, vice-versa, habilito a rolagem horizontal e desaprovo a vertical. Certifique-se de usar overflow-x: hidden;e overflow-y: hidden;, caso contrário, os contêineres filhos podem ir além do contêiner atual.
Também precisa usar min-width: 100%; min-height: 100%;.
Para o terceiro contêiner, precisamos usar display: inline-block;e, em seguida, o conteúdo interno esticará esse contêiner e as barras de rolagem correspondentes aparecerão nos dois blocos pai.

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

Você pode testá-lo aqui no Safari no iPhone.

Boa sorte!! 😉

Konstantin Piliugin
fonte
: mãos levantadas:: mãos levantadas:
cup_of 11/11/19
0

isso parece divertido;)

Não vou discutir se é razoável.

Eu tentei com o 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();
  }

Nós tampão cada 4º touchemoveevento e, em seguida, fazer alguns cálculos altamente sofisticado, com as coordenadas dos quatro eventos ( map(calculateDirection)) que saídas RIGHT, LEFT, UPou DOWNe com base em que tento rolagem vertical ou horicontal desativar. No meu telefone Android no Chrome, isso funciona;)

Criei um pequeno playground , que pode ser aprimorado, reescrito, qualquer que seja ...

Cheers Chris

ChrisY
fonte