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

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() แสดง เนื่องจาก Promise ยังไม่ได้เริ่มเผยแพร่ท่าทางสัมผัสของผู้ใช้ ให้เรียกใช้ 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();
      }
    }
    ...

ฟังเหตุการณ์การแสดงภาพซ้อนภาพ

ระบบปฏิบัติการมักจะจำกัดฟีเจอร์ภาพในภาพไว้ที่ 1 หน้าต่าง ดังนั้นการใช้งานของ 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 ที่ช่วยพัฒนาฟีเจอร์ภาพในภาพและช่วยเขียนบทความนี้ และขอขอบคุณอย่างยิ่งทุกคนที่มีส่วนร่วมในความพยายามในการทำให้มาตรฐาน