Was, wenn ich Ihnen sage, dass es mehr als einen Darstellungsbereich gibt?
BRRRRAAAAAAAMMMMMMMMMM
Der Darstellungsbereich, den Sie gerade verwenden, ist in Wirklichkeit ein Darstellungsbereich in einem Darstellungsbereich.
BRRRRAAAAAAAMMMMMMMMMM
Manchmal beziehen sich die Daten im DOM auf einen dieser Ansichten und nicht auf den anderen.
BRRRRAAAAM… Moment mal, was?
Das stimmt. Sehen Sie sich das an:
Layout- und visueller Darstellungsbereich
Im Video oben wird eine Webseite gescrollt und per Pinch-Zoom herangezoomt. Rechts ist eine Minikarte zu sehen, die die Position der Ansichten auf der Seite zeigt.
Beim normalen Scrollen ist alles ganz einfach. Der grüne Bereich stellt den Layout-Darstellungsbereich dar, an den sich position: fixed
-Elemente halten.
Es wird noch komplizierter, wenn das Zoomen durch Zusammenziehen und Spreizen der Finger eingeführt wird. Der rote Rahmen stellt den visuellen Darstellungsbereich dar, also den Teil der Seite, den wir tatsächlich sehen. Dieser Darstellungsbereich kann verschoben werden, während position: fixed
Elemente an ihrem Platz bleiben, da sie am Layout-Darstellungsbereich angehängt sind. Wenn wir an einer Grenze des Layout-Darstellungsbereichs schwenken, wird der Layout-Darstellungsbereich mitgeschleppt.
Kompatibilität verbessern
Leider sind Web-APIs nicht einheitlich in Bezug auf den Viewport, auf den sie sich beziehen, und auch nicht einheitlich in verschiedenen Browsern.
element.getBoundingClientRect().y
gibt beispielsweise den Offset innerhalb des Layout-Darstellungsbereichs zurück. Das ist cool, aber oft möchten wir die Position auf der Seite wissen. Daher schreiben wir:
element.getBoundingClientRect().y + window.scrollY
Viele Browser verwenden jedoch den visuellen Darstellungsbereich für window.scrollY
. Das bedeutet, dass der Code oben bricht, wenn der Nutzer heranzoomt.
In Chrome 61 bezieht sich window.scrollY
stattdessen auf das Layout-Viewport. Das bedeutet, dass der Code oben auch bei Pinch-Zoom funktioniert. Tatsächlich ändern Browser nach und nach alle Positionierungseigenschaften, sodass sie sich auf den Layout-Darstellungsbereich beziehen.
Mit Ausnahme einer neuen Property…
Visuellen Darstellungsbereich für Script freigeben
Eine neue API stellt den visuellen Darstellungsbereich als window.visualViewport
bereit. Es handelt sich um eine Entwurfsspezifikation mit browserübergreifender Genehmigung, die in Chrome 61 eingeführt wird.
console.log(window.visualViewport.width);
window.visualViewport
bietet folgende Vorteile:
visualViewport Unterkünfte |
|
---|---|
offsetLeft
|
Abstand zwischen dem linken Rand des visuellen Darstellungsbereichs und dem Layout-Darstellungsbereich in CSS-Pixeln. |
offsetTop
|
Abstand zwischen dem oberen Rand des visuellen Darstellungsbereichs und dem Layout-Darstellungsbereich in CSS-Pixeln. |
pageLeft
|
Der Abstand zwischen dem linken Rand des visuellen Darstellungsbereichs und der linken Begrenzung des Dokuments in CSS-Pixeln. |
pageTop
|
Der Abstand zwischen dem oberen Rand des visuellen Darstellungsbereichs und dem oberen Rand des Dokuments in CSS-Pixeln. |
width
|
Breite des visuellen Darstellungsbereichs in CSS-Pixeln. |
height
|
Höhe des visuellen Darstellungsbereichs in CSS-Pixeln. |
scale
|
Die Skalierung, die durch Zusammen- und Auseinanderziehen der Finger angewendet wird. Wenn die Inhalte durch Zoomen doppelt so groß sind, wird 2 zurückgegeben. devicePixelRatio hat keine Auswirkungen darauf.
|
Es gibt auch einige Ereignisse:
window.visualViewport.addEventListener('resize', listener);
visualViewport Ereignisse |
|
---|---|
resize
|
Wird ausgelöst, wenn sich width , height oder scale ändert.
|
scroll
|
Wird ausgelöst, wenn sich offsetLeft oder offsetTop ändert.
|
Demo
Das Video am Anfang dieses Artikels wurde mit visualViewport
erstellt. Sehen Sie sich das Video in Chrome 61 und höher an. Mit visualViewport
wird die Minikarte rechts oben im visuellen Viewport fixiert und es wird ein umgekehrter Maßstab angewendet, damit sie trotz Zusammen- und Auseinanderziehen der Finger immer gleich groß erscheint.
Gotchas
Ereignisse werden nur ausgelöst, wenn sich der visuelle Darstellungsbereich ändert
Das mag offensichtlich erscheinen, aber ich habe es erst gemerkt, als ich zum ersten Mal mit visualViewport
experimentiert habe.
Wenn sich das Layout-Viewport ändert, das visuelle Viewport aber nicht, wird kein resize
-Ereignis ausgelöst. Es ist jedoch ungewöhnlich, dass sich die Größe des Layout-Darstellungsbereichs ändert, ohne dass sich auch die Breite/Höhe des visuellen Darstellungsbereichs ändert.
Das Problem liegt beim Scrollen. Wenn der Nutzer scrollt, der visuelle Darstellungsbereich aber im Vergleich zum Layout-Darstellungsbereich statisch bleibt, wird bei visualViewport
kein scroll
-Ereignis ausgelöst. Das ist sehr häufig der Fall. Beim normalen Scrollen des Dokuments bleibt der visuelle Darstellungsbereich oben links im Layout-Darstellungsbereich fixiert. Daher wird scroll
nicht bei visualViewport
ausgelöst.
Wenn Sie über alle Änderungen am visuellen Darstellungsbereich informiert werden möchten, einschließlich pageTop
und pageLeft
, müssen Sie auch das Scrollereignis des Fensters überwachen:
visualViewport.addEventListener('scroll', update);
visualViewport.addEventListener('resize', update);
window.addEventListener('scroll', update);
Mehrere Listener vermeiden
Ähnlich wie beim Zuhören auf scroll
und resize
im Fenster wird wahrscheinlich eine Art „Update“-Funktion aufgerufen. Es kommt jedoch häufig vor, dass viele dieser Ereignisse gleichzeitig auftreten. Wenn der Nutzer die Größe des Fensters ändert, wird resize
ausgelöst, aber häufig auch scroll
. Vermeiden Sie es, die Änderung mehrmals zu verarbeiten, um die Leistung zu verbessern:
// 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
});
}
Ich habe ein Problem mit der Spezifikation eingereicht, da ich der Meinung bin, dass es eine bessere Möglichkeit gibt, z. B. ein einzelnes update
-Ereignis.
Event-Handler funktionieren nicht
Aufgrund eines Chrome-Bugs funktioniert das nicht:
Fehlerhaft – verwendet einen Event-Handler
visualViewport.onscroll = () => console.log('scroll!');
Gehen Sie in diesem Fall so vor:
Funktioniert – verwendet einen Event-Listener
visualViewport.addEventListener('scroll', () => console.log('scroll'));
Offsetwerte werden gerundet.
Ich denke (oder hoffe), dass es sich um einen weiteren Chrome-Fehler handelt.
offsetLeft
und offsetTop
sind gerundet, was ziemlich ungenau ist, wenn der Nutzer heranzoomt. Die Probleme dabei sehen Sie in der Demo: Wenn der Nutzer heranzoomt und langsam schwenkt, springt die Minikarte zwischen nicht herangezoomten Pixeln.
Die Ereignisrate ist zu niedrig
Wie andere resize
- und scroll
-Ereignisse werden diese nicht bei jedem Frame ausgelöst, insbesondere nicht auf Mobilgeräten. Das können Sie in der Demo sehen: Wenn Sie heranzoomen, bleibt die Minikarte nicht immer im Darstellungsbereich.
Bedienungshilfen
In der Demo habe ich visualViewport
verwendet, um das Zoomen per Zusammenziehen und Spreizen der Finger des Nutzers zu verhindern. Für diese Demo ist es sinnvoll, aber Sie sollten gut überlegen, bevor Sie etwas tun, das den Wunsch des Nutzers, heranzuzoomen, überschreibt.
visualViewport
kann die Barrierefreiheit verbessern. Wenn der Nutzer beispielsweise heranzoomt, können Sie dekorative position: fixed
-Elemente ausblenden, damit sie nicht im Weg sind. Achten Sie aber darauf, dass Sie nichts verbergen, was der Nutzer genauer sehen möchte.
Sie könnten beispielsweise einen Analytics-Dienst benachrichtigen, wenn der Nutzer heranzoomt. So können Sie Seiten identifizieren, auf denen Nutzer bei der Standardzoomstufe Schwierigkeiten haben.
visualViewport.addEventListener('resize', () => {
if (visualViewport.scale > 1) {
// Post data to analytics service
}
});
Webseite. visualViewport
ist eine praktische kleine API, die Kompatibilitätsprobleme behebt.