Bagaimana jika saya katakan, ada lebih dari satu area pandang.
BRRRRAAAAAAAMMMMMMMMMM
Dan area pandang yang Anda gunakan saat ini sebenarnya adalah area pandang dalam area pandang.
BRRRRAAAAAAAMMMMMMMMMM
Dan terkadang, data yang diberikan DOM kepada Anda merujuk ke salah satu dari area pandang tersebut, bukan yang lainnya.
BRRRRAAAAM... tunggu apa?
Benar, lihat:
Area pandang tata letak vs area pandang visual
Video di atas menampilkan halaman web yang di-scroll dan di-zoom, bersama dengan peta mini di sebelah kanan yang menampilkan posisi area pandang dalam halaman.
Semuanya berjalan cukup lancar selama scrolling reguler. Area hijau
mewakili area pandang tata letak, tempat item position: fixed
melekat.
Keadaan menjadi aneh ketika fungsi cubit-zoom diperkenalkan. Kotak merah mewakili
area pandang visual, yang merupakan bagian dari halaman yang benar-benar dapat kita lihat. Area pandang
ini dapat berpindah-pindah sementara elemen position: fixed
tetap berada di tempatnya, yang melekat pada area pandang tata letak. Jika kita menggeser pada batas area pandang
tata letak, kita akan menyeret area pandang tata letak bersamanya.
Meningkatkan kompatibilitas
Sayangnya, web API tidak konsisten dalam hal area pandang yang dirujuk, dan juga tidak konsisten di seluruh browser.
Misalnya, element.getBoundingClientRect().y
menampilkan offset dalam
area pandang tata letak. Memang keren, tetapi kita sering menginginkan posisi di dalam halaman,
jadi kita menulis:
element.getBoundingClientRect().y + window.scrollY
Namun, banyak browser menggunakan area pandang visual untuk window.scrollY
, yang berarti
kode di atas rusak saat pengguna melakukan gerakan cubit-zoom.
Chrome 61 mengubah window.scrollY
untuk merujuk ke area pandang tata letak,
yang berarti kode di atas berfungsi bahkan saat dilakukan cubit. Bahkan, browser
secara perlahan mengubah semua properti posisi untuk merujuk ke area pandang tata letak.
Kecuali satu properti baru...
Mengekspos area pandang visual ke skrip
API baru mengekspos area pandang visual sebagai window.visualViewport
. Ini adalah spesifikasi draf, dengan persetujuan lintas browser, dan diluncurkan di Chrome 61.
console.log(window.visualViewport.width);
Inilah yang diberikan window.visualViewport
kepada kita:
visualViewport properti |
|
---|---|
offsetLeft
|
Jarak antara tepi kiri area pandang visual, dan area pandang tata letak, dalam piksel CSS. |
offsetTop
|
Jarak antara tepi atas area pandang visual, dan area pandang tata letak, dalam piksel CSS. |
pageLeft
|
Jarak antara tepi kiri area pandang visual, dan batas kiri dokumen, dalam piksel CSS. |
pageTop
|
Jarak antara tepi atas area pandang visual, dan batas atas dokumen, dalam piksel CSS. |
width
|
Lebar area pandang visual dalam piksel CSS. |
height
|
Tinggi area pandang visual dalam piksel CSS. |
scale
|
Skala yang diterapkan dengan gerakan cubit. Jika ukuran konten dua kali lipat karena di-zoom, ini akan menampilkan 2 . Hal ini tidak terpengaruh oleh
devicePixelRatio .
|
Ada juga beberapa peristiwa:
window.visualViewport.addEventListener('resize', listener);
visualViewport peristiwa |
|
---|---|
resize
|
Diaktifkan saat width , height , atau scale berubah.
|
scroll
|
Diaktifkan saat offsetLeft atau offsetTop berubah.
|
Demo
Video di awal artikel ini dibuat menggunakan visualViewport
,
lihat di Chrome 61+. Video ini menggunakan
visualViewport
untuk membuat peta mini tetap berada di kanan atas area pandang
visual, dan menerapkan skala terbalik agar selalu muncul ukuran yang sama,
meskipun dilakukan gerakan cubit.
Gotcha
Peristiwa hanya diaktifkan saat area pandang visual berubah
Rasanya seperti hal yang sudah jelas untuk dinyatakan, tetapi hal itu tidak jelas terlihat saat saya pertama kali
bermain dengan visualViewport
.
Jika area pandang tata letak berubah ukuran, tetapi area pandang visual tidak, Anda tidak akan mendapatkan
peristiwa resize
. Namun, tidak biasa jika area pandang tata letak berubah ukuran tanpa
area pandang visual yang juga mengubah lebar/tinggi.
Gotcha yang sesungguhnya adalah men-scroll. Jika scroll terjadi, tetapi area pandang visual
tetap statis relatif terhadap area pandang tata letak, Anda tidak akan mendapatkan peristiwa scroll
di visualViewport
, dan ini sangat umum. Selama scroll dokumen
reguler, area pandang visual tetap terkunci di kiri atas area pandang tata letak, sehingga scroll
tidak diaktifkan pada visualViewport
.
Jika ingin mengetahui semua perubahan pada area pandang visual, termasuk
pageTop
dan pageLeft
, Anda juga harus memproses peristiwa scroll
jendela:
visualViewport.addEventListener('scroll', update);
visualViewport.addEventListener('resize', update);
window.addEventListener('scroll', update);
Menghindari duplikasi pekerjaan dengan beberapa pemroses
Mirip dengan memproses scroll
& resize
di jendela, Anda mungkin akan memanggil
semacam fungsi "update" sebagai hasilnya. Namun, hal ini umum terjadi pada
waktu yang bersamaan. Jika pengguna mengubah ukuran jendela, jendela akan
memicu resize
, tetapi sering kali scroll
juga. Untuk meningkatkan performa, hindari
menangani perubahan beberapa kali:
// 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
});
}
Saya telah mengajukan masalah spesifikasi untuk hal ini, karena sepertinya ada
cara yang lebih baik, misalnya satu peristiwa update
.
Pengendali peristiwa tidak berfungsi
Karena bug Chrome, hal berikut tidak berfungsi:
Buggy – menggunakan pengendali peristiwa
visualViewport.onscroll = () => console.log('scroll!');
Sebagai gantinya:
Berfungsi – menggunakan pemroses peristiwa
visualViewport.addEventListener('scroll', () => console.log('scroll'));
Nilai offset dibulatkan
Sepertinya (baik, semoga) ini adalah bug Chrome lainnya.
offsetLeft
dan offsetTop
dibulatkan, yang cukup tidak akurat setelah
pengguna memperbesarnya. Anda dapat melihat masalahnya selama demo – jika pengguna memperbesar dan menggeser
perlahan, peta mini akan bergeser di antara piksel yang tidak diperbesar.
Rasio peristiwa lambat
Seperti peristiwa resize
dan scroll
lainnya, peristiwa ini tidak mengaktifkan setiap frame,
terutama di perangkat seluler. Anda dapat melihatnya selama demo – setelah Anda mencubit zoom, peta mini kesulitan untuk tetap terkunci di area pandang.
Aksesibilitas
Dalam demo saya menggunakan visualViewport
untuk
menentang tindakan cubit-zoom pengguna. Demo ini memang masuk akal, tetapi
Anda harus berpikir dengan hati-hati sebelum melakukan apa pun yang menggantikan keinginan
pengguna untuk memperbesar.
visualViewport
dapat digunakan untuk meningkatkan aksesibilitas. Misalnya, jika pengguna
memperbesar tampilan, Anda dapat memilih untuk menyembunyikan item position: fixed
dekoratif agar
pengguna tidak mengganggunya. Tapi sekali lagi, berhati-hatilah agar Anda tidak menyembunyikan
sesuatu yang ingin dilihat pengguna lebih dekat.
Anda dapat mempertimbangkan untuk memposting ke layanan analisis saat pengguna memperbesarnya. Metode ini dapat membantu Anda mengidentifikasi halaman yang sulit diakses pengguna pada tingkat zoom default.
visualViewport.addEventListener('resize', () => {
if (visualViewport.scale > 1) {
// Post data to analytics service
}
});
Dan selesai! visualViewport
adalah API kecil yang bagus yang memecahkan masalah
kompatibilitas selama proses berlangsung.