การแสดงภาพซ้อนภาพ (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
โดยค่าเริ่มต้น ปุ่มเล่น/หยุดชั่วคราวจะแสดงในการแสดงภาพซ้อนภาพเสมอ
ยกเว้นกรณีที่วิดีโอกำลังเล่นออบเจ็กต์ 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 ดู
ตัวอย่างเพลย์ลิสต์เสียง
ตัวอย่าง การสาธิต และ Codelab
ดูตัวอย่างการแสดงภาพซ้อนภาพอย่างเป็นทางการเพื่อลองใช้การแสดงภาพซ้อนภาพ Web API
เราจะเผยแพร่การสาธิตและ Codelab ตามมา
สิ่งที่จะเกิดขึ้นหลังจากนี้
ขั้นแรก ไปที่หน้าสถานะการใช้งานเพื่อให้ทราบว่า ปัจจุบัน API มีการใช้งานใน Chrome และเบราว์เซอร์อื่นๆ
สิ่งที่คาดว่าจะเห็นในอนาคตอันใกล้มีดังนี้
- นักพัฒนาเว็บจะเพิ่มการควบคุมการแสดงภาพซ้อนภาพที่กำหนดเองได้
- เรามี Web API ใหม่ ที่จะแสดงออบเจ็กต์
HTMLElement
ที่กำหนดเองในหน้าต่างแบบลอย
การสนับสนุนเบราว์เซอร์
การรองรับ Picture-in-Picture Web API ใน Chrome, Edge, Opera และ Safari ได้ โปรดดูรายละเอียดที่ MDN
แหล่งข้อมูล
- สถานะฟีเจอร์ Chrome: https://www.chromestatus.com/feature/5729206566649856
- ข้อบกพร่องในการใช้งาน Chrome: https://crbug.com/?q=component:Blink>Media>PictureInPicture
- ข้อกำหนด API บนเว็บสำหรับการแสดงภาพซ้อนภาพ: https://wicg.github.io/picture-in-picture
- ปัญหาเกี่ยวกับข้อมูลจำเพาะ: https://github.com/WICG/picture-in-picture/issues
- ตัวอย่าง: https://googlechrome.github.io/samples/picture-in-picture/
- การแสดงภาพซ้อนภาพอย่างไม่เป็นทางการ: https://github.com/gbentaieb/pip-polyfill/
ขอขอบคุณ Mounir Lamouri และ Jennifer Apacible ที่ช่วยสร้างสรรค์ การแสดงภาพซ้อนภาพและความช่วยเหลือสำหรับบทความนี้ และขอขอบคุณทุกคน ที่เกี่ยวข้องกับความพยายามในการกำหนดมาตรฐาน