Picture-in-Picture untuk Elemen apa pun, bukan hanya <video>

François Beaufort
François Beaufort

Dukungan Browser

  • Chrome: 116.
  • Edge: 116.
  • Firefox: tidak didukung.
  • Safari: tidak didukung.

Sumber

Document Picture-in-Picture API memungkinkan jendela selalu di atas yang dapat diisi dengan konten HTML arbitrer. Tindakan ini memperluas Picture-in-Picture API yang ada untuk <video> yang hanya memungkinkan elemen <video> HTML dimasukkan ke jendela Picture-in-Picture.

Jendela Picture-in-Picture di Document Picture-in-Picture API serupa dengan jendela asal yang sama kosong yang dibuka melalui window.open(), dengan beberapa perbedaan:

  • Jendela Picture-in-Picture mengambang di atas jendela lain.
  • Jendela Picture-in-Picture tidak pernah aktif lebih lama dibandingkan jendela yang dibuka.
  • Jendela Picture-in-Picture tidak dapat dibuka.
  • Posisi jendela Picture-in-Picture tidak dapat disetel oleh situs.
Jendela Picture-in-Picture yang memutar video cuplikan Sintel.
Jendela Picture-in-Picture yang dibuat dengan Document Picture-in-Picture API (demo).

Status saat ini

Langkah Status
1. Buat penjelasan Selesai
2. Membuat draf awal spesifikasi Dalam proses
3. Kumpulkan masukan & mengulangi desain Dalam proses
4. Uji coba origin Selesai
5. Luncurkan Selesai (Desktop)

Kasus penggunaan

Pemutar video khusus

Situs dapat memberikan pengalaman video Picture-in-Picture dengan Picture-in-Picture API untuk <video> yang ada, tetapi sangat terbatas. Jendela Picture-in-Picture yang ada menerima sedikit input, dan memiliki kemampuan terbatas untuk menata gayanya. Dengan Document in Picture-in-Picture yang lengkap, situs web dapat menyediakan kontrol dan input khusus (misalnya, teks, playlist, scrubber waktu, menyukai dan tidak menyukai video) untuk meningkatkan pengalaman video Picture-in-Picture pengguna.

Konferensi video

Umumnya pengguna meninggalkan tab browser selama sesi konferensi video karena berbagai alasan (misalnya, menampilkan tab lain untuk panggilan atau multitasking) sambil tetap ingin melihat panggilan, sehingga ini adalah kasus penggunaan utama untuk Picture-in-Picture. Sekali lagi, pengalaman yang saat ini dapat diberikan situs konferensi video melalui Picture-in-Picture API untuk <video> memiliki batasan gaya dan input. Dengan Document in Picture-in-Picture lengkap, situs dapat dengan mudah menggabungkan beberapa streaming video ke dalam satu jendela PiP tanpa harus bergantung pada peretasan kanvas dan memberikan kontrol kustom seperti mengirim pesan, membisukan pengguna lain, atau mengangkat tangan.

Produktivitas

Penelitian telah menunjukkan bahwa pengguna memerlukan lebih banyak cara untuk menjadi produktif di web. Document dalam Picture-in-Picture memberi aplikasi web fleksibilitas untuk mencapai lebih banyak hal. Baik itu pengeditan teks, pembuatan catatan, daftar tugas, pesan dan obrolan, atau alat desain dan pengembangan, aplikasi web kini dapat membuat kontennya selalu dapat diakses.

Antarmuka

Properti

documentPictureInPicture.window
Menampilkan jendela Picture-in-Picture saat ini jika ada. Jika tidak, menampilkan null.

Metode

documentPictureInPicture.requestWindow(options)

Menampilkan promise yang diselesaikan saat jendela Picture-in-Picture dibuka. Promise akan ditolak jika dipanggil tanpa gestur pengguna. Kamus options berisi anggota opsional berikut:

width
Menetapkan lebar awal jendela Picture-in-Picture.
height
Menyetel tinggi awal jendela Picture-in-Picture.
disallowReturnToOpener
Menyembunyikan "kembali ke tab" di jendela Picture-in-Picture jika benar. Nilainya salah secara default.

Acara

documentPictureInPicture.onenter
Diaktifkan pada documentPictureInPicture saat jendela Picture-in-Picture dibuka.

Contoh

HTML berikut menyiapkan pemutar video kustom dan elemen tombol untuk membuka pemutar video di jendela Picture-in-Picture.

<div id="playerContainer">
  <div id="player">
    <video id="video"></video>
  </div>
</div>
<button id="pipButton">Open Picture-in-Picture window</button>

Membuka jendela Picture-in-Picture

JavaScript berikut memanggil documentPictureInPicture.requestWindow() saat pengguna mengklik tombol untuk membuka jendela Picture-in-Picture yang kosong. Promise yang ditampilkan di-resolve dengan objek JavaScript jendela Picture-in-Picture. Pemutar video dipindahkan ke jendela tersebut menggunakan append().

pipButton.addEventListener('click', async () => {
  const player = document.querySelector("#player");

  // Open a Picture-in-Picture window.
  const pipWindow = await documentPictureInPicture.requestWindow();

  // Move the player to the Picture-in-Picture window.
  pipWindow.document.body.append(player);
});

Menyetel ukuran jendela Picture-in-Picture

Untuk menyetel ukuran jendela Picture-in-Picture, setel opsi width dan height dari documentPictureInPicture.requestWindow() ke ukuran jendela Picture-in-Picture yang diinginkan. Chrome dapat menjepit nilai opsi jika nilai terlalu besar atau terlalu kecil untuk menyesuaikan dengan ukuran jendela yang mudah digunakan.

pipButton.addEventListener("click", async () => {
  const player = document.querySelector("#player");

  // Open a Picture-in-Picture window whose size is
  // the same as the player's.
  const pipWindow = await documentPictureInPicture.requestWindow({
    width: player.clientWidth,
    height: player.clientHeight,
  });

  // Move the player to the Picture-in-Picture window.
  pipWindow.document.body.append(player);
});

Menyembunyikan "kembali ke tab" tombol jendela Picture-in-Picture

Untuk menyembunyikan tombol di jendela Picture-in-Picture yang memungkinkan pengguna kembali ke tab pembuka, setel opsi disallowReturnToOpener dari documentPictureInPicture.requestWindow() ke true.

pipButton.addEventListener("click", async () => {
  // Open a Picture-in-Picture window which hides the "back to tab" button.
  const pipWindow = await documentPictureInPicture.requestWindow({
    disallowReturnToOpener: true,
  });
});

Menyalin lembar gaya ke jendela Picture-in-Picture

Untuk menyalin semua style sheet CSS dari jendela asal, ulangi styleSheets yang secara eksplisit ditautkan ke atau disematkan dalam dokumen, lalu tambahkan ke jendela Picture-in-Picture. Perlu diperhatikan bahwa penyalinan ini hanya dapat dilakukan satu kali.

pipButton.addEventListener("click", async () => {
  const player = document.querySelector("#player");

  // 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(player);
});

Menangani saat jendela Picture-in-Picture ditutup

Proses peristiwa "pagehide" jendela untuk mengetahui kapan jendela Picture-in-Picture ditutup (baik karena situs memulainya atau pengguna menutupnya secara manual). Pengendali peristiwa adalah tempat yang baik untuk mendapatkan elemen kembali dari jendela Picture-in-Picture seperti yang ditunjukkan di bawah ini.

pipButton.addEventListener("click", async () => {
  const player = document.querySelector("#player");

  // Open a Picture-in-Picture window.
  const pipWindow = await documentPictureInPicture.requestWindow();

  // Move the player to the Picture-in-Picture window.
  pipWindow.document.body.append(player);

  // Move the player back when the Picture-in-Picture window closes.
  pipWindow.addEventListener("pagehide", (event) => {
    const playerContainer = document.querySelector("#playerContainer");
    const pipPlayer = event.target.querySelector("#player");
    playerContainer.append(pipPlayer);
  });
});

Tutup jendela Picture-in-Picture secara terprogram menggunakan metode close().

// Close the Picture-in-Picture window programmatically. 
// The "pagehide" event will fire normally.
pipWindow.close();

Dengarkan saat situs masuk ke mode Picture-in-Picture

Dengarkan peristiwa "enter" di documentPictureInPicture untuk mengetahui saat jendela Picture-in-Picture dibuka. Peristiwa berisi objek window untuk mengakses jendela Picture-in-Picture.

documentPictureInPicture.addEventListener("enter", (event) => {
  const pipWindow = event.window;
});

Mengakses elemen di jendela Picture-in-Picture

Akses elemen di jendela Picture-in-Picture dari objek yang ditampilkan oleh documentPictureInPicture.requestWindow(), atau dengan documentPictureInPicture.window seperti yang ditunjukkan di bawah.

const pipWindow = documentPictureInPicture.window;
if (pipWindow) {
  // Mute video playing in the Picture-in-Picture window.
  const pipVideo = pipWindow.document.querySelector("#video");
  pipVideo.muted = true;
}

Menangani peristiwa dari jendela Picture-in-Picture

Buat tombol dan kontrol serta respons peristiwa input pengguna seperti "click" seperti yang biasa Anda lakukan di JavaScript.

// Add a "mute" button to the Picture-in-Picture window.
const pipMuteButton = pipWindow.document.createElement("button");
pipMuteButton.textContent = "Mute";
pipMuteButton.addEventListener("click", () => { 
  const pipVideo = pipWindow.document.querySelector("#video");
  pipVideo.muted = true;
});
pipWindow.document.body.append(pipMuteButton);

Mengubah ukuran jendela Picture-in-Picture

Gunakan metode Jendela resizeBy() dan resizeTo() untuk mengubah ukuran jendela Picture-in-Picture. Kedua metode ini memerlukan gestur pengguna.

const resizeButton = pipWindow.document.createElement('button');
resizeButton.textContent = 'Resize';
resizeButton.addEventListener('click', () => {
  // Expand the Picture-in-Picture window's width by 20px and height by 30px.
  pipWindow.resizeBy(20, 30);
});
pipWindow.document.body.append(resizeButton);

Memfokuskan jendela pembuka

Gunakan metode Jendela focus() untuk memfokuskan jendela pembuka dari jendela Picture-in-Picture. Metode ini memerlukan gestur pengguna.

const returnToTabButton = pipWindow.document.createElement("button");
returnToTabButton.textContent = "Return to opener tab";
returnToTabButton.addEventListener("click", () => {
  window.focus();
});
pipWindow.document.body.append(returnToTabButton);

Mode tampilan picture-in-picture CSS

Gunakan mode tampilan picture-in-picture CSS untuk menulis aturan CSS tertentu yang hanya diterapkan saat (bagian dari) aplikasi web ditampilkan dalam mode Picture-in-Picture.

@media all and (display-mode: picture-in-picture) {
  body {
    margin: 0;
  }
  h1 {
    font-size: 0.8em;
  }
}

Deteksi fitur

Untuk memeriksa apakah Document Picture-in-Picture API didukung, gunakan:

if ('documentPictureInPicture' in window) {
  // The Document Picture-in-Picture API is supported.
}

Demo

Pemutar videoJS

Anda dapat bermain dengan demo pemutar VideoJS Document Picture-in-Picture API. Pastikan untuk memeriksa kode sumber.

Pomodoro

Tomodoro, sebuah aplikasi web pomodoro, juga memanfaatkan Document Picture-in-Picture API jika tersedia (lihat permintaan pull GitHub).

Screenshot Tomodoro, aplikasi web pomodoro.
Jendela Picture-in-Picture di Tomodoro.

Masukan

Harap laporkan masalah di GitHub beserta saran dan pertanyaan.

Ucapan terima kasih

Banner besar oleh Jakob Owens.