اگر به شما بگویم چه میشود، بیش از یک ویوپورت وجود دارد.
BRRRRAAAAAAAMMMMMMMMMM
و نمایی که در حال حاضر از آن استفاده می کنید، در واقع یک پورت دید در یک ویوپورت است.
BRRRRAAAAAAAMMMMMMMMMM
و گاهی اوقات، دادههایی که DOM به شما میدهد، به یکی از آن ویوپورتها اشاره دارد و نه دیگری.
BRRRRAAAAM… صبر کن چی؟
درست است، نگاه کنید:
نمای چیدمان در مقابل نمای دید بصری
ویدئوی بالا یک صفحه وب را در حال پیمایش و کوچکنمایی به همراه یک نقشه کوچک در سمت راست نشان میدهد که موقعیت پورتهای دید را در داخل صفحه نشان میدهد.
در حین پیمایش منظم همه چیز کاملاً رو به جلو است. ناحیه سبز نمایانگر نمای طرح است که position: fixed
به آن می چسبند.
وقتی که زوم کردن کوچک (pinch-zooming) معرفی می شود، همه چیز عجیب می شود. کادر قرمز نمایانگر نمای بصری است که بخشی از صفحه است که در واقع می توانیم ببینیم. این نما می تواند در حالی که position: fixed
در جایی که بودند، متصل به نمای طرح بندی باقی می مانند. اگر در مرزی از نمای layout حرکت کنیم، نمای layout را نیز به همراه خود می کشد.
بهبود سازگاری
متأسفانه وب APIها از نظر دیدگاهی که به آن ارجاع می دهند ناسازگار هستند و همچنین در بین مرورگرها ناسازگار هستند.
برای مثال، element.getBoundingClientRect().y
افست را در نمای layout برمی گرداند. جالب است، اما ما اغلب موقعیت را در صفحه می خواهیم، بنابراین می نویسیم:
element.getBoundingClientRect().y + window.scrollY
با این حال، بسیاری از مرورگرها از viewport بصری برای window.scrollY
استفاده میکنند، به این معنی که وقتی کاربر زوم میکند، کد بالا شکسته میشود.
Chrome 61 window.scrollY
را تغییر میدهد تا در عوض به نمای طرحبندی اشاره کند، به این معنی که کد بالا حتی در صورت کوچکنمایی نیز کار میکند. در واقع، مرورگرها به آرامی تمام خصوصیات موقعیتی را برای ارجاع به نمای layout تغییر می دهند.
به استثنای یک ملک جدید…
قرار دادن نمای بصری در معرض اسکریپت
یک API جدید نمای بصری را به عنوان window.visualViewport
نشان میدهد. این یک مشخصات پیشنویس است، با تأیید بین مرورگرها ، و در Chrome 61 فرود میآید.
console.log(window.visualViewport.width);
در اینجا چیزی است که window.visualViewport
به ما می دهد:
ویژگی های visualViewport | |
---|---|
offsetLeft | فاصله بین لبه سمت چپ نمای بصری و نمای چیدمان در پیکسل های CSS. |
offsetTop | فاصله بین لبه بالای نمای بصری و نمای چیدمان در پیکسل های CSS. |
pageLeft | فاصله بین لبه سمت چپ نمای بصری، و مرز سمت چپ سند، در پیکسل های CSS. |
pageTop | فاصله بین لبه بالای نمای بصری، و مرز بالای سند، در پیکسل های CSS. |
width | عرض نمای بصری در پیکسل های CSS. |
height | ارتفاع نمای بصری در پیکسل های CSS. |
scale | مقیاس اعمال شده با زوم کردن کوچک. اگر محتوا به دلیل بزرگنمایی دو برابر اندازه باشد، این مقدار 2 برمی گردد. devicePixelRatio بر این موضوع تأثیری ندارد. |
همچنین چند رویداد وجود دارد:
window.visualViewport.addEventListener('resize', listener);
رویدادهای visualViewport | |
---|---|
resize | هنگامی که width ، height یا scale تغییر می کند، شلیک می شود. |
scroll | هنگامی که offsetLeft یا offsetTop تغییر می کند فعال می شود. |
نسخه ی نمایشی
ویدیوی ابتدای این مقاله با استفاده از visualViewport
ایجاد شده است، آن را در Chrome 61+ بررسی کنید . از visualViewport
برای چسباندن مینی نقشه به سمت راست بالای نمای بصری استفاده میکند، و یک مقیاس معکوس اعمال میکند تا همیشه یک اندازه به نظر برسد، علیرغم زوم کردن کوچک.
گوچاس
رویدادها فقط زمانی فعال می شوند که نمای بصری تغییر کند
به نظر یک چیز واضح است، اما وقتی برای اولین بار با visualViewport
بازی کردم، مرا جلب کرد.
اگر اندازه نمای طرح بندی تغییر کند اما نمای بصری تغییر اندازه ندهد، رویداد resize
دریافت نمی کنید. با این حال، تغییر اندازه درگاه نمای طرح بدون تغییر عرض/ارتفاع، غیرعادی است.
گوچا واقعی در حال پیمایش است. اگر پیمایش اتفاق بیفتد، اما نمای بصری نسبت به نمای چیدمان ثابت بماند، در visualViewport
یک رویداد scroll
دریافت نمیکنید، و این واقعاً رایج است. در طول پیمایش معمولی سند، درگاه دید بصری در سمت چپ بالای نمای طرح بندی قفل می شود، بنابراین scroll
در visualViewport
فعال نمی شود .
اگر میخواهید در مورد همه تغییرات در نمای بصری، از جمله pageTop
و pageLeft
بشنوید، باید به رویداد پیمایش پنجره نیز گوش دهید:
visualViewport.addEventListener('scroll', update);
visualViewport.addEventListener('resize', update);
window.addEventListener('scroll', update);
از تکرار کار با چندین شنونده خودداری کنید
مانند گوش دادن به scroll
و resize
در پنجره، احتمالاً در نتیجه نوعی تابع "به روز رسانی" را فراخوانی می کنید. با این حال، معمول است که بسیاری از این رویدادها همزمان اتفاق بیفتند. اگر کاربر اندازه پنجره را تغییر دهد، resize
فعال میکند، اما اغلب اوقات نیز scroll
. برای بهبود عملکرد، از انجام چندین بار تغییر اجتناب کنید:
// 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
});
}
من یک مشکل مشخصات را برای این ثبت کرده ام ، زیرا فکر می کنم ممکن است راه بهتری وجود داشته باشد، مانند یک رویداد update
واحد.
کنترل کننده های رویداد کار نمی کنند
به دلیل یک اشکال کروم ، این کار نمی کند :
Buggy - از یک کنترل کننده رویداد استفاده می کند
visualViewport.onscroll = () => console.log('scroll!');
در عوض:
کار می کند - از شنونده رویداد استفاده می کند
visualViewport.addEventListener('scroll', () => console.log('scroll'));
مقادیر افست گرد می شوند
فکر میکنم (خوب، امیدوارم) این یکی دیگر از باگهای Chrome باشد.
offsetLeft
و offsetTop
گرد هستند، که پس از بزرگنمایی کاربر بسیار نادرست است. شما می توانید مشکلات مربوط به آن را در حین نمایش مشاهده کنید - اگر کاربر بزرگنمایی کند و به آرامی حرکت کند، مینی نقشه بین پیکسل های بزرگنمایی نشده قرار می گیرد .
نرخ رویداد کند است
مانند سایر رویدادهای resize
و scroll
، این نه همه فریمها را فعال نمیکند، به خصوص در موبایل. شما می توانید این را در حین نمایش نمایشی ببینید - وقتی بزرگنمایی را فشار دهید، مینی نقشه در قفل ماندن در درگاه دید مشکل دارد.
دسترسی
در نسخه ی نمایشی visualViewport
برای خنثی کردن زوم کاربر استفاده کردم. برای این نسخه نمایشی خاص منطقی است، اما قبل از انجام هر کاری که تمایل کاربر به بزرگنمایی را نادیده می گیرد، باید به دقت فکر کنید.
visualViewport
می توان برای بهبود دسترسی استفاده کرد. برای مثال، اگر کاربر در حال بزرگنمایی است، میتوانید position: fixed
، تا آنها را از سر راه کاربر دور کنید. اما باز هم، مراقب باشید چیزی را که کاربر سعی میکند نگاه دقیقتری به آن داشته باشد، پنهان نکنید.
میتوانید زمانی که کاربر بزرگنمایی میکند، به یک سرویس تجزیه و تحلیل پست ارسال کنید. این میتواند به شما کمک کند صفحاتی را که کاربران با آنها در سطح پیشفرض بزرگنمایی مشکل دارند، شناسایی کنید.
visualViewport.addEventListener('resize', () => {
if (visualViewport.scale > 1) {
// Post data to analytics service
}
});
و بس! visualViewport
یک API کوچک خوب است که مشکلات سازگاری را در طول مسیر حل می کند.