No permita el desplazamiento horizontal al desplazarse verticalmente (y viceversa)

17

Tengo una lista, con el overflow-xy overflow-yestablecido en auto. Además, he configurado el desplazamiento de impulso, por lo que el desplazamiento táctil funciona bien en dispositivos móviles webkit-overflow-scrolling: true.

El problema, sin embargo, es que no puedo entender cómo deshabilitar el desplazamiento horizontal al desplazarme verticalmente. Conduce a una experiencia de usuario realmente mala, ya que deslizar hacia la parte superior izquierda o superior derecha hará que la tabla se desplace en diagonal. Cuando el usuario se desplaza verticalmente, absolutamente NO deseo ningún desplazamiento horizontal hasta que el usuario haya dejado de desplazarse verticalmente.

He intentado lo siguiente:

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

Pero el conjunto que cada vez overflow-xo overflow-ypara hiddencuando su ha desplazado, el desplazamiento será glitch y saltar de nuevo a la parte superior. También he notado que esa webkit-overflow-scrolling: truees la razón por la cual esto ocurre, cuando lo elimino, el comportamiento parece detenerse, pero lo necesito absolutamente para el desplazamiento de impulso en los dispositivos móviles.

¿Cómo desactivo el desplazamiento horizontal cuando me desplazo verticalmente?

Josh O'Connor
fuente
No sé cómo solucionar el problema de desplazamiento. ¿Tal vez dar un paso atrás e intentar evitar que uno de los dos ejes se desplace de todos modos? Fuera de tema: las tablas no están pensadas para pantallas realmente pequeñas.
Joep Kockelkorn
Falta un soporte rizado en su código. Si lo agrega como un fragmento, verá un mensaje de error en la consola.
Razvan Zamfir
Solo una estúpida idea. ¿por qué no usar dos controles deslizantes o controles deslizantes personalizados para desplazarse?
Thomas Ludewig
Concepto relacionado: stackoverflow.com/questions/24594653/…
James Dunn

Respuestas:

4

Prueba esto

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

En lugar de usar un div para desplazarse, ¿por qué no usa dos? Tener uno para X y otro para Y

vaso de
fuente
1
¡Gran idea! Voy a darle una oportunidad a esto.
Josh O'Connor
1
Lol, tengo que decir que es una idea ingeniosa.
frodo2975
15

En general, es una mala práctica para su diseño necesitar desplazamiento de múltiples ejes en dispositivos móviles, a menos que tal vez esté mostrando grandes tablas de datos. Dicho esto, ¿por qué quieres evitarlo? Si un usuario quiere desplazarse en diagonal, eso no me parece el fin del mundo. Algunos navegadores, como Chrome en OSX, ya hacen lo que está describiendo de manera predeterminada.

Si usted debe tener el desplazamiento de un solo eje, una posible solución podría ser hacer un seguimiento de la posición de desplazamiento a través de ti mismo touchstarty touchmoveeventos. Si establece su umbral de arrastre más bajo que el del navegador, es posible que pueda hacer sus cosas CSS antes de que comience a desplazarse, evitando el error percibido. Además, incluso si todavía falla, tiene el inicio táctil y la ubicación actual del toque. A partir de estos, si registra la posición de desplazamiento inicial de su div, puede desplazar manualmente el div al lugar correcto para contrarrestarlo saltando a la parte superior si es necesario. Un posible algoritmo podría verse así:

// 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 idea: en su controlador de desplazamiento, en lugar de cambiar las clases css, establezca la posición de desplazamiento del div directamente para el eje que desea bloquear. Es decir, si se desplaza horizontalmente, configure siempre el scrollTop en su valor inicial. Esto también podría cancelar el desplazamiento, no estoy seguro. Tendría que probarlo para ver si funciona.

frodo2975
fuente
Intenté esto, pero estaba súper nervioso y el rendimiento fue horrible.
Josh O'Connor
3

Hay numerosas formas de tratar de resolver esto. Aquí se ofrecen algunas buenas ideas.

Sin embargo, como otros han aludido, confiar (o tratar de evitar) el desplazamiento multidimensional es un mal olor de UX, lo que indica que el UX es un problema. No creo que este sea un problema de desarrollo legítimo. Sería mejor dar un paso atrás y reevaluar lo que está tratando de lograr. Uno de los problemas podría ser que, en un esfuerzo por hacer que la interfaz de usuario sea más utilizable, confundirá a los usuarios. La usabilidad descrita aquí probablemente causaría confusión.

¿Por qué no puedo desplazarme horizontalmente?

¿Por qué cuando dejo de desplazarme verticalmente, solo entonces puedo desplazarme horizontalmente?

Estas son algunas preguntas que pueden hacerse (inaudible).

Si está tratando de permitir que la información adicional de la lista de datos verticales sea navegable solo cuando el usuario ha seleccionado la fila, probablemente sería mucho mejor simplemente tener una lista plana por defecto solo desplazable verticalmente, y solo alternar la vertical información cuando la "fila" ha sido seleccionada / activada. Colapsar los detalles lo llevaría nuevamente a la lista vertical plana.

Si tiene que saltar a través de aros para resolver un desafío técnico tan marginal, es una buena indicación de que la experiencia del usuario no ha sido bien diseñada para empezar.

Arthur K
fuente
3

Necesito usar tres contenedores.
En el primer contenedor habilito el desplazamiento vertical y no permitir horizontal.
En el segundo, viceversa, habilito el desplazamiento horizontal y no permitir vertical. Asegúrese de usar overflow-x: hidden;y overflow-y: hidden;, de lo contrario, los contenedores secundarios pueden ir más allá del contenedor actual.
También necesita usar min-width: 100%; min-height: 100%;.
Para el tercer contenedor necesitamos usar display: inline-block;y luego el contenido interno estirará este contenedor y las barras de desplazamiento correspondientes aparecerán en los dos bloques principales.

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

Puedes probarlo aquí en Safari en iPhone.

¡¡Buena suerte!! 😉

Konstantin Piliugin
fuente
: manos levantadas:: manos levantadas:
cup_of
0

esto parece divertido;)

No discutiré si es razonable.

Lo probé con 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();
  }

Nos Buffer cada cuarto touchemoveevento y luego hacer algunos cálculos altamente sofisticada con las coordenadas de los cuatro eventos ( map(calculateDirection)) el cual salidas RIGHT, LEFT, UPo DOWNy en base a que trato de desplazamiento vertical o desactivar horicontal. En mi teléfono Android en Chrome funciona un poco;)

He creado un pequeño parque infantil , que podría mejorarse, reescribirse, en absoluto ...

Saludos Chris

ChrisY
fuente