Assumi il controllo dello scorrimento personalizzando gli effetti pull-to-refresh e overflow

Eric Bidelman
Majid Valipour

TL;DR

La proprietà CSS overscroll-behavior consente agli sviluppatori di ignorare il comportamento di scorrimento extra predefinito del browser quando raggiungono la parte superiore/inferiore dei contenuti. Alcuni casi d'uso includono la disattivazione della funzionalità pull-to-refresh sui dispositivi mobili, la rimozione del bagliore dello scorrimento e degli effetti gommati e l'impedimento dello scorrimento dei contenuti della pagina quando si trovano al di sotto di una finestra modale/overlay.

Contesto

Confini di scorrimento e concatenamento di scorrimento

Concatenazione di scorrimento su Chrome Android.

Lo scorrimento è uno dei modi più fondamentali per interagire con una pagina, ma alcuni modelli UX possono essere difficili da gestire a causa degli stravaganti comportamenti predefiniti del browser. Prendiamo, ad esempio, un riquadro a scomparsa delle app con un numero elevato di elementi che l'utente potrebbe dover scorrere. Quando raggiungono il fondo, il contenitore extra smette di scorrere perché non ci sono altri contenuti da consultare. In altre parole, l'utente raggiunge un "limite di scorrimento". Ma nota cosa succede se l'utente continua a scorrere. I contenuti dietro il riquadro a scomparsa iniziano a scorrere. Lo scorrimento viene rilevato dal contenitore principale (la pagina principale stessa nell'esempio).

Abbiamo scoperto che questo comportamento è chiamato concatenamento di scorrimento. Il comportamento predefinito del browser durante lo scorrimento dei contenuti. Spesso i valori predefiniti sono piuttosto piacevoli, ma a volte non sono desiderati o addirittura imprevisti. Alcune app potrebbero voler fornire un'esperienza utente diversa quando l'utente raggiunge un limite di scorrimento.

Effetto pull-to-refresh

Pull-to-refresh è un gesto intuitivo, reso popolare da app mobile come Facebook e Twitter. Tirando giù su un feed social e rilasciandolo si crea un nuovo spazio in cui caricare post più recenti. In effetti, questa particolare UX è diventata così popolare che i browser mobile come Chrome su Android hanno adottato lo stesso effetto. Se scorri verso il basso nella parte superiore della pagina, viene aggiornata l'intera pagina:

Pull-to-refresh personalizzato di Twitter
quando si aggiorna un feed nella PWA.
L'azione nativa pull-to-refresh di Chrome Android
aggiorna l'intera pagina.

Per situazioni come la PWA di Twitter, potrebbe essere opportuno disattivare l'azione nativa di pull-to-refresh. Perché? In questa app, probabilmente non vuoi che l'utente aggiorni accidentalmente la pagina. Esiste anche la possibilità di vedere un'animazione di doppio aggiornamento. In alternativa, potrebbe essere più opportuno personalizzare l'azione del browser, allineandola più da vicino al branding del sito. La sfortuna è che questo tipo di personalizzazione è stato complicato. Gli sviluppatori finiscono per scrivere codice JavaScript non necessario, aggiungere listener touch non passivi (che bloccano lo scorrimento) o bloccare l'intera pagina a 100 vw/vh <div> (per evitare l'overflow della pagina). Queste soluzioni alternative hanno effetti negativi ben documentati sulle prestazioni dello scorrimento.

Possiamo fare di meglio!

Ti presentiamo overscroll-behavior

La proprietà overscroll-behavior è una nuova funzionalità CSS che controlla il comportamento di ciò che accade quando scorri un contenitore (inclusa la pagina stessa). Puoi utilizzarlo per annullare il concatenamento di scorrimento, disattivare/personalizzare l'azione pull-to-refresh, disattivare gli effetti del elastico su iOS (quando Safari implementa overscroll-behavior) e altro ancora. La parte migliore è che l'utilizzo di overscroll-behavior non influisce negativamente sulle prestazioni delle pagine come i trucchi menzionati nell'introduzione.

La proprietà accetta tre valori possibili:

  1. auto: valore predefinito. Gli scorrimenti che hanno origine sull'elemento possono propagarsi agli elementi predecessori.
  2. contiene: impedisce il concatenamento di scorrimento. Gli scorrimenti non si propagano ai predecessori, ma vengono mostrati gli effetti locali all'interno del nodo. Ad esempio, l'effetto del bagliore durante lo scorrimento orizzontale su Android o l'effetto elastico su iOS che avvisa l'utente quando viene raggiunto un limite di scorrimento. Nota: l'utilizzo di overscroll-behavior: contain nell'elemento html impedisce le azioni di navigazione overscroll.
  3. none: uguale a contain, ma impedisce anche gli effetti di overscroll all'interno del nodo stesso (ad esempio, bagliore overscroll Android o elastic banding iOS).

Analizziamo alcuni esempi per scoprire come utilizzare overscroll-behavior.

Impedire agli scorrimenti di eseguire l'escape di un elemento con posizione fissa

Lo scenario della chatbox

Scorrono anche i contenuti sotto la finestra della chat :(

Prendi in considerazione una chatbox con posizione fissa che si trova nella parte inferiore della pagina. L'intenzione è che il riquadro della chat sia un componente autonomo e che scorra separatamente dai contenuti sottostanti. Tuttavia, a causa del concatenamento di scorrimento, il documento inizia a scorrere non appena l'utente seleziona l'ultimo messaggio nella cronologia chat.

Per questa app, è più appropriato fare in modo che gli scorrimenti che hanno origine all'interno della chatbox rimangano all'interno della chat. Per farlo, aggiungiamo overscroll-behavior: contain all'elemento che contiene i messaggi di chat:

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

Essenzialmente, stiamo creando una separazione logica tra il contesto di scorrimento della chatbox e la pagina principale. Il risultato finale è che la pagina principale rimane visualizzata quando l'utente raggiunge la parte superiore/inferiore della cronologia chat. Gli scorrimenti che si avviano nella chatbox non si propagano.

Lo scenario di overlay della pagina

Un'altra variante dello scenario "underscroll" è quando vedi contenuti che scorrono dietro un overlay di posizione fissa. Ci vuole un regalo obsoleto overscroll-behavior: Il browser cerca di essere utile, ma il sito presenta errori.

Esempio - modale con e senza overscroll-behavior: contain:

Prima: i contenuti della pagina scorrono sotto l'overlay.
Dopo: i contenuti della pagina non scorrono sotto l'overlay.

Disattivazione della funzionalità pull-to-refresh

La disattivazione dell'azione pull-to-refresh è costituita da una singola riga di codice CSS. Basta evitare il concatenamento di scorrimento sull'intero elemento che definisce l'area visibile. Nella maggior parte dei casi, è <html> o <body>:

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

Con questa semplice aggiunta, correggiamo le doppie animazioni di pull-to-refresh nella demo chatbox e possiamo implementare un effetto personalizzato che utilizza un'animazione di caricamento più accurata. Anche l'intera casella della posta in arrivo si sfoca quando si aggiorna:

Prima
Dopo

Ecco uno snippet del codice completo:

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

Disattivazione degli effetti del bagliore durante lo scorrimento orizzontale e della banda di gomma

Per disattivare l'effetto del rimbalzo quando raggiungi un limite di scorrimento, utilizza overscroll-behavior-y: none:

body {
  /* Disables pull-to-refresh and overscroll glow effect.
     Still keeps swipe navigations. */
  overscroll-behavior-y: none;
}
Prima: quando si tocca il limite di scorrimento viene mostrato un bagliore.
Dopo: bagliore disattivato.

Demo completa

Riassumendo, la demo chatbox completa utilizza overscroll-behavior per creare un'animazione pull-to-refresh personalizzata e disattivare l'escape degli scorrimenti dal widget della chatbox. In questo modo, si ottiene un'esperienza utente ottimale che sarebbe difficile ottenere senza CSSoverscroll-behavior.

Visualizza demo | Fonte