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

TL;DR

La proprietà CSS overscroll-behavior consente agli sviluppatori di ignorare il comportamento di scorrimento per i contenuti in eccesso predefinito del browser quando si raggiunge la parte superiore/inferiore dei contenuti. I casi d'uso includono la disattivazione della funzionalità di aggiornamento con trazione su dispositivi mobili, la rimozione dell'effetto di sfarfallio e di rallentamento dello scorrimento e l'impedimento dello scorrimento dei contenuti della pagina quando si trovano sotto un popup/overlay.

Sfondo

Limiti di scorrimento e accoppiamento di scorrimenti

Concatenamento con lo scorrimento su Chrome per Android.

Lo scorrimento è uno dei modi più fondamentali per interagire con una pagina, ma alcuni pattern UX possono essere difficili da gestire a causa dei comportamenti predefiniti originali del browser. Ad esempio, prendi un riquadro delle app con un numero elevato di elementi che l'utente potrebbe dover scorrere. Quando raggiunge il fondo, il contenitore overflow interrompe lo scorrimento perché non ci sono più contenuti da consumare. In altre parole, l'utente raggiunge un "limite di scorrimento". Ma notiamo cosa succede se l'utente continua a scorrere. I contenuti dietro il riquadro a scomparsa iniziano a scorrere. Lo scorrimento viene assunto dal contenitore principale, ovvero la pagina principale stessa nell'esempio.

Questo comportamento si chiama scroll chain; il comportamento predefinito del browser quando scorrono i contenuti. Spesso il valore predefinito è abbastanza buono, ma talvolta non è auspicabile o addirittura imprevisto. Alcune app potrebbero voler offrire un'esperienza utente diversa quando l'utente raggiunge un confine di scorrimento.

L'effetto di scorrimento per aggiornare

Il pull-to-refresh è un gesto intuitivo reso popolare dalle app mobile come Facebook e Twitter. Trascinando e rilasciando un feed social, si crea un nuovo spazio per il caricamento di 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, l'intera pagina viene aggiornata:

La funzionalità di aggiornamento tramite trazione personalizzata di Twitter
quando aggiornano un feed nella loro PWA.
La funzionalità di aggiornamento tramite trazione nativa di Chrome per Android
aggiorna l'intera pagina.

In situazioni come la PWA di Twitter, potrebbe avere senso disabilitare l'azione nativa di pull-to-refresh. Perché? In questa app, probabilmente non vorrai che l'utente aggiorni la pagina per errore. Inoltre, c'è la possibilità di vedere un'animazione di aggiornamento doppio. In alternativa, potrebbe essere più piacevole personalizzare l'azione del browser, allineandola maggiormente al branding del sito. Purtroppo, questo tipo di personalizzazione è stato difficile da realizzare. Gli sviluppatori finiscono per scrivere codice JavaScript non necessario, aggiungere listener touch non passivi (che bloccano lo scorrimento) o inserire l'intera pagina in un elemento 100vw/vh<div> (per evitare che la pagina fuoriesca). Queste soluzioni alternative hanno effetti negativi ben documentati sulle prestazioni di scorrimento.

Possiamo fare di meglio!

Ti presentiamo overscroll-behavior

La proprietà overscroll-behavior è una nuova funzionalità CSS che controlla il comportamento che si verifica quando scorri oltre un contenitore (inclusa la pagina stessa). Puoi utilizzarlo per annullare la catena di scorrimento, disattivare/personalizzare l'azione di scorrimento verso l'alto per aggiornare, disattivare gli effetti di rallentamento su iOS (quando Safari implementa overscroll-behavior) e altro ancora. La parte migliore è che l'utilizzo di overscroll-behavior non influisce negativamente sul rendimento della pagina, come gli hack menzionati nell'introduzione.

La proprietà può avere tre valori possibili:

  1. auto: valore predefinito. Le scorrimenti che hanno origine nell'elemento possono propagarsi agli elementi ancestrali.
  2. contain: impedisce la concatenazione dello scorrimento. I scorrimenti non si propagano agli antenati, ma vengono mostrati gli effetti locali all'interno del nodo. Ad esempio, l'effetto di bagliore overscroll su Android o l'effetto elastico su iOS che avvisa l'utente quando raggiunge il limite di scorrimento. Nota: l'utilizzo di overscroll-behavior: contain nell'elemento html impedisce le azioni di navigazione con scorrimento eccessivo.
  3. none: come contain, ma impedisce anche gli effetti di scorrimento eccessivo all'interno del nodo stesso (ad es. l'effetto bagliore di scorrimento eccessivo di Android o il rallentamento di iOS).

Vediamo alcuni esempi per capire come utilizzare overscroll-behavior.

Impedisci agli scorrimenti di uscire da un elemento con posizione fissa

Lo scenario della finestra della chat

Anche i contenuti sotto la finestra della chat scorrono :(

Prendi in considerazione una finestra della chat posizionata in modo fisso nella parte inferiore della pagina. L'intenzione è che la casella della chat sia un componente autonomo e scorra separatamente dai contenuti che la seguono. Tuttavia, a causa della catena di scorrimento, il documento inizia a scorrere non appena l'utente raggiunge l'ultimo messaggio nella chat storia.

Per questa app, è più appropriato che le scorribande che hanno origine all'interno della chatbox rimangano all'interno della chat. Possiamo farlo aggiungendo overscroll-behavior: contain all'elemento che contiene i messaggi della chat:

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

In sostanza, stiamo creando una separazione logica tra il contesto scorrevole della finestra della chat e la pagina principale. Il risultato finale è che la pagina principale rimane invariata quando l'utente raggiunge la parte superiore/inferiore della cronologia chat. Le scorribande che iniziano nella chatbox non si propagano.

Scenario di overlay della pagina

Un'altra variante dello scenario "sotto scorrimento" si verifica quando i contenuti scorrono dietro un overlay con posizione fissa. Un regalo davvero speciale overscroll-behavior è in arrivo. Il browser sta cercando di essere utile, ma alla fine il sito sembra pieno di errori.

Esempio: finestra 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.

Disabilitazione del pull-to-refresh

La disattivazione dell'azione di aggiornamento con scorrimento è una singola riga di CSS. Basta impedire la catena di scorrimento nell'intero elemento che definisce l'area visibile. Nella maggior parte dei casi, si tratta di <html> o <body>:

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

Con questa semplice aggiunta, correggiamo le animazioni di aggiornamento con doppio scorrimento nella demo della casella di chat e possiamo implementare un effetto personalizzato che utilizza un'animazione di caricamento più ordinata. Anche l'intera Posta in arrivo viene sfocata durante l'aggiornamento:

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 di sfarfallio e di rallentamento durante lo scorrimento

Per disattivare l'effetto di rimbalzo quando viene raggiunto un confine 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 viene raggiunto il limite di scorrimento, viene visualizzato un bagliore.
Dopo: glow disattivato.

Demo completa

Mettendo tutto insieme, la demo completa della casella di chat utilizza overscroll-behavior per creare un'animazione personalizzata per l'aggiornamento tramite trascinamento e per impedire che le operazioni di scorrimento escano dal widget della casella di chat. In questo modo viene offerta un'esperienza utente ottimale che sarebbe stata difficile da ottenere senza CSSoverscroll-behavior.

Visualizza la demo | Codice sorgente