Picture-in-Picture (PiP) memungkinkan pengguna menonton video dalam jendela mengambang (selalu berada di atas jendela lain) sehingga mereka dapat terus melihat video yang sedang mereka tonton sambil berinteraksi dengan situs atau aplikasi lain.
Dengan Picture-in-Picture Web API, Anda dapat memulai dan mengontrol Picture-in-Picture untuk elemen video di situs Anda. Coba di contoh Picture-in-Picture resmi kami.
Latar belakang
Pada September 2016, Safari menambahkan dukungan Picture-in-Picture melalui WebKit API di macOS Sierra. Enam bulan kemudian, Chrome otomatis memutar video Picture-in-Picture di perangkat seluler dengan rilis Android O menggunakan API Android native. Enam bulan kemudian, kami mengumumkan niat kami untuk mem-build dan menstandarkan Web API, fitur yang kompatibel dengan Safari, yang akan memungkinkan developer web membuat dan mengontrol pengalaman lengkap seputar Picture-in-Picture. Dan kita sudah sampai.
Mempelajari kode
Masuk ke Mode Picture-in-Picture
Mari kita mulai dengan elemen video dan cara pengguna berinteraksi dengannya, seperti elemen tombol.
<video id="videoElement" src="https://example.com/file.mp4"></video>
<button id="pipButtonElement"></button>
Hanya minta Picture-in-Picture sebagai respons terhadap gestur pengguna, dan jangan pernah dalam
promise yang ditampilkan oleh videoElement.play()
. Hal ini karena promise belum
menyebarkan gestur pengguna. Sebagai gantinya, panggil requestPictureInPicture()
dalam
pengendali klik di pipButtonElement
seperti yang ditunjukkan di bawah. Anda bertanggung jawab
untuk menangani apa yang terjadi jika pengguna mengklik dua kali.
pipButtonElement.addEventListener('click', async function () {
pipButtonElement.disabled = true;
await videoElement.requestPictureInPicture();
pipButtonElement.disabled = false;
});
Saat promise diselesaikan, Chrome akan mengecilkan video menjadi jendela kecil yang dapat dipindahkan dan diposisikan oleh pengguna di atas jendela lain.
Selesai. Bagus! Anda dapat berhenti membaca dan pergi berlibur yang pantas Anda dapatkan. Sayangnya, hal ini tidak selalu terjadi. Promise dapat ditolak karena salah satu alasan berikut:
- Picture-in-Picture tidak didukung oleh sistem.
- Dokumen tidak diizinkan untuk menggunakan Picture-in-Picture karena kebijakan izin yang membatasi.
- Metadata video belum dimuat (
videoElement.readyState === 0
). - File video hanya berisi audio.
- Atribut
disablePictureInPicture
baru ada di elemen video. - Panggilan tidak dilakukan di pengendali peristiwa gestur pengguna (misalnya, klik tombol). Mulai Chrome 74, hal ini hanya berlaku jika belum ada elemen di Picture-in-Picture.
Bagian Dukungan fitur di bawah menunjukkan cara mengaktifkan/menonaktifkan tombol berdasarkan batasan ini.
Mari kita tambahkan blok try...catch
untuk menangkap potensi error ini dan memberi tahu
pengguna apa yang terjadi.
pipButtonElement.addEventListener('click', async function () {
pipButtonElement.disabled = true;
try {
await videoElement.requestPictureInPicture();
} catch (error) {
// TODO: Show error message to user.
} finally {
pipButtonElement.disabled = false;
}
});
Elemen video berperilaku sama, baik dalam Picture-in-Picture maupun tidak: peristiwa diaktifkan dan metode panggilan berfungsi. Ini mencerminkan perubahan status di jendela Picture-in-Picture (seperti putar, jeda, cari, dll.) dan juga dapat mengubah status secara terprogram di JavaScript.
Keluar dari Picture-in-Picture
Sekarang, mari kita buat tombol untuk beralih masuk dan keluar dari Picture-in-Picture. Kita
harus memeriksa terlebih dahulu apakah objek hanya baca document.pictureInPictureElement
adalah elemen video kita. Jika tidak, kami akan mengirim permintaan untuk masuk
ke Picture-in-Picture seperti di atas. Jika tidak, kita meminta untuk keluar dengan memanggil
document.exitPictureInPicture()
, yang berarti video akan muncul kembali di
tab asli. Perhatikan bahwa metode ini juga menampilkan promise.
...
try {
if (videoElement !== document.pictureInPictureElement) {
await videoElement.requestPictureInPicture();
} else {
await document.exitPictureInPicture();
}
}
...
Memproses peristiwa Picture-in-Picture
Sistem operasi biasanya membatasi Picture-in-Picture ke satu jendela, sehingga penerapan Chrome mengikuti pola ini. Artinya, pengguna hanya dapat memutar satu video Picture-in-Picture dalam satu waktu. Anda harus mengharapkan pengguna keluar dari Picture-in-Picture meskipun Anda tidak memintanya.
Pengendali peristiwa enterpictureinpicture
dan leavepictureinpicture
baru memungkinkan
kita menyesuaikan pengalaman untuk pengguna. Hal ini bisa berupa apa saja, mulai dari menjelajahi
katalog video, hingga menampilkan chat live stream.
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.
});
Menyesuaikan jendela Picture-in-Picture
Chrome 74 mendukung tombol putar/jeda, trek sebelumnya, dan trek berikutnya di jendela Picture-in-Picture yang dapat Anda kontrol menggunakan Media Session API.
Secara default, tombol putar/jeda selalu ditampilkan di jendela Picture-in-Picture
kecuali jika video memutar objek MediaStream (misalnya, getUserMedia()
,
getDisplayMedia()
, canvas.captureStream()
) atau video memiliki durasi MediaSource
yang disetel ke +Infinity
(misalnya, feed live). Untuk memastikan tombol putar/jeda
selalu terlihat, tetapkan pengendali tindakan Sesi Media untuk peristiwa media "Putar" dan
"Jeda" seperti di bawah.
// 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.
});
Cara menampilkan kontrol jendela "Lagu Sebelumnya" dan "Lagu Berikutnya" serupa. Menetapkan pengendali tindakan Sesi Media untuk tindakan tersebut akan menampilkannya di jendela Picture-in-Picture dan Anda akan dapat menangani tindakan ini.
navigator.mediaSession.setActionHandler('previoustrack', function () {
// User clicked "Previous Track" button.
});
navigator.mediaSession.setActionHandler('nexttrack', function () {
// User clicked "Next Track" button.
});
Untuk melihat cara kerjanya, coba contoh Sesi Media resmi.
Mendapatkan ukuran jendela Picture-in-Picture
Jika ingin menyesuaikan kualitas video saat video masuk dan keluar dari Picture-in-Picture, Anda perlu mengetahui ukuran jendela Picture-in-Picture dan diberi tahu jika pengguna mengubah ukuran jendela secara manual.
Contoh di bawah menunjukkan cara mendapatkan lebar dan tinggi jendela Picture-in-Picture saat dibuat atau diubah ukurannya.
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.
}
Sebaiknya jangan melakukan hook langsung ke peristiwa pengubahan ukuran karena setiap perubahan kecil yang dilakukan pada ukuran jendela Picture-in-Picture akan memicu peristiwa terpisah yang dapat menyebabkan masalah performa jika Anda melakukan operasi yang mahal pada setiap pengubahan ukuran. Dengan kata lain, operasi pengubahan ukuran akan memicu peristiwa berulang kali dengan sangat cepat. Sebaiknya gunakan teknik umum seperti throttling dan debouncing untuk mengatasi masalah ini.
Dukungan fitur
Picture-in-Picture Web API mungkin tidak didukung, jadi Anda harus mendeteksinya
untuk memberikan progressive enhancement. Meskipun didukung, fitur ini dapat
dinonaktifkan oleh pengguna atau dinonaktifkan oleh kebijakan izin. Untungnya, Anda dapat menggunakan
document.pictureInPictureEnabled
boolean baru untuk menentukannya.
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.');
}
Diterapkan ke elemen tombol tertentu untuk video, berikut cara menangani visibilitas tombol Picture-in-Picture.
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;
}
Dukungan video MediaStream
Video yang memutar objek MediaStream (misalnya, getUserMedia()
, getDisplayMedia()
,
canvas.captureStream()
) juga mendukung Picture-in-Picture di Chrome 71. Artinya,
Anda dapat menampilkan jendela Picture-in-Picture yang berisi streaming video webcam
pengguna, streaming video tampilan, atau bahkan elemen kanvas. Perhatikan bahwa
elemen video tidak harus dilampirkan ke DOM untuk memasuki
Picture-in-Picture seperti yang ditunjukkan di bawah.
Menampilkan webcam pengguna di jendela Picture-in-Picture
const video = document.createElement('video');
video.muted = true;
video.srcObject = await navigator.mediaDevices.getUserMedia({video: true});
video.play();
// Later on, video.requestPictureInPicture();
Menampilkan tampilan di jendela Picture-in-Picture
const video = document.createElement('video');
video.muted = true;
video.srcObject = await navigator.mediaDevices.getDisplayMedia({video: true});
video.play();
// Later on, video.requestPictureInPicture();
Menampilkan elemen kanvas di jendela Picture-in-Picture
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();
Dengan menggabungkan canvas.captureStream()
dengan Media Session API, Anda dapat
misalnya membuat jendela playlist audio di Chrome 74. Lihat
Contoh playlist audio resmi.
Contoh, demo, dan codelab
Lihat contoh Picture-in-Picture resmi kami untuk mencoba Picture-in-Picture Web API.
Demo dan codelab akan menyusul.
Langkah selanjutnya
Pertama, lihat halaman status penerapan untuk mengetahui bagian API mana yang saat ini diterapkan di Chrome dan browser lainnya.
Berikut hal-hal yang akan Anda lihat dalam waktu dekat:
- Developer web akan dapat menambahkan kontrol Picture-in-Picture kustom.
- Web API baru akan disediakan untuk menampilkan objek
HTMLElement
arbitrer di jendela mengambang.
Dukungan browser
Picture-in-Picture Web API didukung di Chrome, Edge, Opera, dan Safari. Lihat MDN untuk mengetahui detailnya.
Resource
- Status Fitur Chrome: https://www.chromestatus.com/feature/5729206566649856
- Bug Penerapan Chrome: https://crbug.com/?q=component:Blink>Media>PictureInPicture
- Spesifikasi Picture-in-Picture Web API: https://wicg.github.io/picture-in-picture
- Masalah Spesifikasi: https://github.com/WICG/picture-in-picture/issues
- Contoh: https://googlechrome.github.io/samples/picture-in-picture/
- Polyfill Picture-in-Picture tidak resmi: https://github.com/gbentaieb/pip-polyfill/
Terima kasih banyak kepada Mounir Lamouri dan Jennifer Apacible atas pekerjaan mereka terkait Picture-in-Picture, dan bantuan mereka dalam membuat artikel ini. Dan terima kasih banyak kepada semua orang yang terlibat dalam upaya standardisasi.