당겨서 새로고침 및 오버플로 효과를 맞춤설정하여 스크롤을 관리하세요

요약

개발자는 CSS overscroll-behavior 속성을 사용하여 콘텐츠의 상단/하단에 도달할 때 브라우저의 기본 오버플로 스크롤 동작을 재정의할 수 있습니다. 사용 사례로는 모바일에서 당겨서 새로고침 기능을 사용 중지하고, 오버스크롤 발광 및 고무밴드 효과를 삭제하며, 페이지 콘텐츠가 모달/오버레이 아래에 있을 때 스크롤되지 않도록 하는 등의 기능을 들 수 있습니다.

배경

스크롤 경계 및 스크롤 체인

Chrome Android의 스크롤 체인

스크롤은 페이지와 상호작용하는 가장 기본적인 방법 중 하나이지만 특정 UX 패턴은 브라우저의 기발한 기본 동작으로 인해 처리하기가 까다로울 수 있습니다. 예를 들어, 사용자가 스크롤해야 할 수 있는 많은 항목이 있는 앱 검색 창이 있다고 가정해 보겠습니다. 하단에 도달하면 더 이상 사용할 콘텐츠가 없으므로 오버플로 컨테이너가 스크롤을 중지합니다. 즉, 사용자가 '스크롤 경계'에 도달합니다. 그러나 사용자가 계속 스크롤하면 어떻게 되는지 살펴보세요. 뒤에 있는 콘텐츠가 스크롤을 시작합니다. 스크롤은 상위 컨테이너가 인계받습니다(위 예에서는 기본 페이지 자체).

이 동작은 콘텐츠를 스크롤할 때 브라우저의 기본 동작인 스크롤 체인이라고 합니다. 기본값이 꽤 좋은 경우가 많지만 바람직하지 않거나 예상치 못한 경우도 있습니다. 특정 앱은 사용자가 스크롤 경계에 도달할 때 다른 사용자 환경을 제공하려고 할 수 있습니다.

당겨서 새로고침 효과

당겨서 새로고침은 Facebook 및 Twitter와 같은 모바일 앱에서 널리 사용되는 직관적인 동작입니다. 소셜 피드를 내리고 손을 떼면 최신 게시물을 로드할 공간이 새로 만들어집니다. 실제로 이 특정 UX가 많이 사용되어 Android의 Chrome과 같은 모바일 브라우저도 동일한 효과를 도입했습니다. 페이지 상단에서 아래로 스와이프하면 전체 페이지가 새로고침됩니다.

트위터의 PWA에서 피드를 새로고침할 때
트위터의 커스텀 당겨서 새로고침
Chrome Android의 기본적인 당겨서 새로고침 작업
으로 전체 페이지를 새로 고칩니다.

Twitter PWA와 같은 상황에서는 기본 당겨서 새로고침 작업을 사용 중지하는 것이 좋습니다. 왜일까요? 이 앱에서는 사용자가 실수로 페이지를 새로고침하는 일이 없도록 해야 합니다. 이중 새로고침 애니메이션이 표시될 수도 있습니다. 또는 브라우저의 작업을 맞춤설정하여 사이트의 브랜딩에 더 밀접하게 맞추는 것이 더 좋을 수 있습니다. 아쉽게도 이러한 유형의 맞춤설정은 적용하기가 까다롭다는 점입니다. 개발자가 불필요한 JavaScript를 작성하거나, 스크롤을 차단하는 비수동 터치 리스너를 추가하거나, 페이지 오버플로를 방지하기 위해 전체 페이지를 100vw/vh <div>로 고정하게 됩니다. 이러한 해결 방법은 스크롤 성능에 관한 부정적인 영향을 잘 문서화되어 있습니다.

더 잘 할 수 있어!

overscroll-behavior 소개

overscroll-behavior 속성은 컨테이너 (페이지 자체 포함)를 오버스크롤할 때 발생하는 동작을 제어하는 새로운 CSS 기능입니다. 이를 사용하여 스크롤 체인을 취소하고, 당겨서 새로고침 작업을 사용 중지/맞춤설정하고, iOS (Safari에서 overscroll-behavior를 구현하는 경우)에서 고무밴드 효과를 사용 중지하는 등의 작업을 할 수 있습니다. 가장 좋은 점은 인트로에서 언급한 해킹과 같은 overscroll-behavior를 사용하더라도 페이지 성능에 부정적인 영향을 미치지 않는다는 것입니다.

이 속성은 다음 세 가지 값을 사용할 수 있습니다.

  1. auto - 기본값입니다. 요소에서 발생한 스크롤은 상위 요소로 전파될 수 있습니다.
  2. contain - 스크롤 체인을 방지합니다. 스크롤은 상위 요소로 전파되지 않지만 노드 내의 로컬 효과는 표시됩니다. 예를 들어 Android의 오버스크롤 발광 효과나 iOS의 고무밴드 효과는 사용자가 스크롤 경계에 도달하면 사용자에게 알립니다. 참고: html 요소에서 overscroll-behavior: contain를 사용하면 오버스크롤 탐색 작업이 방지됩니다.
  3. none - contain과 동일하지만 노드 자체 내의 오버스크롤 효과 (예: Android 오버스크롤 발광 또는 iOS 고무밴딩)도 방지합니다.

몇 가지 예를 통해 overscroll-behavior 사용 방법을 자세히 살펴보겠습니다.

스크롤이 고정 위치 요소를 이스케이프하지 않도록 방지

채팅 상자 시나리오

채팅 창 아래의 콘텐츠도 스크롤됩니다. :(

페이지 하단에 위치가 고정된 채팅 상자를 생각해 보세요. 채팅 상자는 독립 실행형 구성요소이며 뒤에 있는 콘텐츠와 별도로 스크롤되도록 하기 위한 것입니다. 그러나 스크롤 체인으로 인해 사용자가 채팅 기록에서 마지막 메시지를 조회하자마자 문서가 스크롤되기 시작합니다.

이 앱에서는 채팅 상자에서 시작된 스크롤을 채팅 내에 유지하는 것이 더 좋습니다. 채팅 메시지를 보유하는 요소에 overscroll-behavior: contain를 추가하면 됩니다.

#chat .msgs {
  overflow: auto;
  overscroll-behavior: contain;
  height: 300px;
}

기본적으로 채팅 상자의 스크롤 컨텍스트와 기본 페이지를 논리적으로 분리합니다. 결과적으로 사용자가 채팅 기록의 상단/하단에 도달해도 기본 페이지가 유지됩니다. 채팅 상자에서 시작되는 스크롤은 전파되지 않습니다.

페이지 오버레이 시나리오

'언더스크롤' 시나리오의 또 다른 변형은 콘텐츠가 고정 위치 오버레이 뒤에서 스크롤되는 경우입니다. overscroll-behavior 무료 배송이 시작됩니다. 브라우저는 사용자에게 도움을 주려고 하지만 사이트에 버그가 많아집니다.

- overscroll-behavior: contain 유무와 관계없는 모달

: 페이지 콘텐츠를 오버레이 아래로 스크롤
이후: 페이지 콘텐츠가 오버레이 아래로 스크롤되지 않습니다.

당겨서 새로고침 사용 중지

당겨서 새로고침 작업을 사용 중지하는 작업은 한 줄의 CSS로 수행할 수 있습니다. 표시 영역을 정의하는 전체 요소에서 스크롤 체인이 발생하지 않도록 하기만 하면 됩니다. 대부분의 경우 <html> 또는 <body>입니다.

body {
  /* Disables pull-to-refresh but allows overscroll glow effects. */
  overscroll-behavior-y: contain;
}

이 간단한 추가를 통해 채팅 상자 데모에서 이중 당겨서 새로고침 애니메이션을 수정하고 대신 깔끔한 로드 애니메이션을 사용하는 맞춤 효과를 구현할 수 있습니다. 받은편지함이 새로고침되면 전체 받은편지함도 흐려집니다.

이전
이후

다음은 전체 코드의 스니펫입니다.

<style>
  body.refreshing #inbox {
    filter: blur(1px);
    touch-action: none; /* prevent scrolling */
  }
  body.refreshing .refresher {
    transform: translate3d(0,150%,0) scale(1);
    z-index: 1;
  }
  .refresher {
    --refresh-width: 55px;
    pointer-events: none;
    width: var(--refresh-width);
    height: var(--refresh-width);
    border-radius: 50%;
    position: absolute;
    transition: all 300ms cubic-bezier(0,0,0.2,1);
    will-change: transform, opacity;
    ...
  }
</style>

<div class="refresher">
  <div class="loading-bar"></div>
  <div class="loading-bar"></div>
  <div class="loading-bar"></div>
  <div class="loading-bar"></div>
</div>

<section id="inbox"><!-- msgs --></section>

<script>
  let _startY;
  const inbox = document.querySelector('#inbox');

  inbox.addEventListener('touchstart', e => {
    _startY = e.touches[0].pageY;
  }, {passive: true});

  inbox.addEventListener('touchmove', e => {
    const y = e.touches[0].pageY;
    // Activate custom pull-to-refresh effects when at the top of the container
    // and user is scrolling up.
    if (document.scrollingElement.scrollTop === 0 && y > _startY &&
        !document.body.classList.contains('refreshing')) {
      // refresh inbox.
    }
  }, {passive: true});
</script>

오버스크롤 발광 및 고무밴딩 효과 사용 중지

스크롤 경계에 도달할 때 바운스 효과를 사용 중지하려면 overscroll-behavior-y: none를 사용합니다.

body {
  /* Disables pull-to-refresh and overscroll glow effect.
     Still keeps swipe navigations. */
  overscroll-behavior-y: none;
}
이전: 스크롤 경계에 도달하면 발광 효과가 나타납니다.
이후: 발광 효과 사용 중지됨

전체 데모

종합해보면, 전체 채팅 상자 데모에서는 overscroll-behavior를 사용하여 맞춤 당겨서 새로고침 애니메이션을 만들고 스크롤이 채팅 상자 위젯을 이스케이프하지 않도록 합니다. 이를 통해 CSS overscroll-behavior가 없으면 달성하기 어려웠을 최적의 사용자 환경을 제공합니다.

데모 보기 | 소스