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

François Beaufort
François Beaufort

يمكن مشاركة علامات التبويب والنوافذ والشاشات من قبل على النظام الأساسي للويب من خلال واجهة برمجة تطبيقات التقاط الشاشة. عندما يتصل تطبيق ويب بخدمة getDisplayMedia()، يطلب Chrome من المستخدم مشاركة علامة تبويب أو نافذة أو شاشة مع تطبيق الويب على شكل فيديو MediaStreamTrack.

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

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

يتنقّل المستخدم في علامة تبويب تم التقاطها ويكبّرها (عرض توضيحي).

لماذا يُنصح باستخدام أداة التحكم في السطح التي تم التقاطها؟

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

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

وتعالج واجهة برمجة التطبيقات Taked 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 further explained 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.
}

تفعيل ميزة التحكّم في السطح التي تم التقاطها

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

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

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

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

عرض توضيحي

يمكنك اللعب باستخدام ميزة "التحكّم في السطح" من خلال تفعيل العرض التوضيحي على ميزة Glitch. تأكّد من الاطّلاع على رمز المصدر.

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

في ما يلي بعض الاختلافات السلوكية الأساسية حول ميزة "التحكم في السطح" التي يجب أن تكون على دراية بها:

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

إضافة ملاحظات

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

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

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

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

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