نافذة ضمن النافذة لأي عنصر، وليس فقط <video>

François Beaufort
François Beaufort

دعم المتصفح

  • Chrome: 116.
  • الحافة: 116.
  • Firefox: غير مدعوم.
  • Safari: غير متاح.

المصدر

تتيح واجهة برمجة التطبيقات Document Picture-in-Picture API فتح نافذة تظهر في أعلى الشاشة ويمكن تعبئتها بمحتوى HTML عشوائي. توسّع واجهة برمجة التطبيقات Picture-in-Picture API في <video>، وهي تسمح فقط بوضع عنصر HTML <video> في نافذة "نافذة ضمن النافذة".

تشبه نافذة "نافذة ضمن النافذة" في واجهة برمجة التطبيقات Document Picture-in-Picture نافذة فارغة من المصدر نفسه تم فتحها عبر window.open()، مع بعض الاختلافات:

  • تطفو نافذة "نافذة ضمن النافذة" فوق النوافذ الأخرى.
  • لا تتجاوز نافذة "نافذة ضمن النافذة" النافذة المفتوحة مطلقًا.
  • لا يمكن التنقّل في نافذة "نافذة ضمن النافذة".
  • لا يمكن تعيين موضع نافذة "نافذة ضمن النافذة" بواسطة الموقع الإلكتروني.
نافذة ضمن النافذة تعرض فيديو دعائيًا لقناة Sintel
نافذة ضمن النافذة تم إنشاؤها باستخدام واجهة برمجة التطبيقات Document Picture-in-Picture API (عرض توضيحي)

الوضع الحالي

الخطوة الحالة
1. إنشاء شرح مكتمل
2. إنشاء مسودة أولية للمواصفات قيد التقدّم
3- جمع الملاحظات التكرار التحسيني للتصميم قيد التقدّم
4. مرحلة التجربة والتقييم مكتمل
5- الإطلاق مكتمل (سطح المكتب)

حالات الاستخدام

مشغّل الفيديو المخصّص

يمكن أن يوفّر موقع إلكتروني تجربة فيديو ضمن ميزة "نافذة ضمن النافذة" باستخدام واجهة برمجة التطبيقات Picture-in-Picture API في <video>، علمًا أنّ هذه الميزة محدودة للغاية. وتقبل نافذة "نافذة ضمن النافذة" الحالية عددًا قليلاً من الإدخالات، ويمكن تصميمها بشكل محدود. إذا كان المستند كاملاً في وضع "نافذة ضمن النافذة"، يمكن للموقع الإلكتروني توفير عناصر تحكّم ومدخلات مخصّصة (مثل التسميات التوضيحية وقوائم التشغيل وأداة تنقيح الوقت وإبداء الإعجاب أو عدم الإعجاب بالفيديوهات) لتحسين تجربة المستخدم في ميزة "نافذة ضمن النافذة".

اجتماعات الفيديو

من الشائع أن يغادر المستخدمون علامة تبويب المتصفِّح أثناء جلسة اجتماع فيديو لأسباب مختلفة (على سبيل المثال، عرض علامة تبويب أخرى في المكالمة أو أثناء تنفيذ مهام متعددة) بينما لا يزالون يريدون مشاهدة المكالمة، لذا تُعدّ الميزة حالة استخدام أساسية لميزة "نافذة ضمن النافذة". مرة أخرى، يمكن أن تكون التجربة الحالية التي يمكن أن يوفرها الموقع الإلكتروني لعقد اجتماعات الفيديو عبر واجهة برمجة التطبيقات لميزة "صورة داخل الصورة" في <video> محدودة من حيث النمط والإدخال. من خلال توفير مستند كامل في ميزة "نافذة ضمن النافذة"، يمكن للموقع الإلكتروني بسهولة دمج عدة فيديوهات مضمّنة في نافذة واحدة في وضع "نافذة ضمن النافذة" بدون الحاجة إلى الاعتماد على نصائح لاستخدام لوحة الرسم وتوفير عناصر تحكّم مخصّصة، مثل إرسال رسالة أو كتم صوت مستخدم آخر أو رفع اليد.

الإنتاجية

أظهرت الأبحاث أنّ المستخدمين بحاجة إلى مزيد من الطرق ليكونوا منتجين على الويب. يمنح المستند في وضع "نافذة ضمن النافذة" تطبيقات الويب المرونة لإنجاز المزيد من المهام. ويمكن لتطبيقات الويب الآن الوصول دائمًا إلى المحتوى الذي يوفّره سواءً كنت تريد تعديل النصوص أو تدوين الملاحظات أو قوائم المهام أو المراسلة والدردشة أو باستخدام أدوات التصميم والتطوير.

الواجهة

أماكن إقامة

documentPictureInPicture.window
لعرض نافذة "نافذة ضمن النافذة" الحالية، إن توفّرت. في حال عدم توفيره، يتم إرجاع null.

الطُرق

documentPictureInPicture.requestWindow(options)

عرض وعد يتم حله عند فتح نافذة "نافذة ضمن النافذة". يتم رفض الوعد في حال طلبه بدون إيماءة المستخدم. يحتوي قاموس options على الأعضاء الاختياريين التاليين:

width
لضبط العرض الأولي لنافذة "نافذة ضمن النافذة".
height
يتم ضبط الارتفاع الأولي لنافذة "نافذة ضمن النافذة".
disallowReturnToOpener
إخفاء "الرجوع إلى علامة التبويب" في نافذة "نافذة ضمن النافذة" إذا كانت القيمة true. ويكون false افتراضيًا.

فعاليات

documentPictureInPicture.onenter
تم تنشيطه في documentPictureInPicture عند فتح نافذة "نافذة ضمن النافذة".

أمثلة

تعمل كود HTML التالي على إعداد مشغّل فيديو مخصص وعنصر زر لفتح مشغّل الفيديو في نافذة "نافذة ضمن النافذة".

<div id="playerContainer">
  <div id="player">
    <video id="video"></video>
  </div>
</div>
<button id="pipButton">Open Picture-in-Picture window</button>

فتح نافذة "نافذة ضمن النافذة"

تطلب لغة JavaScript التالية documentPictureInPicture.requestWindow() عندما ينقر المستخدم على الزر لفتح نافذة "نافذة ضمن النافذة". وتتم إزالة الوعد الذي تم إرجاعه من خلال كائن JavaScript لنافذة "نافذة ضمن النافذة". يتم نقل مشغّل الفيديو إلى تلك النافذة باستخدام append().

pipButton.addEventListener('click', async () => {
  const player = document.querySelector("#player");

  // Open a Picture-in-Picture window.
  const pipWindow = await documentPictureInPicture.requestWindow();

  // Move the player to the Picture-in-Picture window.
  pipWindow.document.body.append(player);
});

ضبط حجم نافذة "نافذة ضمن النافذة"

لضبط حجم نافذة "نافذة ضمن النافذة"، اضبط خيارَي width وheight على documentPictureInPicture.requestWindow() على حجم نافذة "نافذة ضمن النافذة" المطلوب. قد يثبّت Chrome قيم الخيارات إذا كانت كبيرة جدًا أو صغيرة جدًا بحيث لا تناسب حجم نافذة سهل الاستخدام.

pipButton.addEventListener("click", async () => {
  const player = document.querySelector("#player");

  // Open a Picture-in-Picture window whose size is
  // the same as the player's.
  const pipWindow = await documentPictureInPicture.requestWindow({
    width: player.clientWidth,
    height: player.clientHeight,
  });

  // Move the player to the Picture-in-Picture window.
  pipWindow.document.body.append(player);
});

إخفاء ميزة "الرجوع إلى علامة التبويب" الزر في نافذة "نافذة ضمن النافذة"

لإخفاء الزرّ في نافذة "نافذة ضمن النافذة" الذي يسمح للمستخدم بالرجوع إلى علامة التبويب المفتوحة، اضبط الخيار disallowReturnToOpener على documentPictureInPicture.requestWindow() على true.

pipButton.addEventListener("click", async () => {
  // Open a Picture-in-Picture window which hides the "back to tab" button.
  const pipWindow = await documentPictureInPicture.requestWindow({
    disallowReturnToOpener: true,
  });
});

نسخ أوراق الأنماط إلى نافذة "نافذة ضمن النافذة"

لنسخ كل أوراق أنماط CSS من النافذة الأصلية، انتقِل إلى styleSheets المرتبط أو مضمّنًا بشكل صريح في المستند، وألحقها في نافذة "نافذة ضمن النافذة". تجدر الإشارة إلى أنّ هذه النسخة تُستخدم لمرة واحدة.

pipButton.addEventListener("click", async () => {
  const player = document.querySelector("#player");

  // Open a Picture-in-Picture window.
  const pipWindow = await documentPictureInPicture.requestWindow();

  // Copy style sheets over from the initial document
  // so that the player looks the same.
  [...document.styleSheets].forEach((styleSheet) => {
    try {
      const cssRules = [...styleSheet.cssRules].map((rule) => rule.cssText).join('');
      const style = document.createElement('style');

      style.textContent = cssRules;
      pipWindow.document.head.appendChild(style);
    } catch (e) {
      const link = document.createElement('link');

      link.rel = 'stylesheet';
      link.type = styleSheet.type;
      link.media = styleSheet.media;
      link.href = styleSheet.href;
      pipWindow.document.head.appendChild(link);
    }
  });

  // Move the player to the Picture-in-Picture window.
  pipWindow.document.body.append(player);
});

التعامل مع الجهاز عند إغلاق نافذة "نافذة ضمن النافذة"

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

pipButton.addEventListener("click", async () => {
  const player = document.querySelector("#player");

  // Open a Picture-in-Picture window.
  const pipWindow = await documentPictureInPicture.requestWindow();

  // Move the player to the Picture-in-Picture window.
  pipWindow.document.body.append(player);

  // Move the player back when the Picture-in-Picture window closes.
  pipWindow.addEventListener("pagehide", (event) => {
    const playerContainer = document.querySelector("#playerContainer");
    const pipPlayer = event.target.querySelector("#player");
    playerContainer.append(pipPlayer);
  });
});

يمكنك إغلاق نافذة ميزة "نافذة ضمن النافذة" آليًا باستخدام الطريقة close().

// Close the Picture-in-Picture window programmatically. 
// The "pagehide" event will fire normally.
pipWindow.close();

الاستماع إلى الصوت عند دخول الموقع الإلكتروني في وضع "نافذة ضمن النافذة"

استمِع إلى الحدث "enter" في documentPictureInPicture لمعرفة وقت فتح نافذة "نافذة ضمن النافذة". يحتوي الحدث على كائن window للوصول إلى نافذة "نافذة ضمن النافذة".

documentPictureInPicture.addEventListener("enter", (event) => {
  const pipWindow = event.window;
});

الوصول إلى العناصر في نافذة "نافذة ضمن النافذة"

يمكنك الوصول إلى العناصر في نافذة "نافذة ضمن النافذة" من العنصر الذي تم عرضه من خلال "documentPictureInPicture.requestWindow()" أو من خلال "documentPictureInPicture.window" على النحو الموضّح أدناه.

const pipWindow = documentPictureInPicture.window;
if (pipWindow) {
  // Mute video playing in the Picture-in-Picture window.
  const pipVideo = pipWindow.document.querySelector("#video");
  pipVideo.muted = true;
}

التعامل مع الأحداث من نافذة "نافذة ضمن النافذة"

يمكنك إنشاء أزرار وعناصر تحكّم والرد على أحداث إدخال المستخدم، مثل "click"، كما تفعل عادةً في JavaScript.

// Add a "mute" button to the Picture-in-Picture window.
const pipMuteButton = pipWindow.document.createElement("button");
pipMuteButton.textContent = "Mute";
pipMuteButton.addEventListener("click", () => { 
  const pipVideo = pipWindow.document.querySelector("#video");
  pipVideo.muted = true;
});
pipWindow.document.body.append(pipMuteButton);

تغيير حجم نافذة ميزة "نافذة ضمن النافذة"

يمكنك استخدام طريقتَي النافذة resizeBy() وresizeTo() لتغيير حجم نافذة "نافذة ضمن النافذة". تتطلب كلتا الطريقتين استخدام إيماءة المستخدم.

const resizeButton = pipWindow.document.createElement('button');
resizeButton.textContent = 'Resize';
resizeButton.addEventListener('click', () => {
  // Expand the Picture-in-Picture window's width by 20px and height by 30px.
  pipWindow.resizeBy(20, 30);
});
pipWindow.document.body.append(resizeButton);

التركيز على نافذة الفتح

يمكنك استخدام طريقة النافذة focus() للتركيز على النافذة المفتوحة من نافذة "نافذة ضمن النافذة". تتطلّب هذه الطريقة استخدام إيماءة المستخدم.

const returnToTabButton = pipWindow.document.createElement("button");
returnToTabButton.textContent = "Return to opener tab";
returnToTabButton.addEventListener("click", () => {
  window.focus();
});
pipWindow.document.body.append(returnToTabButton);

وضع عرض "نافذة ضمن النافذة" في CSS

يمكنك استخدام وضع عرض CSS picture-in-picture لكتابة قواعد CSS محدَّدة يتم تطبيقها فقط عند (جزء من) ظهور تطبيق الويب في وضع "نافذة ضمن النافذة".

@media all and (display-mode: picture-in-picture) {
  body {
    margin: 0;
  }
  h1 {
    font-size: 0.8em;
  }
}

رصد الميزات

للتحقّق مما إذا كانت واجهة برمجة التطبيقات Document Picture-in-Picture API متوافقة، يمكنك استخدام:

if ('documentPictureInPicture' in window) {
  // The Document Picture-in-Picture API is supported.
}

إصدارات تجريبية

مشغّل VideoJS

يمكنك التشغيل باستخدام العرض التوضيحي لمشغّل فيديو JavaScript من خلال واجهة برمجة التطبيقات Document Picture-in-Picture API. تأكّد من مراجعة رمز المصدر.

بومودورو

يستفيد أيضًا تطبيق Tomodoro، وهو تطبيق ويب pomodoro، من واجهة برمجة التطبيقات Document Picture-in-Picture API عند توفّرها (يمكنك الاطّلاع على طلب سحب GitHub).

لقطة شاشة لتطبيق Tomodoro على الويب.
نافذة ضمن النافذة في "تومودورو"

ملاحظات

يُرجى الإبلاغ عن المشاكل في GitHub وتقديم الاقتراحات والأسئلة.

شكر وتقدير

صورة رئيسية من تصميم جاكوب أوينز