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

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>

ขอภาพซ้อนภาพเมื่อตอบสนองต่อท่าทางสัมผัสของผู้ใช้เท่านั้น และอย่าขอในสัญญาที่ 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.
}

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

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

อุปกรณ์อาจไม่รองรับ 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 ด้วย ซึ่งหมายความว่าคุณสามารถแสดงหน้าต่างภาพซ้อนภาพที่มีสตรีมวิดีโอจากเว็บแคมของผู้ใช้ สตรีมวิดีโอที่แสดง หรือแม้แต่องค์ประกอบภาพพิมพ์แคนวาส โปรดทราบว่าไม่จำเป็นต้องแนบองค์ประกอบวิดีโอกับ 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 และเบราว์เซอร์อื่นๆ

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

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

Chrome, Edge, Opera และ Safari รองรับ Web API ภาพในภาพ ดูรายละเอียดได้ที่ MDN

แหล่งข้อมูล

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