การแสดงภาพซ้อนภาพสำหรับองค์ประกอบใดๆ ไม่ใช่แค่ <วิดีโอ>

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() โดยมีความแตกต่างบางอย่างดังนี้

  • หน้าต่างภาพในภาพจะลอยอยู่เหนือหน้าต่างอื่นๆ
  • หน้าต่างการแสดงภาพซ้อนภาพจะปิดไปก่อนหน้าต่างที่เปิดอยู่เสมอ
  • ไปยังส่วนต่างๆ ของหน้าต่างภาพซ้อนภาพไม่ได้
  • เว็บไซต์ไม่สามารถกำหนดตำแหน่งหน้าต่างของฟีเจอร์ภาพซ้อนภาพได้
หน้าต่างการแสดงภาพซ้อนภาพที่เล่นวิดีโอตัวอย่างของ Sintel
หน้าต่างภาพในภาพที่สร้างด้วย Document Picture-in-Picture API (เดโม)

สถานะปัจจุบัน

ขั้นตอน สถานะ
1. สร้างคำอธิบาย เสร็จสมบูรณ์
2. สร้างฉบับร่างแรกของข้อกําหนด กำลังดำเนินการ
3. รวบรวมความคิดเห็นและปรับปรุงการออกแบบ กำลังดำเนินการ
4. ช่วงทดลองใช้จากต้นทาง เสร็จสมบูรณ์
5. เปิด เสร็จสมบูรณ์ (เดสก์ท็อป)

กรณีการใช้งาน

วิดีโอเพลเยอร์ที่กำหนดเอง

เว็บไซต์สามารถมอบประสบการณ์การใช้งานวิดีโอแบบ PIP ด้วย Picture-in-Picture API สำหรับ <video> ที่มีอยู่ แต่มีข้อจำกัดอย่างมาก หน้าต่างการแสดงภาพซ้อนภาพที่มีอยู่ยอมรับอินพุตเพียงไม่กี่รายการและจัดรูปแบบได้จำกัด เมื่อใช้เอกสารแบบเต็มในโหมดภาพในภาพ เว็บไซต์จะมอบการควบคุมและอินพุตที่กำหนดเองได้ (เช่น คำบรรยายแทนเสียง เพลย์ลิสต์ ตัวควบคุมเวลา การกดชอบและกดไม่ชอบวิดีโอ) เพื่อปรับปรุงประสบการณ์การใช้งานวิดีโอแบบภาพในภาพให้แก่ผู้ใช้

การประชุมทางวิดีโอ

เป็นเรื่องปกติที่ผู้ใช้จะออกจากแท็บเบราว์เซอร์ระหว่างเซสชันการประชุมทางวิดีโอด้วยเหตุผลหลายประการ (เช่น นำเสนอแท็บอื่นในการประชุมหรือทำงานหลายอย่างพร้อมกัน) ขณะยังต้องการดูการประชุมอยู่ การแสดงภาพซ้อนภาพจึงถือเป็น Use Case หลัก เราขอย้ำอีกครั้งว่าประสบการณ์ปัจจุบันที่เว็บไซต์วิดีโอคอนเฟอเรนซ์มอบให้ได้ผ่าน Picture-in-Picture API สำหรับ <video> นั้นมีข้อจำกัดด้านรูปแบบและอินพุต เมื่อใช้เอกสารแบบเต็มในโหมดภาพซ้อนภาพ เว็บไซต์จะรวมสตรีมวิดีโอหลายรายการไว้ในหน้าต่าง PiP เดียวได้อย่างง่ายดายโดยไม่ต้องใช้การแฮ็ก Canvas และสามารถมอบการควบคุมที่กำหนดเอง เช่น การส่งข้อความ การปิดเสียงผู้ใช้รายอื่น หรือยกมือ

ประสิทธิภาพการทำงาน

งานวิจัยแสดงให้เห็นว่าผู้ใช้ต้องการวิธีใหม่ๆ ในการทำงานบนเว็บ เอกสารในโหมดภาพซ้อนภาพช่วยให้เว็บแอปมีความยืดหยุ่นมากขึ้น ไม่ว่าจะเป็นการแก้ไขข้อความ การจดบันทึก รายการงาน การรับส่งข้อความและแชท หรือเครื่องมือออกแบบและพัฒนา ตอนนี้เว็บแอปสามารถเข้าถึงเนื้อหาได้เสมอ

SDK โฆษณา B

พร็อพเพอร์ตี้

documentPictureInPicture.window
แสดงหน้าต่างการแสดงภาพซ้อนภาพปัจจุบัน (หากมี) ไม่เช่นนั้น ระบบจะแสดงผล null

เมธอด

documentPictureInPicture.requestWindow(options)

แสดงผลลัพธ์เป็น Promise ที่แสดงผลเมื่อเปิดหน้าต่างการแสดงภาพซ้อนภาพ พรอมต์จะปฏิเสธหากมีการเรียกใช้โดยไม่มีท่าทางสัมผัสของผู้ใช้ พจนานุกรม options มีสมาชิกต่อไปนี้ซึ่งไม่บังคับ

width
กำหนดความกว้างเริ่มต้นของหน้าต่างการแสดงภาพซ้อนภาพ
height
กำหนดความสูงเริ่มต้นของหน้าต่างภาพซ้อนภาพ
disallowReturnToOpener
ซ่อนปุ่ม "กลับไปยังแท็บ" ในหน้าต่างภาพในภาพหากเป็นจริง โดยค่าเริ่มต้นจะเป็น False
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,
  });
});

คัดลอกสไตล์ชีตไปยังหน้าต่างการแสดงภาพซ้อนภาพ

หากต้องการคัดลอกสไตล์ชีต 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() เพื่อปรับขนาดหน้าต่างการแสดงภาพซ้อนภาพ ทั้ง 2 วิธีต้องใช้ท่าทางสัมผัสของผู้ใช้

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

คุณสามารถเล่นกับการสาธิตโปรแกรมเล่น VideoJS ของ Document Picture-in-Picture API อย่าลืมตรวจสอบซอร์สโค้ด

Pomodoro

Tomodoro ซึ่งเป็นเว็บแอป Pomodoro ยังใช้ประโยชน์จาก Document Picture-in-Picture API ด้วยหากมี ดูคำขอดึงข้อมูล GitHub

Tomodoro ซึ่งเป็นเว็บแอป Pomodoro
หน้าต่างแสดงภาพซ้อนภาพใน Tomodoro

แชร์ความคิดเห็น

แจ้งปัญหาใน GitHub พร้อมคำแนะนำและคำถาม