Se te lo dico, esiste più di un'area visibile.
BRRRRAAAAAAAMMMMMMMM
L'area visibile in uso in questo momento è in realtà un'area visibile all'interno di un'area visibile.
BRRRRAAAAAAAMMMMMMMM
A volte, i dati forniti dal DOM si riferiscono a una di queste aree visibili e non all'altra.
BRRRRAAAAM... cosa?
È vero, dai un'occhiata:
Area visibile del layout e area visibile visiva
Il video in alto mostra una pagina web che viene eseguita lo scorrimento e pizzica lo zoom, insieme a una mini-mappa sulla destra che mostra la posizione delle aree visibili all'interno della pagina.
Durante lo scorrimento regolare le cose sono piuttosto semplici. L'area verde rappresenta l'area visibile del layout, a cui si fissano position: fixed
elementi.
Le cose si fanno strane quando si adotta lo zoom tramite pizzico. La casella rossa rappresenta l'area visibile visiva, ovvero la parte della pagina che possiamo effettivamente vedere. Questo
viewport può essere spostato mentre gli elementi position: fixed
rimangono nella posizione
in cui si trovavano, collegati all'area visibile del layout. Se eseguiamo la panoramica in corrispondenza di un confine
dell'area visibile del layout, viene trascinata anche l'area visibile del layout.
Miglioramento della compatibilità
Sfortunatamente, le API web sono incoerenti per quanto riguarda l'area visibile a cui fanno riferimento e da un browser all'altro.
Ad esempio, element.getBoundingClientRect().y
restituisce l'offset all'interno dell'area visibile del layout. Fantastico, ma spesso vogliamo la posizione all'interno della pagina,
quindi scriviamo:
element.getBoundingClientRect().y + window.scrollY
Tuttavia, molti browser utilizzano l'area visibile visiva per window.scrollY
, il che significa che il codice riportato sopra non funziona quando l'utente esegue lo zoom con le dita.
Chrome 61 modifica window.scrollY
per fare riferimento all'area visibile del layout,
il che significa che il codice riportato sopra funziona anche quando viene eseguito lo zoom tramite pizzico. Infatti, i browser stanno
modificando lentamente tutte le proprietà di posizionamento per fare riferimento all'area visibile del layout.
Ad eccezione di una nuova proprietà...
Esposizione dell'area visibile allo script
Una nuova API espone l'area visibile visiva come window.visualViewport
. Si tratta di una bozza di specifica, con approvazione cross-browser, che arriva in Chrome 61.
console.log(window.visualViewport.width);
Ecco cosa ci offre window.visualViewport
:
visualViewport strutture |
|
---|---|
offsetLeft
|
Distanza tra il bordo sinistro dell'area visibile visiva e l'area visibile del layout, in pixel CSS. |
offsetTop
|
Distanza tra il bordo superiore dell'area visibile visiva e l'area visibile del layout, in pixel CSS. |
pageLeft
|
Distanza tra il bordo sinistro dell'area visibile visiva e il limite sinistro del documento, in pixel CSS. |
pageTop
|
Distanza tra il bordo superiore dell'area visibile visiva e il limite superiore del documento, in pixel CSS. |
width
|
Larghezza dell'area visibile in pixel CSS. |
height
|
Altezza dell'area visibile visiva in pixel CSS. |
scale
|
La scala applicata da pizzicare con lo zoom. Se i contenuti hanno una dimensione doppia a causa dello zoom, viene restituito 2 . Questo non è interessato da
devicePixelRatio .
|
Ci sono anche un paio di eventi:
window.visualViewport.addEventListener('resize', listener);
visualViewport di eventi |
|
---|---|
resize
|
Attivato quando vengono modificati width , height o scale .
|
scroll
|
Attivato quando cambiano le impostazioni offsetLeft o offsetTop .
|
Demo
Il video all'inizio di questo articolo è stato creato utilizzando visualViewport
,
dai un'occhiata in Chrome 61 e versioni successive. Utilizza
visualViewport
per fissare la mini mappa nella parte in alto a destra dell'area visibile
e applica una scala inversa in modo che appaia sempre delle stesse dimensioni,
nonostante lo zoom sia pizzicato.
Oggetti utili
Gli eventi vengono attivati solo quando cambia l'area visibile visiva
Sembra ovvio da dire, ma mi ha colpito quando ho giocato per la prima volta
con visualViewport
.
Se l'area visibile del layout si ridimensiona, ma non quella visiva, non ricevi un evento resize
. Tuttavia, è insolito che l'area visibile del layout venga ridimensionata senza che anche l'area visibile visiva cambi larghezza/altezza.
Il segreto è scorrere. Se viene eseguito lo scorrimento, ma l'area visibile rimane statica rispetto all'area visibile del layout, non ricevi un evento scroll
su visualViewport
, cosa molto comune. Durante lo scorrimento normale del documento, l'area visibile rimane bloccata in alto a sinistra nell'area visibile del layout, quindi scroll
non si attiva su visualViewport
.
Se vuoi conoscere tutte le modifiche all'area visibile, incluse
pageTop
e pageLeft
, dovrai ascoltare anche l'evento di scorrimento della finestra:
visualViewport.addEventListener('scroll', update);
visualViewport.addEventListener('resize', update);
window.addEventListener('scroll', update);
Evitare di duplicare i lavori con più ascoltatori
Come per l'ascolto di scroll
e resize
sulla finestra, probabilmente richiamerai
una funzione di aggiornamento. Tuttavia, è comune che molti di questi eventi
si verifichino contemporaneamente. Se l'utente ridimensiona la finestra, si attiverà resize
, ma molto spesso anche scroll
. Per migliorare le prestazioni, evita di
gestire la modifica più volte:
// Add listeners
visualViewport.addEventListener('scroll', update);
visualViewport.addEventListener('resize', update);
addEventListener('scroll', update);
let pendingUpdate = false;
function update() {
// If we're already going to handle an update, return
if (pendingUpdate) return;
pendingUpdate = true;
// Use requestAnimationFrame so the update happens before next render
requestAnimationFrame(() => {
pendingUpdate = false;
// Handle update here
});
}
Ho presentato un problema relativo alle specifiche, perché ritengo che ci sia un modo migliore, ad esempio un singolo evento update
.
I gestori di eventi non funzionano
A causa di un bug di Chrome, la procedura non funziona:
Buggy: utilizza un gestore di eventi.
visualViewport.onscroll = () => console.log('scroll!');
Invece:
Funziona: utilizza un listener di eventi
visualViewport.addEventListener('scroll', () => console.log('scroll'));
I valori di offset sono arrotondati
Penso (beh, spero) che questo sia un altro bug di Chrome.
offsetLeft
e offsetTop
sono arrotondati, il che non è preciso una volta che l'utente ha aumentato lo zoom. Puoi verificarli durante la demo: se l'utente aumenta lo zoom e fa una panoramica lentamente, la mini-mappa si aggancia tra i pixel non ingranditi.
La frequenza di eventi è lenta
Come altri eventi resize
e scroll
, questi eventi non vengono attivati a ogni frame,
soprattutto sui dispositivi mobili. Puoi verificarlo durante la demo: dopo aver pizzicato lo zoom, la mini-mappa ha problemi a rimanere bloccata sull'area visibile.
Accessibilità
Nella demo ho utilizzato visualViewport
per
contrapporre lo zoom tramite pizzico dell'utente. Può essere logico per questa particolare demo, ma devi riflettere attentamente prima di fare qualsiasi cosa che sostituisca il desiderio dell'utente di aumentare lo zoom.
L'app visualViewport
può essere utilizzata per migliorare l'accessibilità. Ad esempio, se l'utente aumenta lo zoom, puoi scegliere di nascondere elementi position: fixed
decorativi per allontanarli dall'utente. Ma, ancora una volta, fai attenzione a non nascondere qualcosa
che l'utente sta cercando di vedere più da vicino.
Potresti valutare la possibilità di pubblicare un post su un servizio di analisi quando l'utente aumenta lo zoom. Questo potrebbe aiutarti a identificare le pagine con cui gli utenti hanno difficoltà con il livello di zoom predefinito.
visualViewport.addEventListener('resize', () => {
if (visualViewport.scale > 1) {
// Post data to analytics service
}
});
e il gioco è fatto. visualViewport
è una piccola API che risolve
i problemi di compatibilità.