Ne pas autoriser le défilement horizontal lors du défilement vertical (et vice versa)

17

J'ai une liste, avec overflow-xet overflow-yréglé sur auto. De plus, j'ai configuré le défilement de momentum, de sorte que le défilement tactile fonctionne bien sur mobile, en utilisant webkit-overflow-scrolling: true.

Le problème, cependant, est que je ne peux pas comprendre comment désactiver le défilement horizontal lors du défilement vertical. Cela conduit à une très mauvaise expérience utilisateur, car le glissement vers le haut à gauche ou le haut à droite entraînera le défilement de la table en diagonale. Lorsque l'utilisateur fait défiler verticalement, je ne veux absolument PAS de défilement horizontal tant que l'utilisateur n'a pas arrêté de défiler verticalement.

J'ai essayé ce qui suit:

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

Mais à chaque fois que je mets overflow-xouoverflow-y à hiddenquand son défilée, défilante pépin et revenir en arrière vers le haut. J'ai également remarqué que webkit-overflow-scrolling: truec'est la raison pour laquelle cela se produit, lorsque je le supprime, le comportement semble s'arrêter, mais j'en ai absolument besoin pour le défilement de l'élan sur les appareils mobiles.

Comment désactiver le défilement horizontal lors du défilement vertical?

Josh O'Connor
la source
Je ne sais pas comment résoudre le problème de défilement. Peut-être faire un pas en arrière et essayer d'empêcher l'un des deux axes de défiler de toute façon? Hors sujet: les tableaux ne sont pas destinés à de très petits écrans.
Joep Kockelkorn
Il y a un crochet manquant dans votre code. Si vous l'ajoutez en tant qu'extrait, vous verrez un message d'erreur dans la console.
Razvan Zamfir
juste une idée stupide. pourquoi ne pas utiliser deux curseurs ou des curseurs personnalisés pour faire défiler?
Thomas Ludewig
Concept connexe: stackoverflow.com/questions/24594653/…
James Dunn

Réponses:

4

Essaye ça

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

Au lieu d'utiliser un div pour le défilement, pourquoi n'en utilisez-vous pas deux? Avoir un pour X et un pour Y

tasse de
la source
1
Bonne idée! je vais lui donner un coup de feu.
Josh O'Connor
1
Lol, je dois dire que c'est une idée ingénieuse.
frodo2975
15

C'est généralement une mauvaise pratique pour votre conception d'avoir besoin d'un défilement multiaxe sur mobile, à moins que vous ne montriez peut-être de grandes tables de données. Cela étant dit, pourquoi voulez-vous l'empêcher? Si un utilisateur veut faire défiler en diagonale, cela ne me semble pas être la fin du monde. Certains navigateurs, comme Chrome sur OSX, font déjà ce que vous décrivez par défaut.

Si vous devez avoir un défilement sur un seul axe, une solution possible pourrait être de suivre vous-même la position de défilement via touchstartet les touchmoveévénements. Si vous définissez votre seuil de glissement inférieur à celui du navigateur, vous pourrez peut-être faire vos trucs css avant qu'il ne commence à défiler, en évitant le problème perçu. De plus, même si cela ne fonctionne toujours pas, vous avez le démarrage tactile et l'emplacement actuel du toucher. À partir de ceux-ci, si vous enregistrez la position de défilement de départ de votre div, vous pouvez faire défiler manuellement le div à l'endroit correct pour le contrer en sautant vers le haut si vous le devez. Un algorithme possible pourrait ressembler à ceci:

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

Deuxième idée: dans votre gestionnaire de défilement, au lieu de changer les classes css, définissez la position de défilement de la div directement pour l'axe que vous souhaitez verrouiller. IE, si vous faites défiler horizontalement, définissez toujours scrollTop sur sa valeur de départ. Cela peut également annuler le défilement, pas sûr. Il faudrait l'essayer pour voir si cela fonctionne.

frodo2975
la source
J'ai essayé mais c'était super nerveux et les performances étaient horribles.
Josh O'Connor
3

Il existe de nombreuses façons de résoudre ce problème. Quelques bonnes idées vous sont proposées ici.

Cependant, comme d'autres l'ont fait allusion, s'appuyer sur (ou essayer d'éviter) le défilement multidimensionnel est une mauvaise odeur UX - indiquant que l'UX est un problème. Je ne pense pas que ce soit un problème de développement légitime. Il serait préférable de prendre du recul et de réévaluer ce que vous essayez d'accomplir. L'un des problèmes pourrait être que dans un effort pour rendre l'interface utilisateur plus utilisable, vous confondrez les utilisateurs. La convivialité décrite ici provoquerait probablement de la confusion.

Pourquoi ne puis-je pas faire défiler horizontalement?

Pourquoi quand j'arrête de faire défiler verticalement, alors seulement puis-je faire défiler horizontalement?

Ce sont quelques questions qui peuvent être posées (inaudible).

Si vous essayez de permettre à des informations supplémentaires de la liste de données verticale d'être consultables uniquement lorsque l'utilisateur a sélectionné la ligne, il serait probablement bien préférable d'avoir simplement une liste plate par défaut défilante verticalement et de basculer uniquement la verticale informations lorsque la "ligne" a été sélectionnée / activée. Réduire les détails vous ramènerait à la liste verticale plate.

Si vous devez sauter à travers des cerceaux pour résoudre un tel défi technique, c'est une bonne indication que l'expérience utilisateur n'a pas été bien conçue au départ.

Arthur K
la source
3

Besoin d'utiliser trois conteneurs.
Dans le premier conteneur, j'active le défilement vertical et interdit l'autorisation horizontale.
Dans le second, vice versa, j'active le défilement horizontal et interdit le vertical. Assurez-vous d'utiliser overflow-x: hidden;et overflow-y: hidden;, sinon, les conteneurs enfants peuvent dépasser le conteneur actuel.
Besoin également d'utiliser min-width: 100%; min-height: 100%;.
Pour le troisième conteneur, nous devons utiliserdisplay: inline-block; , puis le contenu interne étirera ce conteneur et les barres de défilement correspondantes apparaîtront sur les deux blocs parents.

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

Vous pouvez le tester ici dans Safari sur iPhone.

Bonne chance!! 😉

Konstantin Piliugin
la source
: mains levées:: mains levées:
cup_of
0

ça a l'air amusant;)

Je ne discuterai pas si c'est raisonnable.

Je l'ai essayé avec 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();
  }

Nous TAMPON tous les 4 touchemoveévénement, puis faire un calcul très sophistiqué avec les coordonnées des quatre événements ( map(calculateDirection)) qui sorties RIGHT, LEFT, UPou DOWNet sur la base que j'essaie de désactiver le défilement vertical ou horicontal. Sur mon téléphone Android en chrome ça marche un peu;)

J'ai créé une petite aire de jeux qui pourrait être améliorée, réécrite, que ce soit ...

Bravo Chris

ChrisY
la source