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

François Beaufort
François Beaufort

Browser Support

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

Source

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

หน้าต่างการแสดงภาพซ้อนภาพใน Document Picture-in-Picture API จะคล้ายกับหน้าต่างที่มาจากต้นทางเดียวกันที่ว่างเปล่าซึ่งเปิดขึ้นโดยใช้ window.open() โดยมีข้อแตกต่างบางประการดังนี้

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

สถานะ

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

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

คุณสามารถใช้ API นี้ได้หลายวิธี รวมถึงวิดีโอเพลเยอร์ที่กำหนดเอง การประชุมทางวิดีโอ และแอปเพิ่มประสิทธิภาพการทำงาน

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

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

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

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

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

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

อินเทอร์เฟซ

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

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

เมธอด

documentPictureInPicture.requestWindow(options)

แสดง Promise ที่ได้รับการแก้ไขเมื่อเปิดหน้าต่างการแสดงภาพซ้อนภาพ สัญญาจะถูกปฏิเสธหากมีการเรียกใช้โดยไม่มีท่าทางของผู้ใช้ พจนานุกรม 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() เมื่อผู้ใช้คลิกปุ่มเพื่อเปิดหน้าต่างการแสดงภาพซ้อนภาพที่ว่างเปล่า Promise ที่แสดงผลจะได้รับการแก้ไขด้วยออบเจ็กต์ 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() เป็นขนาดหน้าต่าง PiP ที่เหมาะสม 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);
});

ซ่อนปุ่ม "กลับไปที่แท็บ" ในหน้าต่าง PiP

หากต้องการซ่อนปุ่มในหน้าต่างการแสดงภาพซ้อนภาพที่อนุญาตให้ผู้ใช้กลับไปที่แท็บที่เปิด ให้ตั้งค่าตัวเลือก 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,
  });
});

เปิด PiP ไปยังตำแหน่งและขนาดเริ่มต้น

หากไม่ต้องการใช้ตำแหน่งหรือขนาดของหน้าต่างการแสดงภาพซ้อนภาพก่อนหน้าซ้ำ ให้ตั้งค่าตัวเลือก 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);
});

จัดการเมื่อหน้าต่าง PiP ปิด

ฟังเหตุการณ์ "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();

ฟังเมื่อเว็บไซต์เข้าสู่ PiP

ฟังเหตุการณ์ "enter" ใน documentPictureInPicture เพื่อดูว่าหน้าต่างการแสดงภาพซ้อนภาพ เปิดเมื่อใด เหตุการณ์นี้มีออบเจ็กต์ window เพื่อเข้าถึงหน้าต่างการแสดงภาพซ้อนภาพ

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

เข้าถึงองค์ประกอบในหน้าต่าง PiP

เข้าถึงองค์ประกอบในหน้าต่างการแสดงภาพซ้อนภาพจากออบเจ็กต์ที่แสดงผลโดย 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;
}

จัดการเหตุการณ์จากหน้าต่าง PiP

สร้างปุ่มและการควบคุม แล้วตอบสนองต่อเหตุการณ์ที่เป็นอินพุตจากผู้ใช้ (เช่น "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);

ปรับขนาดหน้าต่าง PiP

ใช้วิธี resizeBy() และ resizeTo() ของ Window เพื่อปรับขนาดหน้าต่างการแสดงภาพซ้อนภาพ ทั้ง 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() ของ Window เพื่อโฟกัสหน้าต่างที่เปิดจากหน้าต่างการแสดงภาพซ้อนภาพ วิธีนี้ต้องใช้ท่าทางของผู้ใช้

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

โหมดการแสดงผล PiP ของ 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 Player: ลองใช้การสาธิต VideoJS Player ของ Document Picture-in-Picture API
  • **Tomodoro** ซึ่งเป็นเว็บแอป Pomodoro ใช้ประโยชน์จาก Document Picture-in-Picture API เมื่อพร้อมใช้งาน ดูคำขอ Pull Request ใน GitHub
Tomodoro ซึ่งเป็นเว็บแอป Pomodoro
หน้าต่างการแสดงภาพซ้อนภาพใน Tomodoro

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

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