Complessità di uno scorrimento continuo

TL;DR: Riutilizzare gli elementi DOM e rimuovere quelli lontani dal area visibile. Utilizza i segnaposto per tenere conto dei dati in ritardo. Ecco un demo e il codice per l'infinito scorrimento.

Su internet appaiono infiniti scorrimenti. L'elenco degli artisti di Google Music è uno, la sequenza temporale di Facebook lo è e anche il feed live di Twitter. Tu scorri verso il basso e, prima di raggiungere il punto più basso, appaiono magicamente nuovi contenuti apparentemente dal nulla. È un'esperienza fluida per gli utenti ed è facile visualizzare il ricorso.

La sfida tecnica dietro uno scorrimento continuo, tuttavia, è più difficile di quanto . La gamma di problemi che riscontri quando vuoi fare la cosa giustaTM è vasto. Inizia con elementi semplici come i link nel piè di pagina praticamente irraggiungibile perché i contenuti continuano ad allontanare il piè di pagina. Ma i problemi diventano sempre più difficili. Come gestire un evento di ridimensionamento quando qualcuno trasforma il proprio lo smartphone passa dall'orientamento verticale a quello orizzontale o come fai a evitare che lo smartphone si grinda si blocca quando l'elenco diventa troppo lungo?

La cosa giustaTM

Abbiamo pensato che fosse un motivo sufficiente per proporre un'implementazione di riferimento che mostra un modo per affrontare tutti questi problemi in modo riutilizzabile mantenere standard di prestazioni.

Utilizzeremo 3 tecniche per raggiungere il nostro obiettivo: riciclo del DOM, lapidi e l'ancoraggio allo scorrimento.

Il nostro caso dimostrativo sarà una finestra di chat in stile Hangouts in cui possiamo scorrere tramite i messaggi. La prima cosa di cui abbiamo bisogno è una fonte infinita di chat messaggi. Tecnicamente, nessuno degli strumenti di scorrimento infiniti disponibile è davvero infinita, ma con la quantità di dati a disposizione per confluire in questi potrebbero essere gli scorrimenti. Per semplicità, basta eseguire l'hard-coding di un insieme di messaggi di chat e scegli messaggio, autore e immagini allegate occasionali su casuale con una pizzica di ritardo artificiale per comportarsi un po' più come rete reale.

Screenshot dell'app Chat

Riciclo DOM

Il riciclo del DOM è una tecnica sottoutilizzata per mantenere basso il numero di nodi DOM. La l'idea generale è quella di usare elementi DOM già creati che sono invece fuori dallo schermo di crearne di nuovi. È vero che i nodi DOM di per sé sono economici, ma non è senza costi, poiché ognuno comporta un costo aggiuntivo in termini di memoria, layout, stile e colorazione. I dispositivi di fascia bassa diventeranno notevolmente più lenti se non completamente inutilizzabili se un sito web ha un DOM troppo grande da gestire. Ricorda inoltre che ogni relayout e la riapplicazione degli stili, un processo che viene attivato ogni volta che una classe viene aggiunto o rimosso da un nodo: diventa più costoso con un DOM più grande. Riciclare i nodi DOM significa che conserveremo il numero totale di DOM nodi notevolmente inferiori, rendendo tutti questi processi più veloci.

Il primo ostacolo è lo scorrimento stesso. Poiché avremo solo un piccolo sottoinsieme di tutti gli elementi disponibili nel DOM in un determinato momento, dobbiamo trovare un altro modo in modo che la barra di scorrimento del browser rifletta correttamente la quantità di contenuti teoricamente. Utilizzeremo un elemento sentinel di 1 x 1 px con una trasformazione per forzare l'elemento che contiene gli articoli (la pista) di avere l'elemento desiderato altezza. Promuoviamo ogni elemento in passerella a un livello specifico per assicurati che il livello della passerella sia completamente vuoto. Nessun colore di sfondo, niente. Se il livello della pista non è vuoto, non è idoneo per il ottimizzazioni e dovremo memorizzare sulla scheda grafica una texture un'altezza di duecentomila pixel. Sicuramente non attuabile su un dispositivo mobile.

Ogni volta che scorriamo, verificheremo se l'area visibile si è avvicinata abbastanza alla fine della passerella. In tal caso, estenderemo la pista muovendo la sentinella e spostando gli elementi che hanno lasciato l'area visibile verso la parte inferiore in passerella e in cui vengono caricati nuovi contenuti.

. Pista Sentinel Area visibile

Lo stesso vale per lo scorrimento nell'altra direzione. Tuttavia, non ridurre la pista nella nostra implementazione, in modo che la posizione della barra di scorrimento resti coerente.

Lapidi

Come accennato in precedenza, cerchiamo di far sì che l'origine dati si comporti come qualcosa nel mondo reale. Con latenza di rete e tutto il resto. Ciò significa che se le nostre gli utenti fanno uso di uno scorrimento sfiorante, possono scorrere facilmente oltre l'ultimo elemento abbiamo i dati. In questo caso, posizioneremo un oggetto lapideo: , che verrà sostituito dall'elemento con i contenuti effettivi una volta sono arrivati i dati. Anche le lapidi sono riciclate e dispongono di una vasca separata per di elementi DOM riutilizzabili. Ne abbiamo bisogno per effettuare una transizione piacevole da tombstone all'elemento riempito di contenuti, che altrimenti sarebbe molto irritante per l'utente e che potrebbe effettivamente fargli perdere di vista i dati su cui concentrarti.

Tale
tomba di Google. Molto pietra. Wow.

Una sfida interessante in questo caso è che gli oggetti reali possono avere un'altezza maggiore di l'elemento tombstone a causa di quantità diverse di testo per elemento o dell'immagine. Per risolvere il problema, regoleremo ogni volta la posizione corrente di scorrimento arrivano i dati e viene sostituita una tombstone sopra l'area visibile, ancoraggio la posizione di scorrimento verso un elemento anziché il valore di pixel. Questo concetto è chiamato ancoraggio a scorrimento.

Ancoraggio al scorrimento

L'ancoraggio a scorrimento verrà richiamato sia quando le lapidi verranno sostituite come nonché quando la finestra viene ridimensionata (cosa che si verifica anche quando i dispositivi vengono capovolto!). Dovremo capire qual è l'elemento più visibile dell'area visibile. Poiché questo elemento potrebbe essere visibile solo parzialmente, memorizza l'offset dalla parte superiore dell'elemento dove inizia l'area visibile.

Scorri il diagramma di ancoraggio.

Se l'area visibile viene ridimensionata e la pista presenta delle modifiche, possiamo ripristinare che sembra identica a quella dell'utente. Vinci! Tranne un riquadro ridimensionato finestra significa che l'altezza di ogni elemento è potenzialmente cambiata, quindi come fino a che punto devono essere posizionati i contenuti ancorati? Non ce l'abbiamo. Per scoprirlo dobbiamo definire il layout di ogni elemento sopra l'elemento ancorato e sommare tutti le loro altezze; ciò potrebbe causare una pausa significativa dopo un ridimensionamento; inoltre, che vuoi. Ricorriamo invece al presupposto che ogni elemento riportato sopra abbia la stessa dimensione come lapide e regola la posizione di scorrimento di conseguenza. Poiché gli elementi sono abbiamo fatto scorrere la pagina verso la pista, regoliamo la posizione dello scorrimento, rinviare efficacemente dal lavoro al layout quando è effettivamente necessario.

Layout

Ho saltato un dettaglio importante: il layout. Ogni riciclo di un elemento DOM di solito ridisegna l'intera pista, andando ben sotto target di 60 frame al secondo. Per evitare che ciò accada, ci prendiamo l'onere in modo che possiamo creare il layout su noi stessi e usare elementi assolutamente posizionati con le trasformazioni. In questo modo possiamo fingere che tutti gli elementi più in alto sulla passerella siano che occupano spazio quando in realtà c'è solo uno spazio vuoto. Dato che stiamo facendo possiamo memorizzare nella cache le posizioni in cui finisce ogni elemento carica immediatamente l'elemento corretto dalla cache quando l'utente scorre indietro.

Idealmente, gli elementi potrebbero essere ridipinti solo una volta quando vengono collegati al DOM e non essere disturbato dall'aggiunta o dalla rimozione di altri articoli in passerella. Vale a dire possibile, ma solo con i browser moderni.

Piccoli ritocchi

Di recente, Chrome ha aggiunto il supporto del Contenimento CSS, una funzionalità che consente agli sviluppatori di indicare al browser che un elemento è un limite layout e dipingere. Poiché stiamo preparando il layout qui, è un'ottima per il contenimento. Ogni volta che aggiungiamo un elemento alla passerella, sappiamo gli altri elementi non devono essere interessati dal relayout. Quindi ogni elemento ottieni contain: layout. Inoltre, non vogliamo influire sul resto del nostro sito web, quindi anche la pista stessa dovrebbe avere questa istruzione di stile.

Un altro aspetto che abbiamo considerato IntersectionObservers come meccanismo per rilevare quando l'utente ha fatto scorrere la pagina abbastanza da consentirci di iniziare a riciclare gli elementi e caricarne di nuovi e i dati di Google Cloud. Tuttavia, gli IntersectionObservers vengono specificati come ad alta latenza (come se utilizzando requestIdleCallback), quindi potremmo sentirci meno reattivi con Intersectionosservatori e non senza. Anche la nostra attuale implementazione utilizzando scroll evento presenta questo problema, perché gli eventi di scorrimento vengono inviati su un del "best effort". In futuro, Houdini’s Compositor Worklet sarebbe la soluzione ad alta fedeltà a questo problema.

Non è ancora perfetto

La nostra attuale implementazione del riciclo del DOM non è ideale perché aggiunge tutti gli elementi che passano attraverso l'area visibile, invece di occuparsi solo di quelle sullo schermo. Ciò significa che quando scorri davvero velocemente, metti per il layout e la colorazione su Chrome non riesce a stare al passo. Terminerai di vedere solo lo sfondo. Non è la fine del mondo, sicuramente qualcosa da migliorare.

Ci auguriamo che tu capisca quanto possono diventare difficili i problemi semplici quando vuoi che combinano un'ottima esperienza utente con standard di prestazioni elevati. Con Le app web progressive diventeranno esperienze fondamentali sui cellulari, questo di maggiore importanza e gli sviluppatori web dovranno continuare a investire utilizzando pattern che rispettano i vincoli delle prestazioni.

Tutto il codice è disponibile nel nostro repository. Abbiamo finito fare del nostro meglio per mantenerlo riutilizzabile, ma non come una vera libreria npm o come repository separato. Si tratta di un utilizzo principale a scopo didattico.