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

François Beaufort
François Beaufort

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

وبافتراض أنّ تطبيق الالتقاط يستخدم عنصر <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 على أجهزة الكمبيوتر المكتبي خلف علامة "تم الالتقاط" في "سطح المكتب"، ويمكن تفعيلها على chrome://flags/#captured-surface-control.

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

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

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

عرض توضيحي

يمكنك اللعب باستخدام Captured Surface Control من خلال تشغيل العرض التوضيحي على تطبيق Glitch. تأكّد من الاطّلاع على رمز المصدر.

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

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

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

ملاحظات

يرغب فريق Chrome ومنتدى معايير الويب في معرفة المزيد عن تجاربك مع Captured Surface Control.

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

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

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

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