تمرير علامة تبويب تم التقاطها وتكبيرها/تصغيرها

François Beaufort
François Beaufort

يمكن حاليًا مشاركة علامات التبويب والنوافذ والشاشات على منصة الويب باستخدام Screen Capture API. عندما يطلب تطبيق ويب getDisplayMedia()، يطلب Chrome من المستخدم مشاركة علامة تبويب أو نافذة أو شاشة مع تطبيق الويب كفيديو MediaStreamTrack.

تعرض العديد من تطبيقات الويب التي تستخدم getDisplayMedia() للمستخدم معاينة فيديو للسطح الذي تم التقاطه. على سبيل المثال، غالبًا ما تبث تطبيقات مؤتمرات الفيديو هذا الفيديو للمستخدمين البعيدين، كما تعرضه على HTMLVideoElement على الجهاز، حتى يتمكن المستخدم المحلي من الاطّلاع باستمرار على معاينة لما يشاركونه.

تقدّم هذه المستندات واجهة برمجة التطبيقات الجديدة Captured Surface Control API في Chrome، والتي تتيح لتطبيق الويب الانتقال إلى علامة تبويب تم التقاطها، بالإضافة إلى قراءة مستوى التكبير/التصغير لعلامة التبويب التي تم التقاطها وكتابته.

ينتقل مستخدم إلى أعلى علامة تبويب تم تسجيلها ويكبّرها (العرض التجريبي).

لماذا يجب استخدام عنصر التحكّم في المساحة التي تمّ التقاطها؟

تواجه جميع تطبيقات مؤتمرات الفيديو العائق نفسه: إذا أراد المستخدم التفاعل مع علامة تبويب أو نافذة تم التقاطها، عليه التبديل إلى تلك المساحة، ما ينقل المستخدم بعيدًا عن تطبيق مؤتمرات الفيديو. ويؤدي ذلك إلى بعض التحديات:

  • لا يمكن للمستخدم رؤية التطبيق الذي تم تسجيله وفيديوهات المستخدمين البعيدين في الوقت نفسه ما لم يستخدم ميزة نافذة ضمن النافذة أو نافذتَين منفصلتَين جنبًا إلى جنب لعلامة التبويب "مؤتمر الفيديو" وعلامة التبويب المشترَكة. قد يكون ذلك صعبًا على شاشة أصغر حجمًا.
  • يضطر المستخدم إلى التبديل بين تطبيق مكالمات الفيديو ومساحة العرض التي تم التقاطها.
  • يفقد المستخدم إمكانية الوصول إلى عناصر التحكّم التي يعرضها تطبيق مكالمات الفيديو عندما لا يكون متصلاً به، مثل تطبيق محادثة مضمّن وتفاعلات الرموز التعبيرية والإشعارات بشأن المستخدمين الذين يطلبون الانضمام إلى المكالمة وعناصر التحكّم في الوسائط المتعددة والتنسيقات وغيرها من ميزات مكالمات الفيديو المفيدة.
  • لا يمكن للمقدّم تفويض التحكّم للمشاركين عن بُعد. يؤدّي ذلك إلى السيناريو المعتاد الذي يطلب فيه المستخدمون عن بُعد من المُقدّم تغيير الشريحة أو الانتقال للأعلى أو للأسفل قليلاً أو ضبط مستوى التكبير/التصغير.

تعالج واجهة برمجة التطبيقات Captured Surface Control API هذه المشاكل.

كيف يمكنني استخدام عنصر التحكّم في السطح الذي تمّ التقاطه؟

يتطلّب استخدام عنصر التحكّم في المساحة التي تمّ التقاطها بنجاح اتّباع بعض الخطوات، مثل التقاط علامة تبويب في المتصفّح صراحةً والحصول على إذن من المستخدم قبل التمكّن من الانتقال إلى علامة التبويب التي تمّ التقاطها وتكبيرها أو تصغيرها.

تسجيل علامة تبويب متصفّح

ابدأ بطلب من المستخدم اختيار سطح لمشاركة المحتوى عليه باستخدام 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;
}

طلب الإذن

يؤدي أول استدعاء لأيّ من sendWheel() أو setZoomLevel() على عنصر CaptureController معيّن إلى ظهور طلب الحصول على الإذن. إذا منح المستخدم الإذن، يُسمح بمزيد من عمليات استدعاء هذه الطرق على عنصر CaptureController هذا. إذا رفض المستخدم الإذن، يتم رفض الوعد الذي تم إرجاعه.

يُرجى العِلم أنّ عناصر CaptureController مرتبطة بشكل فريد بجلسة تسجيل معيّنة، ولا يمكن ربطها بجلسة تسجيل أخرى، ولا تبقى محفوظة عند الانتقال إلى الصفحة التي تم تحديدها فيها. ومع ذلك، تظل جلسات الالتقاط محفوظة بعد الانتقال إلى الصفحة التي تم التقاطها.

يجب أن يُجري المستخدم إيماءة لعرض طلب الحصول على الإذن. لا تتطلّب مكالمات 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()، يمكن للتطبيق الذي يُجري عملية الالتقاط إرسال أحداث عجلة بحجم محدّد على إحداثيات من اختياره ضمن مساحة العرض في علامة التبويب. لا يمكن تمييز الحدث للتطبيق الذي تم تسجيله عن التفاعل المباشر للمستخدم.

بافتراض أنّ التطبيق الذي يُجري عملية الالتقاط يستخدم عنصر <video> يُسمى "previewTile"، يوضّح الرمز التالي كيفية إعادة توجيه أحداث إرسال عجلة إلى علامة التبويب التي تم التقاطها:

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 لا يكون خارج الحدود عادةً، لذا من الآمن استخدامه لطلب الإذن من المستخدم).

Zoom

يتم التفاعل مع مستوى التكبير لعلامة التبويب التي تم التقاطها من خلال مساحات العرض 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.
}

تفعيل عنصر التحكّم في المساحة التي تمّ التقاطها

تتوفّر واجهة برمجة التطبيقات Captured Surface Control API في Chrome على أجهزة الكمبيوتر المكتبي ضمن علامة Captured Surface Control، ويمكن تفعيلها على العنوان chrome://flags/#captured-surface-control.

ستخضع هذه الميزة أيضًا لتجربة بدءًا من الإصدار 122 من Chrome على أجهزة الكمبيوتر المكتبي، ما يسمح للمطوّرين بتفعيل الميزة لزوّار مواقعهم الإلكترونية من أجل جمع البيانات من المستخدمين الفعليين. اطّلِع على البدء باستخدام تجارب المصدر للحصول على مزيد من المعلومات حول تجارب المصدر وطريقة عملها.

الأمان والخصوصية

تتيح لك سياسة الأذونات في "captured-surface-control" إدارة كيفية وصول تطبيق الالتقاط وإطارات iframe التابعة لجهات خارجية المضمّنة إلى عنصر التحكّم في المساحة التي تم التقاطها. لفهم المفاضلات الأمنية، اطّلِع على قسم الاعتبارات المتعلّقة بالخصوصية والأمان في الشرح التفصيلي عن ميزة "التحكّم في السطح الذي تم التقاطه".

عرض توضيحي

يمكنك تجربة ميزة "عناصر التحكّم في السطح الذي تمّ التقاطه" من خلال تشغيل الإصدار التجريبي على Glitch. احرص على الاطّلاع على رمز المصدر.

التغييرات في الإصدارات السابقة من Chrome

في ما يلي بعض الاختلافات السلوكية الرئيسية حول عنصر التحكّم في السطح الذي تمّ التقاطه والتي يجب أن تكون على دراية بها:

  • في الإصدار 124 من Chrome والإصدارات الأقدم:
    • إذا تم منح الإذن، يكون نطاقه محصورًا بجلسة الالتقاط المرتبطة بهذا CaptureController، وليس بمصدر الالتقاط.
  • في الإصدار 122 من Chrome:
    • تعرِض getZoomLevel() وعدًا بمستوى التكبير الحالي لعلامة التبويب.
    • يعرض sendWheel() وعدًا مرفوضًا مع رسالة الخطأ "No permission." إذا لم يمنح المستخدم التطبيق إذنًا لاستخدامه. نوع الخطأ هو "NotAllowedError" في الإصدار 123 من Chrome والإصدارات الأحدث.
    • لا يتوفّر oncapturedzoomlevelchange. يمكنك استخدام polyfill لهذه الميزة باستخدام setInterval().

ملاحظات

يريد فريق Chrome ومجتمع معايير الويب معرفة تجاربك مع عنصر التحكّم في المساحة التي تمّ التقاطها.

أخبرنا عن التصميم

هل هناك مشكلة في ميزة "التقاط السطح المُسجَّل" لا تعمل على النحو المتوقّع؟ هل هناك طرق أو سمات غير متوفّرة تحتاج إليها لتنفيذ فكرتك؟ هل لديك سؤال أو تعليق بشأن نموذج الأمان؟ يمكنك الإبلاغ عن مشكلة في المواصفات على مستودع GitHub، أو إضافة أفكارك إلى مشكلة حالية.

هل هناك مشكلة في التنفيذ؟

هل رصدت خطأ في عملية تنفيذ Chrome؟ أم أنّ عملية التنفيذ مختلفة عن المواصفات؟ يمكنك إرسال بلاغ عن خلل على الرابط https://new.crbug.com. احرص على تضمين أكبر قدر ممكن من التفاصيل، بالإضافة إلى تعليمات لإعادة إنتاج المشكلة. تُعدّ أداة Glitch مثالية لمشاركة الأخطاء التي يمكن إعادة إنتاجها.