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

François Beaufort
François Beaufort

توافق المتصفّح

  • Chrome: 116
  • ‫Edge: 116
  • Firefox: غير متوافق
  • Safari: غير متوافق

المصدر

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

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

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

الوضع الحالي

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

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

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

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

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

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

الإنتاجية

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

الواجهة

أماكن إقامة

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

الطُرق

documentPictureInPicture.requestWindow(options)

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

width
لضبط العرض الأولي لنافذة "نافذة ضمن النافذة".
height
يضبط الارتفاع الأولي لنافذة "نافذة ضمن النافذة".
disallowReturnToOpener
يخفي الزر "الرجوع إلى علامة التبويب" في نافذة "نافذة ضمن النافذة" إذا كان صحيحًا. تكون القيمة التلقائية هي خطأ.
preferInitialWindowPlacement
فتح نافذة "الصورة في الصورة" في موضعها وحجمها التلقائيَين إذا كان صحيحًا ويكون 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,
  });
});

فتح نافذة "نافذة ضمن النافذة" في موضعها وحجمها التلقائيَين

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

pipButton.addEventListener("click", async () => {
  // Open a Picture-in-Picture window in its default position / size.
  const pipWindow = await documentPictureInPicture.requestWindow({
    preferInitialWindowPlacement: 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

استخدِم وضع عرض picture-in-picture في CSS لكتابة قواعد 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. تأكّد من مراجعة رمز المصدر.

Pomodoro

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

Tomodoro، وهو تطبيق ويب مخصّص لتقنية &quot;إدارة الوقت بتقنية بومودورو&quot;
نافذة "صورة داخل صورة" في Tomodoro

مشاركة ملاحظاتك

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