Picture-in-Picture (PiP) memungkinkan pengguna menonton video dalam jendela mengambang (selalu berada di atas jendela lain) sehingga mereka dapat mengawasi apa yang mereka tonton saat 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. Cobalah 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 membuat dan menstandarkan Web API, yakni fitur yang kompatibel dengan Safari, yang memungkinkan developer web membuat dan mengontrol pengalaman penuh seputar Picture-in-Picture. Dan inilah dia!
Memahami kode
Masuk ke mode Picture-in-Picture
Mari kita mulai dengan elemen video dan cara bagi pengguna untuk 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 belum menyebarkan gestur pengguna. Sebagai gantinya, panggil requestPictureInPicture()
dalam
pengendali klik pada pipButtonElement
seperti yang ditunjukkan di bawah ini. 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;
});
Setelah promise ini selesai, Chrome akan memperkecil video menjadi jendela kecil yang dapat dipindahkan dan diletakkan pengguna di atas jendela lain.
Tugas Anda sudah selesai! Selamat! Anda bisa berhenti membaca dan menikmati liburan yang pantas. Sayangnya, hal itu 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 membatasi.
- Metadata video belum dimuat (
videoElement.readyState === 0
). - File video hanya berupa audio.
- Atribut
disablePictureInPicture
baru ada di elemen video. - Panggilan tidak dibuat dalam pengendali peristiwa gestur pengguna (mis., klik tombol). Mulai Chrome 74, hal ini hanya berlaku jika belum ada elemen dalam Picture-in-Picture.
Bagian Dukungan fitur di bawah ini 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 atau tidak: peristiwa diaktifkan dan metode panggilan berfungsi. Ini mencerminkan perubahan status di jendela Picture-in-Picture (seperti putar, jeda, cari, dll.) dan juga memungkinkan untuk mengubah status secara terprogram dalam JavaScript.
Keluar dari Picture-in-Picture
Sekarang, mari kita buat tombol beralih masuk dan keluar dari Picture-in-Picture. Pertama-tama, kita harus memeriksa apakah objek hanya baca document.pictureInPictureElement
adalah elemen video kita. Jika tidak, kita akan mengirim permintaan untuk memasukkan
Picture-in-Picture seperti di atas. Jika tidak, kita akan meminta untuk keluar dengan memanggil
document.exitPictureInPicture()
, yang berarti video akan muncul kembali di
tab awal. Perhatikan, 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 implementasi Chrome mengikuti pola ini. Artinya, pengguna hanya dapat memutar satu video Picture-in-Picture dalam satu waktu. Anda harus memperkirakan pengguna akan keluar dari Picture-in-Picture meskipun Anda tidak memintanya.
Pengendali peristiwa enterpictureinpicture
dan leavepictureinpicture
baru memungkinkan kami menyesuaikan pengalaman untuk pengguna. Hal itu bisa berupa apa pun, mulai dari menelusuri
katalog video hingga menampilkan chat livestream.
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.
![Kontrol pemutaran media di jendela Picture-in-Picture](https://developer.chrome.com/static/blog/watch-video-using-picture-in-picture/image/media-playback-controls-b73d819a65211.png?authuser=6&hl=id)
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 ditetapkan ke +Infinity
(misalnya, feed live). Untuk memastikan tombol putar/jeda
selalu terlihat, tetapkan pengendali tindakan Sesi Media somesee untuk peristiwa media "Putar" dan
"Jeda" seperti di bawah ini.
// 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.
});
Menampilkan kontrol jendela "Jalur Sebelumnya" dan "Jalur berikutnya" serupa. Menyetel pengendali tindakan Sesi Media untuk pengendali 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 penerapannya, 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 mengaitkan langsung ke peristiwa perubahan 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 mahal pada setiap perubahan ukuran. Dengan kata lain, operasi pengubahan ukuran akan mengaktifkan 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, sehingga Anda harus mendeteksinya
untuk memberikan progressive enhancement. Meskipun didukung, aplikasi dapat
dinonaktifkan oleh pengguna atau dinonaktifkan oleh kebijakan izin. Untungnya, Anda dapat menggunakan
boolean document.pictureInPictureEnabled
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, beginilah cara Anda 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 masuk ke
Picture-in-Picture seperti yang ditunjukkan di bawah.
Tampilkan 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();
Tampilkan 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();
Tampilkan 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
membuat jendela playlist audio di Chrome 74, misalnya. Lihat Contoh playlist audio resmi.
![Playlist audio di jendela Picture-in-Picture](https://developer.chrome.com/static/blog/watch-video-using-picture-in-picture/image/audio-playlist-a-picture-4c2f236920dae.jpeg?authuser=6&hl=id)
Contoh, demo, dan codelab
Lihat contoh Picture-in-Picture resmi kami untuk mencoba Picture-in-Picture Web API.
Demo dan codelab akan mengikuti.
Langkah selanjutnya
Pertama, lihat halaman status implementasi untuk mengetahui bagian API mana yang saat ini diterapkan di Chrome dan browser lain.
Berikut ini 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.
Referensi
- Status Fitur Chrome: https://www.chromestatus.com/feature/5729206566649856
- Bug Implementasi Chrome: https://crbug.com/?q=component:Blink>Media>PictureInPicture
- Spesifikasi API Web Picture-in-Picture: 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 upaya mereka dalam Picture-in-Picture, serta bantuan dalam artikel ini. Dan terima kasih banyak kepada semua orang yang terlibat dalam upaya standardisasi.