[javascript] 세로로 스크롤 할 때 가로 스크롤을 허용하지 마십시오 (또는 그 반대)

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-xoverflow-yhidden의 스크롤 된 경우 글리치 상단으로 다시 이동합니다 스크롤. 또한 webkit-overflow-scrolling: true이것이 발생하는 이유입니다.이를 제거하면 동작이 중지되는 것처럼 보이지만 모바일 장치에서 운동량 스크롤을 위해서는 이것이 필요합니다.

세로로 스크롤 할 때 가로 스크롤을 비활성화하려면 어떻게합니까?



답변

이 시도

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를 사용하는 대신 왜 두 개의 div를 사용하지 않습니까? X와 Y에 각각 하나씩


답변

큰 데이터 테이블을 표시하지 않는 한 모바일에서 다축 스크롤이 필요한 디자인은 일반적으로 좋지 않습니다. 그것은 왜 당신이 그것을 방지하고 싶습니까? 사용자가 대각선으로 스크롤하기를 원한다면 그것은 세상의 종말처럼 보이지 않습니다. OSX의 Chrome과 같은 일부 브라우저는 이미 기본적으로 설명하고 있습니다.

단일 축 스크롤이 필요한 경우 가능한 해결 방법은 스크롤 위치 touchstarttouchmove이벤트를 직접 추적하는 것입니다 . 드래그 임계 값을 브라우저보다 낮게 설정하면 스크롤을 시작하기 전에 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을 시작 값으로 설정하십시오. 스크롤을 취소해도 확실하지 않을 수 있습니다. 작동하는지 확인해야합니다.


답변

이 문제를 해결하는 방법에는 여러 가지가 있습니다. 몇 가지 좋은 아이디어가 여기에 제공됩니다.

그러나 다른 사람들이 언급했듯이 다차원 스크롤에 의존하거나 피하려고 시도하는 것은 UX의 나쁜 냄새입니다. UX가 문제임을 나타냅니다. 나는 이것이 합법적 인 개발 문제라고 생각하지 않습니다. 한 걸음 물러서서 달성하려는 것을 재평가하는 것이 좋습니다. 문제 중 하나는 UI를보다 유용하게 사용하기 위해 사용자를 혼란스럽게 할 수 있다는 것입니다. 여기에 설명 된 유용성은 혼동을 일으킬 수 있습니다.

가로로 스크롤 할 수없는 이유는 무엇입니까?

세로 스크롤을 중지하면 가로 스크롤 만 가능한 이유는 무엇입니까?

다음은 질문이있을 수 있습니다 (들리지 않음).

사용자가 행을 선택한 경우에만 세로 데이터 목록의 추가 정보를 찾아 볼 수있게하려면 기본적으로 세로로만 스크롤 가능하고 세로로만 전환하는 단순 목록을 갖는 것이 훨씬 좋습니다. “행”이 선택 / 활성화되었을 때의 정보. 세부 사항을 축소하면 평평한 수직 목록으로 돌아갑니다.

이러한 기술적 문제를 해결하기 위해 농구대를 뛰어 넘어야하는 경우, 사용자 경험이 처음부터 잘 설계되지 않았 음을 나타내는 것이 좋습니다.


답변

세 개의 용기를 사용해야합니다.
첫 번째 컨테이너에서 세로 스크롤을 사용하고 가로를 허용하지 않습니다.
두 번째, 그 반대로도 가로 스크롤을 사용하고 세로를 허용하지 않습니다. 반드시 사용되어야 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;
}

iPhone의 Safari에서 여기 에서 테스트 할 수 있습니다 .

행운을 빕니다!! 😉


답변

이 재미처럼 보인다;)

그것이 합리적인지에 대해서는 논쟁하지 않을 것입니다.

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 스크롤하려고 것을 기반으로합니다. 크롬에서 내 안드로이드 전화에서 그것은 일종의 작품;)

나는 작은 놀이터를 만들었 습니다.

건배 Chris


답변