요약
CSS overscroll-behavior
속성을 사용하면 개발자가 콘텐츠의 상단/하단에 도달할 때 브라우저의 기본 오버플로 스크롤 동작을 재정의할 수 있습니다. 사용 사례로는 모바일에서 풀-투-리프레시 기능을 사용 중지하고, 오버스크롤 글로우 및 러버밴딩 효과를 삭제하고, 페이지 콘텐츠가 모달/오버레이 아래에 있을 때 스크롤되지 않도록 하는 것이 있습니다.
배경
스크롤 경계 및 스크롤 체이닝
스크롤은 페이지와 상호작용하는 가장 기본적인 방법 중 하나이지만 브라우저의 독특한 기본 동작으로 인해 특정 UX 패턴을 처리하기가 까다로울 수 있습니다. 예를 들어 사용자가 스크롤해야 할 수 있는 항목이 많은 앱 드로어를 생각해 보세요. 맨 아래에 도달하면 더 이상 사용할 콘텐츠가 없으므로 오버플로 컨테이너의 스크롤이 중지됩니다. 즉, 사용자가 '스크롤 경계'에 도달합니다. 하지만 사용자가 계속 스크롤하면 어떻게 되는지 확인해 보세요. 창 뒤에 있는 콘텐츠가 스크롤되기 시작합니다. 스크롤은 상위 컨테이너(이 예에서는 기본 페이지 자체)에서 처리합니다.
이 동작을 스크롤 체이닝이라고 합니다. 콘텐츠를 스크롤할 때 브라우저의 기본 동작입니다. 기본값이 꽤 좋은 경우가 많지만 바람직하지 않거나 예상치 못한 경우도 있습니다. 특정 앱에서는 사용자가 스크롤 경계를 칠 때 다른 사용자 환경을 제공할 수 있습니다.
풀-투-리프레시 효과
스크롤하여 새로고침은 Facebook 및 트위터와 같은 모바일 앱에서 대중화된 직관적인 동작입니다. 소셜 피드를 아래로 당기고 떼면 최신 게시물이 로드될 수 있는 새로운 공간이 생성됩니다. 실제로 이 특정 UX는 너무 인기가 많아서 Android의 Chrome과 같은 모바일 브라우저에서도 동일한 효과를 채택했습니다. 페이지 상단에서 아래로 스와이프하면 전체 페이지가 새로고침됩니다.
트위터 PWA와 같은 경우에는 기본 풀-투-리프레시 작업을 사용 중지하는 것이 좋습니다. 왜냐하면 이 앱에서는 사용자가 실수로 페이지를 새로고침하는 것을 원하지 않을 수 있습니다. 새로고침 애니메이션이 두 번 표시될 수도 있습니다. 또는 브라우저의 작업을 맞춤설정하여 사이트의 브랜딩에 더 적합하게 조정하는 것이 좋습니다. 안타깝게도 이러한 유형의 맞춤설정은 실행하기가 쉽지 않습니다. 개발자는 불필요한 JavaScript를 작성하거나, 스크롤을 차단하는 비패시브 터치 리스너를 추가하거나, 페이지가 오버플로되지 않도록 전체 페이지를 100vw/vh <div>
에 고정합니다. 이러한 해결 방법은 스크롤 성능에 잘 문서화된 부정적인 영향을 미칩니다.
더 나은 방법이 있습니다.
overscroll-behavior
소개
overscroll-behavior
속성은 컨테이너 (페이지 자체 포함)를 오버스크롤할 때 발생하는 동작을 제어하는 새로운 CSS 기능입니다. 이를 사용하여 스크롤 체이닝을 취소하고, 당겨 새로고침 작업을 사용 중지/맞춤설정하고, iOS에서 러버밴딩 효과를 사용 중지할 수 있습니다 (Safari에서 overscroll-behavior
를 구현하는 경우).
가장 좋은 점은 overscroll-behavior
를 사용해도 인트로에 언급된 해킹과 달리 페이지 성능에 악영향을 미치지 않는다는 것입니다.
이 속성은 다음 세 가지 값을 사용할 수 있습니다.
- auto: 기본값입니다. 요소에서 시작된 스크롤이 조상 요소로 전파될 수 있습니다.
- contain: 스크롤 체이닝을 방지합니다. 스크롤은 상위 요소로 전파되지 않지만 노드 내의 로컬 효과는 표시됩니다. 예를 들어 Android의 오버스크롤 글로우 효과나 iOS의 러버밴딩 효과는 사용자가 스크롤 경계에 도달했을 때 사용자에게 알립니다. 참고:
html
요소에서overscroll-behavior: contain
를 사용하면 오버스크롤 탐색 작업이 방지됩니다. - 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
없이는 달성하기 어려운 최적의 사용자 환경을 제공할 수 있습니다.