Pencere içinde pencere (PiP), kullanıcıların videoları kayan bir pencerede (her zaman diğer pencerelerin üzerinde) izlemesine olanak tanır. Böylece kullanıcılar, diğer sitelerle veya uygulamalarla etkileşimde bulunurken izledikleri içeriği gözden kaçırmaz.
Resim İçinde Resim Web API'si ile web sitenizdeki video öğeleri için Resim İçinde Resim özelliğini başlatabilir ve kontrol edebilirsiniz. Resmi Pencere içinde pencere örneğimizde deneyin.
Arka plan
Safari, Eylül 2016'da macOS Sierra'da bir WebKit API'si aracılığıyla Pencere İçinde Pencere desteği ekledi. Altı ay sonra, Android O'nun kullanıma sunulmasıyla Chrome, yerel bir Android API'si kullanarak mobil cihazlarda otomatik olarak Pencere İçinde Pencere videoları oynatmaya başladı. Altı ay sonra, Safari'nin API'siyle uyumlu olan ve web geliştiricilerin Pencere İçinde Pencere özelliğindeki tüm deneyimi oluşturup kontrol etmelerine olanak tanıyan bir Web API'si oluşturup standart hale getirmeyi amaçladığımızı duyurduk. İşte buradayız.
Kodu inceleme
Pencere içinde pencere moduna girme
Basit bir video öğesi ve kullanıcının bu öğeyle etkileşime geçebileceği bir yol (ör. düğme öğesi) ile başlayalım.
<video id="videoElement" src="https://example.com/file.mp4"></video>
<button id="pipButtonElement"></button>
Pencere içinde pencere özelliğini yalnızca kullanıcı hareketine yanıt olarak isteyin ve hiçbir zaman videoElement.play()
tarafından döndürülen promise içinde istemeyin. Bunun nedeni, kullanıcı hareketlerinin henüz söz vermeler aracılığıyla iletilmemesidir. Bunun yerine, aşağıdaki gibi pipButtonElement
üzerinde bir tıklama işleyicisinde requestPictureInPicture()
'ü çağırın. Kullanıcının iki kez tıklaması durumunda ne olacağını belirlemek sizin sorumluluğunuzdadır.
pipButtonElement.addEventListener('click', async function () {
pipButtonElement.disabled = true;
await videoElement.requestPictureInPicture();
pipButtonElement.disabled = false;
});
Sözleşme çözüldüğünde Chrome, videoyu kullanıcının taşıyabileceği ve diğer pencerelerin üzerine yerleştirebileceği küçük bir pencereye dönüştürür.
İşlem tamamlandı. Tebrikler! Okumayı bırakıp hak ettiğiniz tatile gidebilirsiniz. Maalesef bu her zaman geçerli değildir. Vaat, aşağıdaki herhangi bir nedenden dolayı reddedilebilir:
- Pencere içinde pencere özelliği sistem tarafından desteklenmiyor.
- Kısıtlayıcı izin politikası nedeniyle dokümanın Pencere İçinde Pencere özelliğini kullanmasına izin verilmiyor.
- Video meta verileri henüz yüklenmedi (
videoElement.readyState === 0
). - Video dosyası yalnızca ses içeriyor.
- Video öğesinde yeni
disablePictureInPicture
özelliği bulunur. - Çağırma, kullanıcı hareketi etkinlik işleyicisinde (ör. düğme tıklaması) yapılmadı. Chrome 74'ten itibaren bu özellik, Pencere İçinde Pencere'de henüz bir öğe yoksa yalnızca geçerlidir.
Aşağıdaki Özellik desteği bölümünde, bu kısıtlamalara göre bir düğmenin nasıl etkinleştirileceği/devre dışı bırakılacağı gösterilmektedir.
Bu olası hataları yakalamak ve kullanıcıya neler olduğunu bildirmek için bir try...catch
bloğu ekleyelim.
pipButtonElement.addEventListener('click', async function () {
pipButtonElement.disabled = true;
try {
await videoElement.requestPictureInPicture();
} catch (error) {
// TODO: Show error message to user.
} finally {
pipButtonElement.disabled = false;
}
});
Video öğesi, Pencere İçinde Pencere modunda olsun veya olmasın aynı şekilde çalışır: Etkinlikler tetiklenir ve çağrı yöntemleri çalışır. Pencere İçinde Pencere penceresindeki durum değişikliklerini (oynatma, duraklatma, sarma vb.) yansıtır ve durumu, JavaScript'te programlı bir şekilde değiştirmek de mümkündür.
Pencere içinde pencere modundan çıkma
Şimdi düğmemizi Pencere İçinde Pencere'ye girme ve çıkma düğmesi haline getirelim. Öncelikle, document.pictureInPictureElement
salt okunur nesnesinin bizim video öğemiz olup olmadığını kontrol etmemiz gerekiyor. Aksi takdirde, yukarıda belirtildiği gibi Pencere İçinde Pencere moduna girme isteği göndeririz. Aksi takdirde, document.exitPictureInPicture()
çağrısı yaparak ayrılmasını isteriz. Bu durumda video, orijinal sekmede tekrar gösterilir. Bu yöntemin aynı zamanda bir vaat döndürdüğünü unutmayın.
...
try {
if (videoElement !== document.pictureInPictureElement) {
await videoElement.requestPictureInPicture();
} else {
await document.exitPictureInPicture();
}
}
...
Pencere İçinde Pencere etkinliklerini dinleme
İşletim sistemleri genellikle pencere içinde pencere özelliğini tek bir pencereyle kısıtlar. Bu nedenle Chrome'un uygulaması da bu kalıbı izler. Bu, kullanıcıların tek seferde yalnızca bir Pencere İçinde Pencere video oynatabileceği anlamına gelir. Kullanıcıların, istemediğiniz halde Pencere İçinde Pencere özelliğinden çıkmasını bekleyebilirsiniz.
Yeni enterpictureinpicture
ve leavepictureinpicture
etkinlik işleyiciler, deneyimi kullanıcılar için özelleştirmemize olanak tanıyor. Video kataloğuna göz atma veya canlı yayın sohbetlerini öne çıkarma gibi işlemler bu kapsamdadır.
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.
});
Pencere içinde pencereyi özelleştirme
Chrome 74, Media Session API'yi kullanarak kontrol edebileceğiniz Pencere İçinde Pencere penceresindeki oynat/duraklat, önceki parça ve sonraki parça düğmelerini destekler.
Videoda MediaStream nesneleri (ör. getUserMedia()
, getDisplayMedia()
, canvas.captureStream()
) oynatılmadığı veya videonun MediaSource süresi +Infinity
olarak ayarlanmadığı (ör. canlı feed) sürece varsayılan olarak, Pencere İçinde Pencere penceresinde her zaman bir oynat/duraklat düğmesi gösterilir. Bir oynatma/duraklatma düğmesinin her zaman görünür olduğundan
emin olmak üzere, aşağıdaki gibi hem "Oynat" hem de "Duraklat"
medya etkinlikleri için Medya Oturumu işlem işleyicileri ayarlayın.
// 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.
});
"Önceki parça" ve "Sonraki parça" pencere denetimlerini gösterme işlemi benzerdir. Bunlar için Medya Oturumu işlem işleyicileri ayarlamak, bunları Pencere İçinde Pencere penceresinde gösterir. Böylece bu işlemleri gerçekleştirebilirsiniz.
navigator.mediaSession.setActionHandler('previoustrack', function () {
// User clicked "Previous Track" button.
});
navigator.mediaSession.setActionHandler('nexttrack', function () {
// User clicked "Next Track" button.
});
Bu özelliğin nasıl çalıştığını görmek için resmi Medya Oturumu örneğini deneyin.
Pencere içinde pencere penceresinin boyutunu alma
Video, pencere içinde pencereye girip çıktığında video kalitesini ayarlamak istiyorsanız pencere içinde pencere boyutunu bilmeniz ve kullanıcı pencereyi manuel olarak yeniden boyutlandırdığında bildirim almanız gerekir.
Aşağıdaki örnekte, Pencere İçinde Pencere penceresi oluşturulurken veya yeniden boyutlandırılırken genişliğinin ve yüksekliğinin nasıl alınacağı gösterilmektedir.
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.
}
Pencere içinde pencere boyutunda yapılan her küçük değişiklik ayrı bir etkinlik tetikler. Bu da her yeniden boyutlandırmada pahalı bir işlem yapıyorsanız performans sorunlarına neden olabilir. Bu nedenle, doğrudan yeniden boyutlandırma etkinliğine bağlanmanızı önermeyiz. Diğer bir deyişle, yeniden boyutlandırma işlemi etkinlikleri ��ok hızlı bir şekilde tekrar tekrar tetikler. Bu sorunu gidermek için düşük hızlandırma ve yeniden başlatma gibi yaygın teknikleri kullanmanızı öneririm.
Özellik desteği
Pencere İçinde Pencere Web API'si desteklenmeyebilir. Bu nedenle, progresif geliştirme sağlamak için bunu tespit etmeniz gerekir. Desteklense bile kullanıcı tarafından kapatılmış veya izin politikası tarafından devre dışı bırakılmış olabilir. Neyse ki bunu belirlemek için yeni boole document.pictureInPictureEnabled
değerini kullanabilirsiniz.
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.');
}
Videonun belirli bir düğme öğesine uygulandığında, Pencere İçinde Pencere düğmenizin görünürlüğünü bu şekilde yönetebilirsiniz.
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 video desteği
MediaStream nesnelerine (ör. getUserMedia()
, getDisplayMedia()
,
canvas.captureStream()
) oynatılan video, Chrome 71'de Pencere İçinde Pencereyi de destekler. Bu, kullanıcının web kamerası video akışını, görüntülü video akışını veya hatta bir kanvas öğesini içeren bir Pencere İçinde Pencere penceresi gösterebileceğiniz anlamına gelir. Video öğesinin, aşağıdaki gibi küçük resim moduna girmek için DOM'a eklenmesinin gerekmediğini unutmayın.
Kullanıcının web kamerasını Pencere İçinde Pencere penceresinde göster
const video = document.createElement('video');
video.muted = true;
video.srcObject = await navigator.mediaDevices.getUserMedia({video: true});
video.play();
// Later on, video.requestPictureInPicture();
Ekranı Pencere içinde pencere penceresinde gösterme
const video = document.createElement('video');
video.muted = true;
video.srcObject = await navigator.mediaDevices.getDisplayMedia({video: true});
video.play();
// Later on, video.requestPictureInPicture();
Tuval öğesini Pencere içinde pencere penceresinde gösterme
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()
'yi Media Session API ile birlikte kullanarak örneğin Chrome 74'te ses çalma listesi penceresi oluşturabilirsiniz. Resmi Sesli oynatma listesi örneği'ne göz atın.
Örnekler, demolar ve codelab'ler
Pencere İçinde Pencere Web API'sini denemek için resmi Pencere İçinde Pencere örneğimize göz atın.
Ardından, tanıtımlar ve codelab'ler yayınlanacaktır.
Sırada ne var?
Öncelikle, API'nin hangi bölümlerinin şu anda Chrome ve diğer tarayıcılarda uygulandığını öğrenmek için uygulama durumu sayfasına göz atın.
Yakın gelecekte aşağıdaki değişiklikleri görebilirsiniz:
- Web geliştiricileri özel Pencere İçinde Pencere kontrolleri ekleyebilir.
- İsteğe bağlı
HTMLElement
nesnelerini kayan bir pencerede görüntülemek için yeni bir Web API sağlanacaktır.
Tarayıcı desteği
Pencere İçinde Pencere Web API'si Chrome, Edge, Opera ve Safari'de desteklenir. Ayrıntılar için MDN'ye bakın.
Kaynaklar
- Chrome Özellik Durumu: https://www.chromestatus.com/feature/5729206566649856
- Chrome Uygulama Hataları: https://crbug.com/?q=component:Blink>Media>PictureInPicture
- Pencere İçinde Pencere Web API Spesifikasyonu: https://wicg.github.io/picture-in-picture
- Spesifik sorunlar: https://github.com/WICG/picture-in-picture/issues
- Örnek: https://googlechrome.github.io/samples/picture-in-picture/
- Resim İçinde Resim resmi olmayan polyfill'i: https://github.com/gbentaieb/pip-polyfill/
Pencere İçinde Pencere özelliğindeki çalışmaları ve bu makaledeki yardımları için Mounir Lamouri ve Jennifer Apacible'a teşekkür ederiz. Standartlaştırma çalışmalarına katılan herkese çok teşekkür ederiz.