पेश है विज़ुअलव्यूपोर्ट

जेक आर्चिबाल्ड
जेक आर्किबाल्ड

अगर मैं आपको बताऊँ, तो एक से ज़्यादा व्यूपोर्ट हैं.

BRRRRAAAAAAAMMMMMMMM

और जिस व्यूपोर्ट का इस्तेमाल अभी किया जा रहा है, वह असल में व्यूपोर्ट में एक व्यूपोर्ट है.

BRRRRAAAAAAAMMMMMMMM

कभी-कभी, DOM आपको जो डेटा देता है वह उनमें से किसी एक व्यूपोर्ट का इस्तेमाल करता है और दूसरे का नहीं.

BRRRAAAAM... कुछ इंतज़ार करो?

यह सच है, एक नज़र डालें:

लेआउट व्यूपोर्ट बनाम विज़ुअल व्यूपोर्ट

ऊपर दिया गया वीडियो, वेब पेज को स्क्रोल और पिंच-ज़ूम करके दिखाता है. साथ ही, दाईं ओर एक मिनी-मैप भी है जिसमें पेज के अंदर व्यूपोर्ट की जगह दिखती है.

नियमित स्क्रोलिंग के दौरान चीज़ें काफ़ी आसान होती हैं. हरा एरिया, लेआउट व्यूपोर्ट को दिखाता है, जिस पर position: fixed आइटम चिपके रहते हैं.

पिंच-ज़ूमिंग शुरू होने पर चीज़ें अजीब हो जाती हैं. लाल रंग का बॉक्स, विज़ुअल व्यूपोर्ट को दिखाता है, जो पेज का वह हिस्सा है जिसे हम असल में देख सकते हैं. यह व्यूपोर्ट वहीं इधर-उधर जा सकता है, जबकि position: fixed एलिमेंट वहीं पर रहते हैं जहां वे लेआउट व्यूपोर्ट से अटैच किए गए थे. अगर हम लेआउट व्यूपोर्ट की सीमा पर पैन करते हैं, तो इसके साथ लेआउट व्यूपोर्ट भी खींच लिया जाता है.

डिवाइसों के साथ काम करने की सुविधा को बेहतर बनाया जा रहा है

माफ़ करें, वेब एपीआई किस व्यूपोर्ट के बारे में बताते हैं, इसके साथ ही वे सभी ब्राउज़र पर भी एक जैसे नहीं होते.

उदाहरण के लिए, element.getBoundingClientRect().y, लेआउट व्यूपोर्ट में ऑफ़सेट दिखाता है. अच्छी बात है, लेकिन हम अक्सर पेज में ही जगह पाना चाहते हैं, इसलिए हम यह लिखते हैं:

element.getBoundingClientRect().y + window.scrollY

हालांकि, कई ब्राउज़र window.scrollY के लिए विज़ुअल व्यूपोर्ट का इस्तेमाल करते हैं. इसका मतलब है कि जब उपयोगकर्ता पिंच-ज़ूम करता है, तब ऊपर दिया गया कोड ब्रेक हो जाता है.

इसके बजाय, लेआउट व्यूपोर्ट को रेफ़र करने के लिए, Chrome 61 window.scrollY को बदल देता है. इसका मतलब है कि ऊपर दिया गया कोड, पिंच-ज़ूम करने पर भी काम करता है. असल में, लेआउट व्यूपोर्ट को रेफ़र करने के लिए ब्राउज़र, धीरे-धीरे सभी पोज़िशनल प्रॉपर्टी में बदलाव कर रहे हैं.

सिर्फ़ एक नई प्रॉपर्टी को छोड़कर...

विज़ुअल व्यूपोर्ट को स्क्रिप्ट के संपर्क में लाना

नया एपीआई, विज़ुअल व्यूपोर्ट को window.visualViewport के तौर पर दिखाता है. यह क्रॉस-ब्राउज़र मंज़ूरी के साथ एक ड्राफ़्ट की खास जानकारी है और इसे Chrome 61 पर ले जाया जा रहा है.

console.log(window.visualViewport.width);

window.visualViewport से हमें यह जानकारी मिलती है:

visualViewport प्रॉपर्टी
offsetLeft सीएसएस पिक्सल में विज़ुअल व्यूपोर्ट के बाएं किनारे और लेआउट व्यूपोर्ट के बीच की दूरी.
offsetTop सीएसएस पिक्सल में विज़ुअल व्यूपोर्ट के ऊपरी किनारे और लेआउट व्यूपोर्ट के बीच की दूरी.
pageLeft विज़ुअल व्यूपोर्ट के बाएं किनारे और दस्तावेज़ की बाईं सीमा के बीच की दूरी, सीएसएस पिक्सल में है.
pageTop विज़ुअल व्यूपोर्ट के ऊपरी किनारे और दस्तावेज़ की ऊपरी सीमा के बीच की दूरी, सीएसएस पिक्सल में.
width सीएसएस पिक्सल में विज़ुअल व्यूपोर्ट की चौड़ाई.
height सीएसएस पिक्सल में विज़ुअल व्यूपोर्ट की ऊंचाई.
scale पिंच-ज़ूमिंग द्वारा लागू किया गया पैमाना. अगर ज़ूम करने की वजह से कॉन्टेंट का साइज़ दोगुना है, तो नतीजे के तौर पर 2 दिखेगा. इस पर devicePixelRatio का कोई असर नहीं पड़ता.

यहां कुछ इवेंट भी दिए गए हैं:

window.visualViewport.addEventListener('resize', listener);
visualViewport इवेंट
resize width, height या scale में बदलाव होने पर सक्रिय होता है.
scroll offsetLeft या offsetTop में बदलाव होने पर सक्रिय होता है.

डेमो

इस लेख की शुरुआत में बनाया गया वीडियो visualViewport का इस्तेमाल करके बनाया गया था. इसे Chrome 61 के बाद के वर्शन पर देखें. इसमें visualViewport का इस्तेमाल करके, मिनी-मैप, विज़ुअल व्यूपोर्ट में सबसे ऊपर दाईं ओर चिपक जाता है. साथ ही, इसे इनवर्स स्केल लागू किया जाता है, ताकि पिंच करने के बावजूद भी इसका साइज़ एक जैसा रहे.

गॉचास

इवेंट सिर्फ़ तब सक्रिय होते हैं, जब विज़ुअल व्यूपोर्ट बदलता है

यह बात आम तौर पर कही जा सकती है, लेकिन जब मैंने पहली बार visualViewport के साथ खेला, तो मुझे पता चल गया.

अगर लेआउट व्यूपोर्ट का साइज़ बदल जाता है, लेकिन विज़ुअल व्यूपोर्ट का साइज़ नहीं बदलता, तो आपको resize इवेंट नहीं मिलेगा. हालांकि, लेआउट व्यूपोर्ट के लिए चौड़ाई/ऊंचाई को बदले बिना विज़ुअल व्यूपोर्ट का आकार बदलना असामान्य है.

असल में स्क्रीन पर स्क्रोल करना सही है. अगर स्क्रोलिंग होती है, लेकिन विज़ुअल व्यूपोर्ट स्टैटिक होता है, तो लेआउट व्यूपोर्ट के हिसाब से, तो आपको visualViewport पर scroll इवेंट नहीं मिलता. यह बहुत आम है. दस्तावेज़ को सामान्य स्क्रोल करने के दौरान, विज़ुअल व्यूपोर्ट, लेआउट व्यूपोर्ट में सबसे ऊपर बाईं ओर लॉक रहता है. इसलिए, visualViewport पर scroll फ़ायर नहीं होता.

अगर आपको विज़ुअल व्यूपोर्ट में हुए सभी बदलावों के बारे में जानना है, जिनमें 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 इवेंट.

इवेंट हैंडलर काम नहीं करते

Chrome में गड़बड़ी की वजह से, यह काम नहीं करता:

यह न करें

बगी – इवेंट हैंडलर का इस्तेमाल करता है

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 एक अच्छा एपीआई है. यह साथ काम करने से जुड़ी समस्याओं को हल करता है.