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

François Beaufort
François Beaufort

Browser Support

  • Chrome: 116.
  • Edge: 116.
  • Firefox: not supported.
  • Safari: not supported.

Source

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

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

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

الحالة

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

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

يمكنك استخدام واجهة برمجة التطبيقات هذه بعدة طرق، بما في ذلك مشغّلات الفيديو المخصّصة واجتماعات الفيديو وتطبيقات الإنتاجية.

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

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

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

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

الإنتاجية

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

واجهة البث المباشر

الخصائص

documentPictureInPicture.window
تعرض هذه السمة نافذة "نافذة ضمن النافذة" الحالية، إذا كانت متاحة. بخلاف ذلك، تعرض null.

الطُرق

documentPictureInPicture.requestWindow(options)

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

width
تضبط هذه السمة العرض الأولي لنافذة "نافذة ضمن النافذة".
height
تضبط هذه السمة الارتفاع الأولي لنافذة "نافذة ضمن النافذة".
disallowReturnToOpener
يخفي زر "الرجوع إلى علامة التبويب" في نافذة "النافذة ضمن النافذة" إذا كانت القيمة صحيحة. القيمة التلقائية هي "خطأ".
preferInitialWindowPlacement
فتح نافذة "نافذة ضمن النافذة" في موضعها وحجمها التلقائيَين إذا كانت القيمة صحيحة القيمة التلقائية هي "خطأ".

الفعاليات

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,
  });
});

نسخ أوراق الأنماط إلى PiP

لنسخ جميع أوراق أنماط 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() Window لتغيير حجم نافذة "نافذة ضمن النافذة". تتطلّب كلتا الطريقتَين إيماءة من المستخدم.

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() Window للتركيز على نافذة الفتح من نافذة "نافذة ضمن النافذة". تتطلّب هذه الطريقة إيماءة مستخدم.

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: يمكنك التشغيل باستخدام Document Picture-in-Picture API عرض توضيحي لمشغّل VideoJS.
  • يستفيد تطبيق الويب Tomodoro الذي يعتمد على تقنية بومودورو من واجهة برمجة التطبيقات Document Picture-in-Picture API عندما تكون متاحة. اطّلِع على طلب السحب على GitHub.
‫Tomodoro، وهو تطبيق ويب يعتمد على تقنية بومودورو
نافذة "نافذة ضمن النافذة" في Tomodoro

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

ملء مشاكل على GitHub مع اقتراحات وأسئلة