Przejmij kontrolę nad przewijaniem – dostosuj efekty przeciągnięcia, aby odświeżyć lub przepełnienia

TL;DR

Właściwość CSS overscroll-behavior pozwala programistom na zastąpienie domyślnego działania przewijania u góry/dół treści w przeglądarce. Zastosowania obejmują między innymi wyłączenie funkcji odświeżania „przeciągnij, aby odświeżyć” na urządzeniach mobilnych, usunięcie poświaty i efektu gumki

Wprowadzenie

Przewijanie granic i łańcuch przewijania

Przewijanie w łańcuchu w Chrome na Androida.

Przewijanie to jeden z najbardziej podstawowych sposobów interakcji ze stroną, jednak zachowanie niektórych wzorców UX może być trudne z powodu dziwnych domyślnych ustawień przeglądarki. Weźmy np. panel aplikacji z dużą liczbą elementów, które użytkownik prawdopodobnie będzie musiał przewinąć. Po dotarciu na dół kontener niepotrzebny przestaje się przewijać, bo nie da się go pobrać. Innymi słowy, użytkownik osiąga „granicę przewijania”. Zwróć jednak uwagę, co się stanie, jeśli użytkownik będzie nadal przewijać stronę. Treść za panelem zaczyna się przewijać. Przewijanie jest przejmowane przez kontener nadrzędny; w przykładzie sama strona główna.

Okazuje się, że jest to tzw. łańcuch przewijania – to domyślne zachowanie przeglądarki podczas przewijania treści. Często domyślne ustawienia są całkiem dobre, ale czasami nie jest to pożądane, a nawet nieoczekiwane. Niektóre aplikacje mogą chcieć zapewniać inne wrażenia użytkownikom, którzy przewiną stronę w inne miejsce.

Efekt „przeciągnij, by odświeżyć”

Odświeżanie przez wyciąganie to intuicyjny gest, który jest popularny wśród aplikacji mobilnych takich jak Facebook czy Twitter. Wykorzystanie kanału społecznościowego i opublikowanie treści tworzy nowe miejsce na wczytywane nowsze posty. Ten konkretny interfejs stał się tak popularny, że przeglądarki mobilne, takie jak Chrome na Androidzie, uzyskały ten sam efekt. Przesunięcie palcem w dół u góry strony powoduje odświeżenie całej strony:

Niestandardowa aplikacja Twittera, która umożliwia odświeżanie,
podczas odświeżania kanału w aplikacji PWA.
Natywne działanie „przeciągnij, aby odświeżyć” w Chrome na Androida
odświeża całą stronę.

W sytuacjach takich jak PWA na Twitterze warto wyłączyć natywne działanie „przeciągnij, by odświeżyć”. Dlaczego? Zapewne nie chcesz, aby użytkownik przypadkowo odświeżał stronę. Można też zobaczyć animację podwójnego odświeżania. Lepszym rozwiązaniem może być dostosowanie działania przeglądarki do marki witryny. Niestety wdrożenie tego typu dostosowywania było trudne. W końcu deweloperzy piszą zbędny kod JavaScript, dodają niepasywne detektory dotyku (które blokują przewijanie) lub wyświetlają całą stronę jako 100vw/vh <div> (aby strona się nie przepełniała). Te obejścia mają dobrze udokumentowany negatywny wpływ na wydajność przewijania.

Stać nas na więcej!

Przedstawiamy overscroll-behavior

Właściwość overscroll-behavior to nowa funkcja CSS, która kontroluje to, co dzieje się, gdy przewiniesz kontener (w tym samą stronę) zbyt mocno. Możesz jej użyć m.in. do anulowania łańcucha przewijania, wyłączenia lub dostosowania działania polegającego na odświeżaniu, wyłączania efektu gumki w systemie iOS (w przypadku Safari w wersji overscroll-behavior). Najlepsze jest to, że korzystanie z narzędzia overscroll-behavior nie wpływa negatywnie na wydajność strony, jak np. ataki hakerskie opisane we wstępie.

Właściwość może przyjmować 3 wartości:

  1. auto – domyślne. Przewinięcia pochodzące z elementu mogą być rozpowszechniane w elementach nadrzędnych.
  2. contain – zapobiega tworzeniu łańcuchów przewijania. Przewinięcia nie są przekazywane do elementów nadrzędnych, ale widoczne są efekty lokalne w obrębie węzła. Na przykład efekt nadmiernych przewijania w Androidzie lub efekt gumki na iOS, który powiadamia użytkownika o tym, że użytkownik znajdzie się poza granicą przewijania. Uwaga: użycie overscroll-behavior: contain w elemencie html zapobiega dalekim przewijaniu.
  3. none – działa tak samo jak contain, ale zapobiega efektom przewinięcia w obrębie samego węzła (np. nadmiernym przewijaniu w Androidzie lub elastycznym przewijaniu w iOS).

Przyjrzyjmy się kilku przykładom, aby dowiedzieć się, jak używać atrybutu overscroll-behavior.

Zapobiegaj pomijaniu przewijania w obrębie elementu o stałej pozycji

Scenariusz rozmowy

Treść pod oknem czatu również się przewija :(

Weźmy pod uwagę stałe okno czatu u dołu strony. Pole czatu jest autonomicznym elementem i przewija się niezależnie od znajdujących się za nim treści. Jednak ze względu na łańcuch przewijania dokument zaczyna przewijać się, gdy tylko użytkownik kliknie ostatnią wiadomość w historii czatu.

W przypadku tej aplikacji przewijanie pochodzące z pola czatu powinno pozostać na czacie. Aby to zrobić, dodaj overscroll-behavior: contain do elementu, który zawiera wiadomości czatu:

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

Zasadniczo tworzymy logiczny podział między kontekstem przewijania pola czatu a stroną główną. Efektem jest to, że strona główna pozostaje na stałe, gdy użytkownik dotrze do górnej lub dolnej części historii czatu. Przewinięcia rozpoczynające się w polu czatu nie są rozpowszechnione.

Scenariusz rozmowy z informacjami o nakładce na stronie

Innym rodzajem scenariuszy „poniżej przewijania” jest sytuacja, w której treść przewija się za nakładką o stałej pozycji. Mamy już upominki overscroll-behavior! Przeglądarka próbuje pomóc, ale w końcu staje się błędna.

Przykład – modalny z parametrem overscroll-behavior: contain i bez niego:

Przed: zawartość strony przewija się pod nakładką.
Po: zawartość strony nie przewija się pod nakładką.

Wyłączam odświeżanie metodą „przeciągnij, aby”

Wyłączenie działania „przeciągnij, aby odświeżyć” to jeden wiersz kodu CSS. Wystarczy zapobiec łańcuchowi przewijania w obrębie całego elementu definiującego widoczny obszar. W większości przypadków jest to <html> lub <body>:

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

Dzięki temu rozwiązaniu poprawiamy podwójne animacje „przeciągnij, aby odświeżyć” w prezentacji pola czatu i możemy zamiast tego zaimplementować niestandardowy efekt, który zapewnia płynniejszą animację wczytywania. Cała skrzynka odbiorcza jest też rozmyta podczas odświeżania:

Przed
Po

Oto fragment pełnego kodu:

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

Wyłączanie poświaty i efektu gumki

Aby wyłączyć efekt odbicia po dotarciu do granicy przewijania, użyj polecenia overscroll-behavior-y: none:

body {
  /* Disables pull-to-refresh and overscroll glow effect.
     Still keeps swipe navigations. */
  overscroll-behavior-y: none;
}
Przed: dotknięcie granicy przewijania powoduje wyświetlenie poświaty.
Po: podświetlenie wyłączone.

Pełna wersja demonstracyjna

W pełnej wersji prezentacji pola czatu wykorzystuje się overscroll-behavior, aby utworzyć niestandardową animację „przeciągnij, by odświeżyć” i wyłączyć przewijanie treści przed zmianą znaczenia widżetu pola czatu. Zapewnia to użytkownikom optymalne wrażenia, co byłoby trudno osiągnąć bez CSS overscroll-behavior.

Wyświetl demonstrację | Źródło