कैप्चर किए गए टैब को स्क्रोल और ज़ूम करें

François Beaufort
François Beaufort

स्क्रीन कैप्चर एपीआई की मदद से, वेब प्लैटफ़ॉर्म पर टैब, विंडो, और स्क्रीन शेयर की जा सकती हैं. जब कोई वेब ऐप्लिकेशन getDisplayMedia() को कॉल करता है, तो Chrome, उपयोगकर्ता को वेब ऐप्लिकेशन के साथ MediaStreamTrack वीडियो के तौर पर किसी टैब, विंडो या स्क्रीन को शेयर करने का अनुरोध करता है.

getDisplayMedia() का इस्तेमाल करने वाले कई वेब ऐप्लिकेशन, उपयोगकर्ता को कैप्चर किए गए प्लैटफ़ॉर्म की वीडियो की झलक दिखाते हैं. उदाहरण के लिए, वीडियो कॉन्फ़्रेंसिंग ऐप्लिकेशन अक्सर इस वीडियो को दूर रहने वाले उपयोगकर्ताओं को स्ट्रीम करता है. साथ ही, इसे स्थानीय HTMLVideoElement पर रेंडर भी करता है. इससे स्थानीय उपयोगकर्ता को शेयर की जा रही चीज़ों की झलक लगातार दिखती रहती है.

इस दस्तावेज़ में Chrome में नए कैप्चर किए गए सर्फ़ेस कंट्रोल एपीआई के बारे में जानकारी दी गई है. यह आपके वेब ऐप्लिकेशन को कैप्चर किए गए टैब को स्क्रोल करने और कैप्चर किए गए टैब के ज़ूम लेवल को पढ़ने और उसमें बदलाव करने की सुविधा देता है.

उपयोगकर्ता कैप्चर किए गए टैब (डेमो) को स्क्रोल और ज़ूम कर रहा है.

कैप्चर किए गए सरफ़ेस कंट्रोल का इस्तेमाल क्यों करना चाहिए?

वीडियो कॉन्फ़्रेंसिंग के सभी ऐप्लिकेशन में एक ही कमी होती है: अगर उपयोगकर्ता कैप्चर किए गए टैब या विंडो का इस्तेमाल करना चाहता है, तो उसे वीडियो कॉन्फ़्रेंसिंग ऐप्लिकेशन से दूर होते हुए उस प्लैटफ़ॉर्म पर स्विच करना होगा. इसमें कुछ चुनौतियां हैं:

  • उपयोगकर्ता तब तक कैप्चर किए गए ऐप्लिकेशन और दूर से मौजूद उपयोगकर्ताओं के वीडियो एक साथ नहीं देख सकता, जब तक कि वे वीडियो कॉन्फ़्रेंस टैब और शेयर किए गए टैब के लिए पिक्चर में पिक्चर या अलग-अलग साइड-बाय-साइड विंडो का इस्तेमाल नहीं करते. छोटी स्क्रीन पर, यह काम करना मुश्किल हो सकता है.
  • उपयोगकर्ता को वीडियो कॉन्फ़्रेंसिंग ऐप्लिकेशन से कैप्चर किए गए प्लैटफ़ॉर्म के बीच स्विच करने की प्रक्रिया का बोझ ज़्यादा होता है.
  • वीडियो कॉन्फ़्रेंसिंग ऐप्लिकेशन से दूर रहने पर, उपयोगकर्ता के पास उन कंट्रोल का ऐक्सेस नहीं रहेगा जो वीडियो कॉन्फ़्रेंसिंग ऐप्लिकेशन के ज़रिए दिखाए गए हैं. उदाहरण के लिए, एम्बेड किया गया चैट ऐप्लिकेशन, इमोजी से प्रतिक्रिया देना, कॉल में शामिल होने के लिए उपयोगकर्ताओं के अनुरोध की सूचना, मल्टीमीडिया और लेआउट कंट्रोल, और वीडियो कॉन्फ़्रेंसिंग की अन्य ज़रूरी सुविधाएं.
  • प्रज़ेंटर, रिमोट तरीके से मीटिंग में हिस्सा लेने वाले लोगों को कंट्रोल नहीं दे सकता. इसकी वजह से बहुत ज़्यादा जाना-पहचाना स्थिति बनती है. इसमें रिमोट उपयोगकर्ता, प्रज़ेंटर से स्लाइड बदलने, थोड़ा ऊपर और नीचे स्क्रोल करने या ज़ूम लेवल को अडजस्ट करने के लिए कहते हैं.

कैप्चर किए गए सरफ़ेस कंट्रोल एपीआई से इन समस्याओं को हल किया जाता है.

मैं कैप्चर किए गए सरफ़ेस कंट्रोल का इस्तेमाल कैसे करूं?

कैप्चर किए गए सरफ़ेस कंट्रोल का इस्तेमाल करने के लिए, आपको कुछ चरणों को पूरा करना होगा. उदाहरण के लिए, साफ़ तौर पर ब्राउज़र टैब कैप्चर करना और कैप्चर किए गए टैब को स्क्रोल और ज़ूम करने से पहले, उपयोगकर्ता से अनुमति लेना.

ब्राउज़र टैब कैप्चर करें

सबसे पहले, उपयोगकर्ता को getDisplayMedia() का इस्तेमाल करके, शेयर करने के लिए कोई प्लैटफ़ॉर्म चुनने के लिए कहें. इसके बाद, कैप्चर सेशन के साथ CaptureController ऑब्जेक्ट जोड़ें. हम कैप्चर की गई सतह को जल्द ही कंट्रोल करने के लिए, उस ऑब्जेक्ट का इस्तेमाल करेंगे.

const controller = new CaptureController();
const stream = await navigator.mediaDevices.getDisplayMedia({ controller });

इसके बाद, कैप्चर किए गए प्लैटफ़ॉर्म की स्थानीय झलक, <video> एलिमेंट के तौर पर बनाएं:

const previewTile = document.querySelector('video');
previewTile.srcObject = stream;

अगर उपयोगकर्ता ने कोई विंडो या स्क्रीन शेयर करने का विकल्प चुना है, तो वह इस दायरे से बाहर है. हालांकि, अगर वह टैब शेयर करता है, तो हम कार्रवाई कर सकते हैं.

const [track] = stream.getVideoTracks();

if (track.getSettings().displaySurface !== 'browser') {
  // Bail out early if the user didn't pick a tab.
  return;
}

अनुमति का प्रॉम्प्ट

किसी दिए गए CaptureController ऑब्जेक्ट पर, sendWheel() या setZoomLevel() को पहली बार शुरू करने पर, अनुमति का प्रॉम्प्ट दिखता है. अगर उपयोगकर्ता अनुमति देता है, तो उस CaptureController ऑब्जेक्ट पर इन तरीकों को इस्तेमाल करने की अनुमति मिल जाती है. अगर उपयोगकर्ता अनुमति से इनकार करता है, तो लौटाए गए प्रॉमिस को अस्वीकार कर दिया जाता है.

ध्यान दें कि CaptureController ऑब्जेक्ट किसी खास capture-session से यूनीक तौर पर जुड़े होते हैं. साथ ही, इन्हें किसी दूसरे कैप्चर-सेशन के साथ नहीं जोड़ा जा सकता. साथ ही, ये उस पेज के नेविगेशन में भी नहीं बचते जहां उन्हें तय किया गया है. हालांकि, कैप्चर-सेशन कैप्चर किए गए पेज के नेविगेशन में बचे रहते हैं.

लोगों को अनुमति का प्रॉम्प्ट दिखाने के लिए, हाथ के जेस्चर की ज़रूरत होती है. सिर्फ़ sendWheel() और setZoomLevel() कॉल के लिए, उपयोगकर्ता के जेस्चर की ज़रूरत होती है. ऐसा सिर्फ़ तब होता है, जब प्रॉम्प्ट दिखाने की ज़रूरत हो. अगर कोई उपयोगकर्ता, वेब ऐप्लिकेशन में ज़ूम-इन या ज़ूम-आउट करने वाले बटन पर क्लिक करता है, तो उसे उपयोगकर्ता के लिए जेस्चर (हाव-भाव) दिया जाता है; अगर ऐप्लिकेशन को पहले स्क्रोल-कंट्रोल की सुविधा देनी है, तो डेवलपर को यह ध्यान रखना चाहिए कि स्क्रोल करने से उपयोगकर्ता का जेस्चर नहीं बनता. एक संभावना यह है कि पहले उपयोगकर्ता को "स्क्रोल करना शुरू करें" दिया जाए बटन पर क्लिक करें, जैसा कि नीचे दिए गए उदाहरण में बताया गया है:

const startScrollingButton = document.querySelector('button');

startScrollingButton.addEventListener('click', async () => {
  try {
    const noOpWheelAction = {};

    await controller.sendWheel(noOpWheelAction);
    // The user approved the permission prompt.
    // You can now scroll and zoom the captured tab as shown later in the article.
  } catch (error) {
    return; // Permission denied. Bail.
  }
});

स्क्रोल करें

sendWheel() का इस्तेमाल करके, कैप्चर करने वाला कोई ऐप्लिकेशन, टैब के व्यूपोर्ट में अपनी पसंद के निर्देशांक पर, चुने गए किसी भी तीव्रता के व्हील इवेंट को डिलीवर कर सकता है. कैप्चर किए गए ऐप्लिकेशन और प्रत्यक्ष उपयोगकर्ता इंटरैक्शन से इवेंट अलग से नहीं पहचाना जा सकता.

मान लें कि कैप्चर करने वाले ऐप्लिकेशन में "previewTile" नाम का एक <video> एलिमेंट इस्तेमाल किया जाता है, तो नीचे दिया गया कोड दिखाता है कि कैप्चर किए गए टैब में व्हील इवेंट भेजने का तरीका कैसे रिले किया जा सकता है:

const previewTile = document.querySelector('video');

previewTile.addEventListener('wheel', async (event) => {
  // Translate the offsets into coordinates which sendWheel() can understand.
  // The implementation of this translation is explained further below.
  const [x, y] = translateCoordinates(event.offsetX, event.offsetY);
  const [wheelDeltaX, wheelDeltaY] = [-event.deltaX, -event.deltaY];

  try {
    // Relay the user's action to the captured tab.
    await controller.sendWheel({ x, y, wheelDeltaX, wheelDeltaY });
  } catch (error) {
    // Inspect the error.
    // ...
  }
});

तरीका sendWheel(), वैल्यू के दो सेट वाली डिक्शनरी का इस्तेमाल करता है:

  • x और y: वे निर्देशांक, जहां व्हील इवेंट डिलीवर करना है.
  • wheelDeltaX और wheelDeltaY: हॉरिज़ॉन्टल और वर्टिकल स्क्रोल के लिए पिक्सल में स्क्रोल के माप. ध्यान दें कि ये वैल्यू, ओरिजनल व्हील इवेंट की तुलना में इन्वर्टेड होती हैं.

translateCoordinates() को लागू किया जा सकता है:

function translateCoordinates(offsetX, offsetY) {
  const previewDimensions = previewTile.getBoundingClientRect();
  const trackSettings = previewTile.srcObject.getVideoTracks()[0].getSettings();

  const x = trackSettings.width * offsetX / previewDimensions.width;
  const y = trackSettings.height * offsetY / previewDimensions.height;

  return [Math.floor(x), Math.floor(y)];
}

ध्यान दें कि कोड में शुरू होने वाले तीन अलग-अलग आकार होते हैं:

  • <video> एलिमेंट का साइज़.
  • कैप्चर किए गए फ़्रेम का साइज़ (इसे यहां trackSettings.width और trackSettings.height के तौर पर दिखाया गया है).
  • टैब का साइज़.

<video> एलिमेंट का साइज़, पूरी तरह से कैप्चर करने वाले ऐप्लिकेशन के डोमेन के अंदर होता है. साथ ही, ब्राउज़र को इसके बारे में जानकारी नहीं होती है. टैब का साइज़ पूरी तरह से ब्राउज़र के डोमेन में होता है और वेब ऐप्लिकेशन के पास इसकी जानकारी नहीं होती.

वेब ऐप्लिकेशन translateCoordinates() का इस्तेमाल करके, <video> एलिमेंट से जुड़े ऑफ़सेट को वीडियो ट्रैक के निर्देशांक स्पेस में निर्देशांक में अनुवाद करता है. इसी तरह ब्राउज़र, कैप्चर किए गए फ़्रेम के साइज़ और टैब के साइज़ के बीच अनुवाद करेगा और स्क्रोल इवेंट को वेब ऐप्लिकेशन की उम्मीद के हिसाब से ऑफ़सेट पर डिलीवर करेगा.

sendWheel() के ज़रिए वापस किए गए प्रॉमिस को इन मामलों में अस्वीकार किया जा सकता है:

  • अगर कैप्चर सेशन अब तक शुरू नहीं हुआ है या बंद हो गया है. इसमें, ब्राउज़र से sendWheel() कार्रवाई को मैनेज करने के दौरान, एसिंक्रोनस तरीके से बंद करना भी शामिल है.
  • अगर उपयोगकर्ता ने ऐप्लिकेशन को sendWheel() का इस्तेमाल करने की अनुमति नहीं दी है.
  • अगर कैप्चर करने वाला ऐप्लिकेशन, [trackSettings.width, trackSettings.height] के बाहर के निर्देशांकों में स्क्रोल इवेंट डिलीवर करने की कोशिश करता है. ध्यान दें कि ये वैल्यू एसिंक्रोनस रूप से बदल सकती हैं, इसलिए बेहतर होगा कि गड़बड़ी को पकड़ लिया जाए और उसे अनदेखा कर दिया जाए. (ध्यान दें कि 0, 0 आम तौर पर सीमाओं से बाहर नहीं होता, इसलिए उपयोगकर्ता से अनुमति लेने के लिए इनका इस्तेमाल करना सुरक्षित है.)

ज़ूम करें

कैप्चर किए गए टैब के ज़ूम लेवल के साथ इंटरैक्ट करने के लिए, इन CaptureController प्लैटफ़ॉर्म का इस्तेमाल किया जाता है:

  • getSupportedZoomLevels(), ब्राउज़र पर काम करने वाले ज़ूम लेवल की सूची दिखाता है. इसे "डिफ़ॉल्ट ज़ूम लेवल" के प्रतिशत के तौर पर दिखाया जाता है. इसे 100% तय किया जाता है. यह सूची अचानक बढ़ रही है और इसका मान 100 है.
  • getZoomLevel(), टैब का मौजूदा ज़ूम लेवल दिखाता है.
  • setZoomLevel(), टैब के ज़ूम लेवल को getSupportedZoomLevels() में मौजूद किसी भी पूर्णांक वैल्यू पर सेट करता है और इसके पूरा होने पर प्रॉमिस दिखाता है. ध्यान दें कि कैप्चर सत्र के अंत में ज़ूम स्तर रीसेट नहीं होता है.
  • जब उपयोगकर्ता कैप्चर करने वाले ऐप्लिकेशन के ज़रिए या कैप्चर किए गए टैब के साथ सीधे तौर पर इंटरैक्शन करके, ज़ूम लेवल बदल सकते हैं. ऐसे में oncapturedzoomlevelchange, आपको कैप्चर किए गए टैब के ज़ूम लेवल में होने वाले बदलावों को सुनने की सुविधा देता है.

setZoomLevel() पर किए जाने वाले कॉल के लिए अनुमति लेना ज़रूरी है; किसी दूसरे कॉल, रीड-ओनली ज़ूम की सुविधा, "मुफ़्त" होती है. यह सुविधा, इवेंट के डेटा की तरह ही काम करती है.

यहां दिए गए उदाहरण में, किसी मौजूदा कैप्चर सेशन में कैप्चर किए गए टैब के ज़ूम लेवल को बढ़ाने का तरीका बताया गया है:

const zoomIncreaseButton = document.getElementById('zoomInButton');

zoomIncreaseButton.addEventListener('click', async (event) => {
  const levels = CaptureController.getSupportedZoomLevels();
  const index = levels.indexOf(controller.getZoomLevel());
  const newZoomLevel = levels[Math.min(index + 1, levels.length - 1)];

  try {
    await controller.setZoomLevel(newZoomLevel);
  } catch (error) {
    // Inspect the error.
    // ...
  }
});

यहां दिए गए उदाहरण में, कैप्चर किए गए टैब के ज़ूम लेवल में हुए बदलावों पर प्रतिक्रिया देने का तरीका बताया गया है:

controller.addEventListener('capturedzoomlevelchange', (event) => {
  const zoomLevel = controller.getZoomLevel();
  document.querySelector('#zoomLevelLabel').textContent = `${zoomLevel}%`;
});

सुविधा की पहचान

यह देखने के लिए कि व्हील इवेंट भेजने की सुविधा उपलब्ध है या नहीं, इसका इस्तेमाल करें:

if (!!window.CaptureController?.prototype.sendWheel) {
  // CaptureController sendWheel() is supported.
}

अगर आपको जानना है कि ज़ूम को कंट्रोल करने की सुविधा काम करती है या नहीं, तो इन विकल्पों का इस्तेमाल करें:

if (!!window.CaptureController?.prototype.setZoomLevel) {
  // CaptureController setZoomLevel() is supported.
}

कैप्चर किए गए सरफ़ेस कंट्रोल को चालू करें

कैप्चर किए गए Surface Control API को Chrome में, डेस्कटॉप पर कैप्चर किए गए सरफ़ेस कंट्रोल फ़्लैग की मदद से देखा जा सकता है. इसे chrome://flags/#captured-surface-control पर चालू किया जा सकता है.

यह सुविधा डेस्कटॉप पर, Chrome 122 के लिए ऑरिजिन ट्रायल शुरू कर रही है. इससे डेवलपर अपनी साइट पर आने वाले लोगों के लिए, इस सुविधा को चालू कर सकते हैं, ताकि वे असल उपयोगकर्ताओं का डेटा इकट्ठा कर सकें. ऑरिजिन ट्रायल और उनके काम करने के तरीके के बारे में ज़्यादा जानकारी के लिए, ऑरिजिन ट्रायल का इस्तेमाल शुरू करना लेख पढ़ें.

सुरक्षा और निजता

"captured-surface-control" अनुमति की नीति की मदद से, यह मैनेज किया जा सकता है कि कैप्चर किए गए ऐप्लिकेशन और एम्बेड किए गए तीसरे पक्ष के iframe के पास, कैप्चर किए गए सरफ़ेस कंट्रोल का ऐक्सेस कैसे हो. सुरक्षा से जुड़े खतरों को समझने के लिए, कैप्चर किए गए सरफ़ेस कंट्रोल के बारे में जानकारी देने वाले पेज पर निजता और सुरक्षा से जुड़ी ज़रूरी बातें सेक्शन देखें.

डेमो

Glitch पर डेमो चलाकर, कैप्चर किए गए सरफ़ेस कंट्रोल का इस्तेमाल किया जा सकता है. सोर्स कोड की जांच करना न भूलें.

Chrome के पिछले वर्शन से बदलाव

यहां कैप्चर किए गए सरफ़ेस कंट्रोल के व्यवहार में कुछ मुख्य अंतर दिए गए हैं, जिनके बारे में आपको पता होना चाहिए:

  • Chrome 124 और उससे पहले के वर्शन में:
    • अगर अनुमति दी जाती है, तो इसका दायरा उस CaptureController से जुड़े कैप्चर सेशन तक सीमित होता है, न कि कैप्चर करने वाले ऑरिजिन तक.
  • Chrome 122 में:
    • getZoomLevel(), टैब के मौजूदा ज़ूम लेवल के साथ प्रॉमिस देता है.
    • अगर उपयोगकर्ता ने ऐप्लिकेशन को इस्तेमाल करने की अनुमति नहीं दी है, तो sendWheel() गड़बड़ी के मैसेज "No permission." के साथ प्रॉमिस को अस्वीकार कर देता है. Chrome 123 और उसके बाद के वर्शन में गड़बड़ी का टाइप "NotAllowedError" है.
    • oncapturedzoomlevelchange उपलब्ध नहीं है. setInterval() का इस्तेमाल करके, इस सुविधा को पॉलीफ़िल किया जा सकता है.

सुझाव/राय दें या शिकायत करें

Chrome टीम और वेब स्टैंडर्ड की कम्यूनिटी, कैप्चर किए गए सरफ़ेस कंट्रोल के अनुभव के बारे में जानना चाहती है.

हमें डिज़ाइन के बारे में बताएं

क्या कैप्चर किए गए सरफ़ेस कैप्चर के बारे में कुछ ऐसा है जो आपकी उम्मीद के मुताबिक काम नहीं करता? या फिर कुछ ऐसे तरीके या प्रॉपर्टी हैं जिन पर आपको अपने आइडिया को लागू करने की ज़रूरत है? क्या आपके पास सुरक्षा मॉडल से जुड़ा कोई सवाल या टिप्पणी है? GitHub रेपो में, स्पेसिफ़िकेशन की समस्या दर्ज करें या किसी मौजूदा समस्या के बारे में अपनी राय दें.

क्या लागू करने में कोई समस्या हुई?

क्या आपको Chrome को लागू करने में कोई गड़बड़ी मिली? या क्या लागू करने का तरीका, स्पेसिफ़िकेशन से अलग है? https://new.crbug.com पर जाकर, गड़बड़ी की शिकायत करें. इसमें ज़्यादा से ज़्यादा जानकारी शामिल करें और इसे दोबारा बनाने के निर्देश भी दें. Glitch, पैदा की जा सकने वाली गड़बड़ियों को शेयर करने के लिए बेहतरीन काम करता है.