ดูวิดีโอโดยใช้การแสดงภาพซ้อนภาพ

François Beaufort
François Beaufort

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

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

ข้อมูลเบื้องต้น

ในเดือนกันยายน 2016 Safari ได้เพิ่มการรองรับการแสดงภาพซ้อนภาพผ่าน WebKit API ใน macOS Sierra 6 เดือนต่อมา Chrome จะเปิดโดยอัตโนมัติ วิดีโอการแสดงภาพซ้อนภาพบนอุปกรณ์เคลื่อนที่ด้วยการเปิดตัว Android O โดยใช้ Android API แบบเนทีฟ หลังจากนั้น 6 เดือน เราได้ประกาศความตั้งใจที่จะ ทำให้ Web API เป็นมาตรฐาน คุณลักษณะที่ทำงานร่วมกับ Safari เพื่อช่วยให้เว็บ ในการสร้างและควบคุมประสบการณ์การใช้งานเต็มรูปแบบเกี่ยวกับการแสดงภาพซ้อนภาพ มาถึงแล้ว!

ทำความเข้าใจโค้ด

เข้าสู่การแสดงภาพซ้อนภาพ

มาเริ่มกันง่ายๆ ด้วยองค์ประกอบวิดีโอและวิธีโต้ตอบกับผู้ใช้ ด้วย เช่น องค์ประกอบปุ่ม

<video id="videoElement" src="https://example.com/file.mp4"></video>
<button id="pipButtonElement"></button>

ขอเฉพาะการแสดงภาพซ้อนภาพเพื่อตอบสนองต่อท่าทางสัมผัสของผู้ใช้ และไม่ขอใน promise ส่งคืนโดย videoElement.play() เพราะสัญญาว่าจะไม่ ยังเผยแพร่ท่าทางสัมผัสของผู้ใช้ แต่ให้โทรหา requestPictureInPicture() ใน เครื่องจัดการคลิกบน pipButtonElement ดังที่แสดงด้านล่าง เป็นความรับผิดชอบของคุณ จัดการกับสิ่งที่จะเกิดขึ้นหากผู้ใช้คลิก 2 ครั้ง

pipButtonElement.addEventListener('click', async function () {
  pipButtonElement.disabled = true;

  await videoElement.requestPictureInPicture();

  pipButtonElement.disabled = false;
});

เมื่อสัญญาสำเร็จ Chrome จะลดขนาดวิดีโอลงเป็นหน้าต่างเล็กๆ ผู้ใช้สามารถเลื่อนไปมาและวางไว้เหนือหน้าต่างอื่นๆ ได้

เท่านี้ก็เรียบร้อย เก่งมาก หยุดอ่านแล้วไปอ่านเนื้อหาที่เหมาะสมกับคุณได้เลย วันหยุดพักผ่อน ซึ่งไม่ได้เป็นเช่นนั้นเสมอไป คำสัญญาอาจปฏิเสธ ด้วยเหตุผลต่อไปนี้

  • ระบบไม่สนับสนุนการแสดงภาพซ้อนภาพ
  • เอกสารไม่ได้รับอนุญาตให้ใช้การแสดงภาพซ้อนภาพเนื่องจากมีข้อจำกัด นโยบายสิทธิ์
  • ยังไม่ได้โหลดข้อมูลเมตาของวิดีโอ (videoElement.readyState === 0)
  • ไฟล์วิดีโอเป็นแบบเสียงเท่านั้น
  • แอตทริบิวต์ disablePictureInPicture ใหม่มีอยู่ในองค์ประกอบวิดีโอ
  • การเรียกไม่ได้เกิดขึ้นในเครื่องจัดการเหตุการณ์ท่าทางสัมผัสของผู้ใช้ (เช่น การคลิกปุ่ม) ตั้งแต่ Chrome 74 เป็นต้นไป จะใช้ได้เท่านั้นเมื่อไม่มีองค์ประกอบใน การแสดงภาพซ้อนภาพอยู่แล้ว

ส่วนการสนับสนุนฟีเจอร์ด้านล่างแสดงวิธีเปิด/ปิดใช้ปุ่มโดยอิงตาม ข้อจำกัดเหล่านี้

มาเพิ่มบล็อก try...catch เพื่อบันทึกข้อผิดพลาดที่อาจเกิดขึ้นเหล่านี้และปล่อยให้ ผู้ใช้จะรู้ว่าเกิดอะไรขึ้น

pipButtonElement.addEventListener('click', async function () {
  pipButtonElement.disabled = true;

  try {
    await videoElement.requestPictureInPicture();
  } catch (error) {
    // TODO: Show error message to user.
  } finally {
    pipButtonElement.disabled = false;
  }
});

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

ออกจากการแสดงภาพซ้อนภาพ

ต่อไป เราจะทำให้ปุ่มของเราสลับเข้าและออกจากการแสดงภาพซ้อนภาพ พ ก่อนอื่นต้องตรวจสอบว่าออบเจ็กต์แบบอ่านอย่างเดียว document.pictureInPictureElement หรือไม่ คือองค์ประกอบวิดีโอ หากไม่ใช่ เราจะส่งคำขอให้ป้อน การแสดงภาพซ้อนภาพตามด้านบน ไม่เช่นนั้น เราจะขอออกโดยการโทร document.exitPictureInPicture() ซึ่งหมายความว่าวิดีโอจะปรากฏขึ้นอีกครั้ง แท็บเดิม โปรดทราบว่าวิธีนี้จะส่งคืนคำสัญญาด้วย

    ...
    try {
      if (videoElement !== document.pictureInPictureElement) {
        await videoElement.requestPictureInPicture();
      } else {
        await document.exitPictureInPicture();
      }
    }
    ...

ฟังกิจกรรมการแสดงภาพซ้อนภาพ

ระบบปฏิบัติการมักจะจำกัดการแสดงภาพซ้อนภาพไว้ในหน้าต่างเดียว ดังนั้น การใช้งาน Chrome เป็นไปตามรูปแบบนี้ ซึ่งหมายความว่าผู้ใช้จะเล่นเกมได้ การแสดงภาพซ้อนภาพครั้งละ 1 วิดีโอ คุณควรอนุญาตให้ผู้ใช้ออก การแสดงภาพซ้อนภาพแม้ว่าคุณจะไม่ได้ขอก็ตาม

เครื่องจัดการเหตุการณ์ enterpictureinpicture และ leavepictureinpicture ใหม่ช่วยให้ เราปรับแต่งประสบการณ์ สำหรับผู้ใช้ได้ ไม่ว่าจะเป็นการเรียกดู แคตตาล็อกวิดีโอ เพื่อแสดงแชทในสตรีมแบบสด

videoElement.addEventListener('enterpictureinpicture', function (event) {
  // Video entered Picture-in-Picture.
});

videoElement.addEventListener('leavepictureinpicture', function (event) {
  // Video left Picture-in-Picture.
  // User may have played a Picture-in-Picture video from a different page.
});

ปรับแต่งหน้าต่างการแสดงภาพซ้อนภาพ

Chrome 74 รองรับปุ่มเล่น/หยุดชั่วคราว แทร็กก่อนหน้า และแทร็กถัดไปใน หน้าต่างการแสดงภาพซ้อนภาพที่คุณควบคุมได้โดยใช้ Media Session API

ตัวควบคุมการเล่นสื่อในหน้าต่างการแสดงภาพซ้อนภาพ
รูปที่ 1 ตัวควบคุมการเล่นสื่อในหน้าต่างการแสดงภาพซ้อนภาพ

โดยค่าเริ่มต้น ปุ่มเล่น/หยุดชั่วคราวจะแสดงในการแสดงภาพซ้อนภาพเสมอ ยกเว้นกรณีที่วิดีโอกำลังเล่นออบเจ็กต์ MediaStream (เช่น getUserMedia() getDisplayMedia(), canvas.captureStream()) หรือวิดีโอมี MediaSource ตั้งค่าระยะเวลาเป็น +Infinity (เช่น ฟีดสด) เพื่อให้มีปุ่มเล่น/หยุดชั่วคราว มองเห็นได้เสมอ ตั้งค่าตัวแฮนเดิลการดำเนินการกับเซสชันสื่อสำหรับทั้ง "เล่น" และ "หยุดชั่วคราว" เหตุการณ์สื่อด้านล่าง

// Show a play/pause button in the Picture-in-Picture window
navigator.mediaSession.setActionHandler('play', function () {
  // User clicked "Play" button.
});
navigator.mediaSession.setActionHandler('pause', function () {
  // User clicked "Pause" button.
});

กำลังแสดง "แทร็กก่อนหน้า" และ "แทร็กถัดไป" การควบคุมหน้าต่างก็คล้ายกัน การเกริ่นนำ ตัวแฮนเดิลการดำเนินการกับเซสชันสื่อเหล่านั้นจะแสดงในการแสดงภาพซ้อนภาพ จากนั้น คุณจะสามารถจัดการการดำเนินการเหล่านี้ได้

navigator.mediaSession.setActionHandler('previoustrack', function () {
  // User clicked "Previous Track" button.
});

navigator.mediaSession.setActionHandler('nexttrack', function () {
  // User clicked "Next Track" button.
});

หากต้องการดูการใช้งานจริง ให้ลองใช้ตัวอย่างเซสชันสื่ออย่างเป็นทางการ

รับขนาดหน้าต่างการแสดงภาพซ้อนภาพ

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

ตัวอย่างด้านล่างแสดงวิธีหาความกว้างและความสูงของ หน้าต่างการแสดงภาพซ้อนภาพเมื่อมีการสร้างหรือปรับขนาด

let pipWindow;

videoElement.addEventListener('enterpictureinpicture', function (event) {
  pipWindow = event.pictureInPictureWindow;
  console.log(`> Window size is ${pipWindow.width}x${pipWindow.height}`);
  pipWindow.addEventListener('resize', onPipWindowResize);
});

videoElement.addEventListener('leavepictureinpicture', function (event) {
  pipWindow.removeEventListener('resize', onPipWindowResize);
});

function onPipWindowResize(event) {
  console.log(
    `> Window size changed to ${pipWindow.width}x${pipWindow.height}`
  );
  // TODO: Change video quality based on Picture-in-Picture window size.
}

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

การรองรับฟีเจอร์

ระบบอาจไม่รองรับ Picture-in-Picture Web API คุณจึงต้องตรวจหา เพื่อมอบการปรับปรุงแบบต่อเนื่อง แม้ว่าจะมีการรองรับ แต่ ปิดโดยผู้ใช้หรือปิดใช้โดยนโยบายสิทธิ์ โชคดีที่คุณสามารถใช้ document.pictureInPictureEnabled บูลีนใหม่เพื่อกำหนดค่านี้

if (!('pictureInPictureEnabled' in document)) {
  console.log('The Picture-in-Picture Web API is not available.');
} else if (!document.pictureInPictureEnabled) {
  console.log('The Picture-in-Picture Web API is disabled.');
}

นำไปใช้กับองค์ประกอบปุ่มที่เฉพาะเจาะจงของวิดีโอ นี่คือลักษณะที่คุณอาจต้องการ จัดการการแสดงปุ่มการแสดงภาพซ้อนภาพ

if ('pictureInPictureEnabled' in document) {
  // Set button ability depending on whether Picture-in-Picture can be used.
  setPipButton();
  videoElement.addEventListener('loadedmetadata', setPipButton);
  videoElement.addEventListener('emptied', setPipButton);
} else {
  // Hide button if Picture-in-Picture is not supported.
  pipButtonElement.hidden = true;
}

function setPipButton() {
  pipButtonElement.disabled =
    videoElement.readyState === 0 ||
    !document.pictureInPictureEnabled ||
    videoElement.disablePictureInPicture;
}

การรองรับวิดีโอ MediaStream

ออบเจ็กต์ MediaStream ที่กำลังเล่น (เช่น getUserMedia(), getDisplayMedia(), canvas.captureStream()) รองรับการแสดงภาพซ้อนภาพใน Chrome 71 ด้วย ช่วงเวลานี้ หมายความว่าคุณสามารถแสดงหน้าต่างการแสดงภาพซ้อนภาพที่มีเว็บแคมของผู้ใช้ สตรีมวิดีโอ แสดงสตรีมวิดีโอ หรือแม้แต่องค์ประกอบ Canvas โปรดทราบว่า องค์ประกอบวิดีโอไม่จำเป็นต้องแนบอยู่กับ DOM เพื่อป้อน การแสดงภาพซ้อนภาพดังที่แสดงด้านล่าง

แสดงเว็บแคมของผู้ใช้ในหน้าต่างการแสดงภาพซ้อนภาพ

const video = document.createElement('video');
video.muted = true;
video.srcObject = await navigator.mediaDevices.getUserMedia({video: true});
video.play();

// Later on, video.requestPictureInPicture();

แสดงข้อมูลในหน้าต่างการแสดงภาพซ้อนภาพ

const video = document.createElement('video');
video.muted = true;
video.srcObject = await navigator.mediaDevices.getDisplayMedia({video: true});
video.play();

// Later on, video.requestPictureInPicture();

แสดงองค์ประกอบ Canvas ในหน้าต่างการแสดงภาพซ้อนภาพ

const canvas = document.createElement('canvas');
// Draw something to canvas.
canvas.getContext('2d').fillRect(0, 0, canvas.width, canvas.height);

const video = document.createElement('video');
video.muted = true;
video.srcObject = canvas.captureStream();
video.play();

// Later on, video.requestPictureInPicture();

การรวม canvas.captureStream() กับ Media Session API ช่วยให้คุณทำสิ่งต่อไปนี้ได้ สร้างหน้าต่างเพลย์ลิสต์เสียงใน Chrome 74 ดู ตัวอย่างเพลย์ลิสต์เสียง

เพลย์ลิสต์เสียงในหน้าต่างการแสดงภาพซ้อนภาพ
รูปที่ 2 เพลย์ลิสต์เสียงในหน้าต่างการแสดงภาพซ้อนภาพ

ตัวอย่าง การสาธิต และ Codelab

ดูตัวอย่างการแสดงภาพซ้อนภาพอย่างเป็นทางการเพื่อลองใช้การแสดงภาพซ้อนภาพ Web API

เราจะเผยแพร่การสาธิตและ Codelab ตามมา

สิ่งที่จะเกิดขึ้นหลังจากนี้

ขั้นแรก ไปที่หน้าสถานะการใช้งานเพื่อให้ทราบว่า ปัจจุบัน API มีการใช้งานใน Chrome และเบราว์เซอร์อื่นๆ

สิ่งที่คาดว่าจะเห็นในอนาคตอันใกล้มีดังนี้

การสนับสนุนเบราว์เซอร์

การรองรับ Picture-in-Picture Web API ใน Chrome, Edge, Opera และ Safari ได้ โปรดดูรายละเอียดที่ MDN

แหล่งข้อมูล

ขอขอบคุณ Mounir Lamouri และ Jennifer Apacible ที่ช่วยสร้างสรรค์ การแสดงภาพซ้อนภาพและความช่วยเหลือสำหรับบทความนี้ และขอขอบคุณทุกคน ที่เกี่ยวข้องกับความพยายามในการกำหนดมาตรฐาน