معرفی visualViewport

اگر به شما بگویم چه می‌شود، بیش از یک ویوپورت وجود دارد.

BRRRRAAAAAAAMMMMMMMMMMMM

و نمایی که در حال حاضر از آن استفاده می کنید، در واقع یک پورت دید در یک ویوپورت است.

BRRRRAAAAAAAMMMMMMMMMMMM

و گاهی اوقات، داده‌هایی که 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 کوچک خوب است که مشکلات سازگاری را در طول مسیر حل می کند.