A co, jeśli powiem Ci, że istnieje więcej niż 1 widok?
BRRRRAAAAAAAMMMMMMMMMM
Widoczny obszar, z którego korzystasz, jest widocznym obszarem w widocznym obszarze.
BRRRRAAAAAAAMMMMMMMMMM
Czasami dane z DOM odnoszą się do jednej z tych widocznych stron, a nie do drugiej.
BRRRRAAAAM… poczekaj, co?
To prawda, zobacz:
Widzialny obszar a widoczny obszar
Film powyżej pokazuje przewijanie strony internetowej i powiększanie jej za pomocą gestów, a po prawej stronie minimapę z położeniem widoku na stronie.
Podczas zwykłego przewijania wszystko jest proste. Zielona część przedstawia strefa widoczna układu, do której przylegają elementy position: fixed
.
Gdy włączysz powiększanie przez zbliżanie i oddalanie, może się to dziwnie skończyć. Czerwone pole to widoczny widok strony, czyli część strony, którą widzi użytkownik. Widziany obszar może się przemieszczać, a elementy position: fixed
pozostają na swoich miejscach, przytwierdzone do widocznego obszaru układu. Jeśli przesuniemy widok na krawędzi układu, przesuniemy też widoczny obszar układu.
Poprawianie zgodności
Niestety interfejsy API stron internetowych nie są spójne pod względem tego, do której przeglądarki odnoszą się w przypadku widoku.
Na przykład element.getBoundingClientRect().y
zwraca przesunięcie w widocznym obszarze układu. To świetnie, ale często chcemy znać pozycję na stronie, więc piszemy:
element.getBoundingClientRect().y + window.scrollY
Jednak wiele przeglądarek używa widocznego obszaru viewport dla window.scrollY
, co oznacza, że powyższy kod przestaje działać, gdy użytkownik powiększy obraz.
W Chrome 61 element window.scrollY
odwołuje się do obszaru widoku układu, co oznacza, że powyższy kod działa nawet po powiększeniu za pomocą gestów. W rzeczywistości przeglądarki powoli zmieniają wszystkie właściwości pozycyjne, aby odwoływały się do widocznego obszaru w układzie.
Z wyjątkiem jednej nowej usługi…
Wyświetlanie widocznego obszaru w skrypcie
Nowy interfejs API udostępnia wizualny widoczny obszar jako window.visualViewport
. Jest to specyfikacja wstępna, która ma zatwierdzenie w wielu przeglądarkach i zostanie wprowadzona w Chrome 61.
console.log(window.visualViewport.width);
Oto, co daje nam window.visualViewport
:
visualViewport miejsca zakwaterowania |
|
---|---|
offsetLeft
|
Odległość między lewą krawędzią obszaru widocznego a obszarem widoku układu w pikselach CSS. |
offsetTop
|
Odległość między górną krawędzią widocznego obszaru a obszarem układu w pikselach CSS. |
pageLeft
|
Odległość między lewą krawędzią wizualnego obszaru widoku a lewą granicą dokumentu w pikselach CSS. |
pageTop
|
Odległość między górną krawędzią widocznego obszaru a górną granicą dokumentu w pikselach CSS. |
width
|
Szerokość wizualnego obszaru widocznego w pikselach CSS. |
height
|
Wysokość wizualnego obszaru widoku w pikselach CSS. |
scale
|
Powiększenie zastosowane przez ściągnięcie palców. Jeśli z powodu powiększenia treści są one dwa razy większe, zwróci to wartość 2 . Nie ma to wpływu na devicePixelRatio .
|
Jest też kilka zdarzeń:
window.visualViewport.addEventListener('resize', listener);
visualViewport zdarzenia |
|
---|---|
resize
|
Uruchamiane, gdy zmieniają się wartości width , height lub scale .
|
scroll
|
Uruchamiane, gdy offsetLeft lub offsetTop się zmieni.
|
Prezentacja
Film na początku tego artykułu został utworzony za pomocą visualViewport
. Sprawdź go w Chrome 61 lub nowszej wersji. Używa on visualViewport
, aby minimapa była przyklejona do prawego górnego rogu widocznego obszaru obrazu, i zachowuje odwrotną skalę, dzięki czemu zawsze ma ten sam rozmiar, mimo powiększania za pomocą pinch-zoom.
Gotcha
zdarzenia są wywoływane tylko wtedy, gdy zmienia się wizualny widoczny obszar;
Może się to wydawać oczywiste, ale mnie to zaskoczyło, gdy po raz pierwszy użyłem visualViewport
.
Jeśli rozmiar widoku układu się zmieni, ale nie zmieni się widoczny widok, nie otrzymasz zdarzeniaresize
. Zmiana rozmiaru widocznego obszaru układu bez zmiany szerokości/wysokości widocznego obszaru jest jednak rzadka.
Prawdziwy problem to przewijanie. Jeśli nastąpi przewijanie, ale wizualny widoczny obszar pozostanie statyczny w stosunku do widocznego obszaru układu, nie otrzymasz zdarzenia scroll
w visualViewport
. Jest to bardzo częsta sytuacja. Podczas zwykłego przewijania dokumentu widoczny widoczny obszar pozostaje zablokowany w lewym górnym rogu widocznego obszaru układu, więc scroll
nie jest wywoływany w przypadku visualViewport
.
Jeśli chcesz otrzymywać informacje o wszystkich zmianach w wizualnym widoku, w tym o pageTop
i pageLeft
, musisz też słuchać zdarzenia przewijania okna:
visualViewport.addEventListener('scroll', update);
visualViewport.addEventListener('resize', update);
window.addEventListener('scroll', update);
Unikanie powielania pracy z wieloma odbiorcami
Podobnie jak w przypadku funkcji scroll
i resize
w ramach okna, prawdopodobnie wywołasz jakąś funkcję „update”. Jednak wiele z tych zdarzeń może występować jednocześnie. Jeśli użytkownik zmieni rozmiar okna, zostanie to zainicjowane przez resize
, ale często też przez scroll
. Aby zwiększyć wydajność, unikaj wielokrotnego przetwarzania zmiany:
// 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
});
}
W związku z tym zgłosiłem problem ze specyfikacją, ponieważ uważam, że istnieje lepszy sposób, np. pojedyncze zdarzenie update
.
Moduł obsługi zdarzeń nie działa
Z powodu błędu w Chrome nie działa:
Wadliwe – używa modułu obsługi zdarzenia
visualViewport.onscroll = () => console.log('scroll!');
Zamiast tego:
Działa – używa detektora zdarzeń
visualViewport.addEventListener('scroll', () => console.log('scroll'));
Wartości przesunięcia są zaokrąglane
Myślę (a może nawet mam nadzieję), że to jeszcze jeden błąd w Chrome.
Wartości offsetLeft
i offsetTop
są zaokrąglone, co powoduje, że po powiększeniu obrazu są one niedokładne. Problemy z tym związane możesz zobaczyć podczas prezentacji – jeśli użytkownik powiększa i przesuwa mapę powoli, minimapa przeskakuje między nierozwiniętymi pikselami.
Zbyt wolna częstotliwość zdarzeń
Podobnie jak inne zdarzenia resize
i scroll
nie są one wywoływane w każdej klatce, zwłaszcza na urządzeniach mobilnych. Możesz to zobaczyć podczas prezentacji – gdy przybliżysz mapę, minimapa może nie pozostać zablokowana w widocznym obszarze.
Ułatwienia dostępu
W prezentacji użyłem visualViewport
, aby przeciwdziałać zbliżaniu przez użytkownika. W przypadku tego konkretnego demo ma to sens, ale zanim zrobisz coś, co zastąpi potrzebę użytkownika, aby powiększyć obraz, dobrze się zastanów.
visualViewport
może służyć do ułatwiania dostępu. Jeśli na przykład użytkownik przybliża widok, możesz ukryć ozdobne elementy position: fixed
, aby nie przeszkadzały użytkownikowi. Pamiętaj jednak, aby nie ukrywać czegoś, co użytkownik chce dokładniej obejrzeć.
Możesz rozważyć przesyłanie danych do usługi analitycznej, gdy użytkownik przybliży widok. Pomoże Ci to zidentyfikować strony, z którymi użytkownicy mają problemy przy domyślnym poziomie powiększenia.
visualViewport.addEventListener('resize', () => {
if (visualViewport.scale > 1) {
// Post data to analytics service
}
});
To wszystko. visualViewport
to przydatny interfejs API, który rozwiązuje problemy ze zgodnością.