และแล้วทำ Picture in Picture บน Element อะไรก็ได้

วันก่อน ประชุม Google meet ก็เหลือบไปเห็นว่า มันมี UI ที่เป็น Pictue in picture แบบนี้โผล่ออกมา ก็ตกใจว่ามันทำได้ยังไงเพราะว่า มัน support แต่ Video นิ ก็เลยค้นจนไปเจอว่า ตอนนี้ใน Chrome เราสามารถทำ Picture in Picture บน element อะไรก็ได้แล้ว เอาจริงๆเหมือนง่ายนะแต่มีจุดงง อยู่หลายจุดมากๆเลย มาดูกันว่าต้องทำยังไงบ้าง โดยที่ผมจะทำตัวอย่างเป็น application แชท

How-to

1.เราต้องสร้าง element ที่เราจะเอาไปยัดใน PIP ขึ้นมาก่อน สมมติว่าผมสร้าง element ที่เป็นแชทขึ้นมา แล้วหลังจากนั้นก็สร้าง ปุ่มที่จะ active pip ขึ้นมา แล้วพอปุ่มนั้น active ก็ให้ย้าย Element ที่เรากำหนดไปไว้ใน PiP window จริงๆแล้วเราไม่จำเป็นต้อง detect เฉพาะ click event ก็ได้ เราอาจจะเอามาใช้กับ visibility event เพื่อให้ active PiP window ตอนที่ tab โดน hide หรือ เปลี่ยน tab ก็ได้ แต่จะมีข้อแม้นึงคือ PiP จะ active ได้ user ต้องมี interactive เช่น scroll หรือ click บนหน้านั้นๆมาก่อน

pipButton.addEventListener('click', async () => {
const chatScreen = document.querySelector("#chat-screen");
// Open a Picture-in-Picture window.
const pipWindow = await documentPictureInPicture.requestWindow();
// Move the player to the Picture-in-Picture window.
pipWindow.document.body.append(chatScreen);
});
view raw pip.js hosted with ❤ by GitHub

2. ตัว PiP เนี่ยมันมองว่าเป็นหน้าต่างๆนึง เพราะฉะนั้น การบอกว่าจะเอา Element ไหนย้ายไป มันคือย้ายไปจริงๆแบบ ถอดออกจากหน้าหลักแล้วย้ายไปที่หน้าต่าง PiP เลย

3. ปัญหามันจะเกิดก็ตรงนี้นี่แหละ เพราะ PiP ย้ายไปทุกอย่าง แต่ไม่ย้าย CSS ไป.. ซึ่งเราต้อง Move เอา CSS จาก หน้าหลก ไปไว้ใน PiP ผ่าน Code แบบนี้ในตอนที่เรา active PiP ขึ้นมา ถ้าเอาแบบง่ายๆก็ใช้วิธีก็เถื่อนๆเลยคือ หา tag style ทั้งหน้าแล้วก็ append เข้าไปใน PiP windows เลย แต่ถ้าเราตั้งสตินิดนึงก็อาจจะใช้วิธีแบบที่สร้าง css file ขึ้นมาแล้ว inject ไปก็ได้ แต่ก็ต้องมั่นใจว่า copy ไปถูก

pipButton.addEventListener("click", async () => {
const chatScreen = document.querySelector("#chat-screen");
// 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(chatScreen);
});
view raw pip2.js hosted with ❤ by GitHub

4. ความบันเทิงมันยังไม่จบเพราะพอเราย้าย Element ไปไว้ใน PiP แล้ว ตอนที่ PiP ปิด เราต้องไม่ลืมย้าย element นั้นกลับมา โดยที่เราต้อง ตอน Listen event ว่า PiP ปิดตอนไหนไว้ด้วยไม่อย่างงั้น เราจะไม่สามารถย้าย Element นั้นกลับมาได้เลยเพราะ โดย normal behavior แล้ว ตัว PiP จะให้เรากำหนดเองว่าจะย้ายไปย้ายมายังไงแบบ manual

pipWindow.addEventListener("pagehide", (event) => {
const chatScreen Container = document.querySelector("#chat-screen-container");
const pipChatScreen = event.target.querySelector("#chat-screen");
playerContainer.append(pipPlayer);
});
view raw pip3.js hosted with ❤ by GitHub

ต้องบอกว่าเหมือนจะไม่ยากมากแต่ถ้าไม่เข้าใจว่ามีส่วนต้องย้ายไปย้ายมาด้วย จะงงมากๆว่าทำไม PiP window ปิดไปแล้ว แต่ Element ยังไม่กลับมาที่หน้าหลัก

สำหรับตัว CSS เองเราก็สามารถกำหนด Media query display-mode: picture-in-picture ที่เป็นของ PiP window ได้เพื่อเราจะได้กำหนดได้ชัดเจนว่าตอนอยู่บนหน้าหลักต้องแสดงผลยังไง และบน PiP window ต้องแสดงผลยังไง ทำให้เรากำหนดการแสดงผลได้พอสมควร

@media all and (display-mode: picture-in-picture) {
……
}
view raw pip.css hosted with ❤ by GitHub

พอเรา implement code ตามนั้นเสร็จเราก็จะได้ PiP ของเราที่หน้าตาแบบนี้

ลองไปเล่น Demo กันได้ที่ https://glitch.com/edit/#!/kon-tum-web-view-pip-on-every-element นะครับ

มาลงรายละเอียดเพิ่มเรื่อง Function ที่ใช้สำหรับ active PiP กันหน่อย สำหรับ Option ที่เรา สามารถกำหนดใน requestWindow ได้ก็จะมีอยู่ 4 อย่าง

  • width – ความกว้างของหน้าต่าง
  • height – ความสูงของหน้าต่าง
  • disallowReturnToOpener – ปิดปุ่มสำหรับการ return ไปหน้าหลัก ต้องปิด PiP ไปเลยเท่านั้น (เอาจริงๆ ปุ่มนี้แทบไม่ต่างกับปุ่มปิด)
  • preferInitialWindowPlacement – เปิด PiP บน position ที่เป็น default

หมายเหตุ

สำหรับ feature นี้ตอนนี้ใช้ได้แค่ Chromium ที่รันบน Desktop เท่านั้น Firefox / Safari และ Mobile Browser ทั้งหมดยังใช้ไม่ได้นะครับ


Discover more from Thangman22's

Subscribe to get the latest posts sent to your email.

Leave a Reply

Discover more from Thangman22's

Subscribe now to keep reading and get access to the full archive.

Continue reading