La modalità Picture in picture (PIP) consente agli utenti di guardare i video in una finestra mobile (sempre in cima alle altre finestre) in modo da poter tenere d'occhio ciò che stanno guardando mentre interagiscono con altri siti o applicazioni.
Con l'API web Picture-in-Picture, puoi avviare e controllare la modalità Picture-in-Picture per gli elementi video sul tuo sito web. Prova la funzionalità sul nostro esempio ufficiale di Picture in picture.
Sfondo
A settembre 2016, Safari ha aggiunto il supporto della modalità Picture-in-Picture tramite un'API WebKit in macOS Sierra. Sei mesi dopo, Chrome riproduceva automaticamente i video in Picture in Picture sui dispositivi mobili con il rilascio di Android O utilizzando un'API Android nativa. Sei mesi dopo, abbiamo annunciato la nostra intenzione di creare e standardizzare un'API web, compatibile con quella di Safari, che consenta agli sviluppatori web di creare e controllare l'esperienza completa relativa al Picture-in-Picture. Ed eccoci qui
Esplorare il codice
Attivare Picture in picture
Iniziamo semplicemente con un elemento video e un modo per consentire all'utente di interagire con esso, ad esempio un elemento pulsante.
<video id="videoElement" src="https://example.com/file.mp4"></video>
<button id="pipButtonElement"></button>
Richiedi la modalità Picture in picture solo in risposta a un gesto dell'utente e mai nel
promise restituito da videoElement.play()
. Questo perché le promesse non propaggono
ancora i gesti dell'utente. Chiama invece requestPictureInPicture()
in un gestore dei clic su pipButtonElement
come mostrato di seguito. È tua responsabilità gestire cosa succede se un utente fa clic due volte.
pipButtonElement.addEventListener('click', async function () {
pipButtonElement.disabled = true;
await videoElement.requestPictureInPicture();
pipButtonElement.disabled = false;
});
Quando la promessa viene risolta, Chrome riduce il video in una piccola finestra che l'utente può spostare e posizionare sopra altre finestre.
Hai finito. Ottimo lavoro. Puoi smettere di leggere e partire per le tue meritate vacanze. Purtroppo, non è sempre così. La promessa può essere rifiutata per uno qualsiasi dei seguenti motivi:
- La funzionalità Picture in picture non è supportata dal sistema.
- Il documento non è autorizzato a utilizzare la modalità Picture in picture a causa di rigide norme relative alle autorizzazioni.
- I metadati del video non sono ancora stati caricati (
videoElement.readyState === 0
). - Il file video è solo audio.
- Il nuovo attributo
disablePictureInPicture
è presente nell'elemento video. - La chiamata non è stata effettuata in un gestore di eventi dei gesti dell'utente (ad es. un clic sul pulsante). A partire da Chrome 74, questa opzione è applicabile solo se non è già presente un elemento in Picture in picture.
La sezione Supporto delle funzionalità di seguito mostra come attivare/disattivare un pulsante in base a queste limitazioni.
Aggiungiamo un blocco try...catch
per rilevare questi potenziali errori e informare l'utente della situazione.
pipButtonElement.addEventListener('click', async function () {
pipButtonElement.disabled = true;
try {
await videoElement.requestPictureInPicture();
} catch (error) {
// TODO: Show error message to user.
} finally {
pipButtonElement.disabled = false;
}
});
L'elemento video si comporta allo stesso modo in modalità Picture in picture o meno: gli eventi vengono attivati e i metodi di chiamata funzionano. Riflette le modifiche di stato nella finestra Picture in picture (ad esempio riproduzione, messa in pausa, ricerca e così via) ed è anche possibile modificare lo stato in modo programmatico in JavaScript.
Uscire da Picture in picture
Ora, facciamo in modo che il pulsante attivi e disattivi la funzionalità Picture in picture. dobbiamo prima verificare se l'oggetto di sola lettura document.pictureInPictureElement
è il nostro elemento video. In caso contrario, invieremo una richiesta per attivare la modalità Picture in Picture come indicato sopra. In caso contrario, chiediamo di uscire chiamandodocument.exitPictureInPicture()
, il che significa che il video verrà visualizzato nuovamente nella scheda originale. Tieni presente che questo metodo restituisce anche una promessa.
...
try {
if (videoElement !== document.pictureInPictureElement) {
await videoElement.requestPictureInPicture();
} else {
await document.exitPictureInPicture();
}
}
...
Ascoltare gli eventi Picture in picture
I sistemi operativi in genere limitano la funzionalità Picture-in-Picture a una finestra, quindi l'implementazione di Chrome segue questo modello. Ciò significa che gli utenti possono riprodurre un solo video in Picture in picture alla volta. Dovresti aspettarti che gli utenti escano dalla modalità Picture in picture anche quando non lo chiedi.
I nuovi gestori eventi enterpictureinpicture
e leavepictureinpicture
ci consentono di personalizzare l'esperienza per gli utenti. ad esempio sfogliare un catalogo di video o visualizzare una chat di live streaming.
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.
});
Personalizzare la finestra Picture in picture
Chrome 74 supporta i pulsanti Riproduci/Metti in pausa, Traccia precedente e Traccia successiva nella finestra Picture-in-Picture, che puoi controllare utilizzando l'API Media Session.
Per impostazione predefinita, nella finestra Picture-in-Picture viene sempre visualizzato un pulsante di riproduzione/messa in pausa, a meno che il video non riproduca oggetti MediaStream (ad es. getUserMedia()
,getDisplayMedia()
, canvas.captureStream()
) o la durata di MediaSource sia impostata su +Infinity
(ad es. feed dal vivo). Per assicurarti che un pulsante di riproduzione/messa in pausa sia sempre visibile, imposta alcuni gestori di azioni della sessione multimediale sia per gli eventi multimediali "Riproduci" sia per quelli "Metti in pausa" come indicato di seguito.
// 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.
});
La visualizzazione dei controlli della finestra "Traccia precedente" e "Traccia successiva" è simile. Se imposti gestori di azioni Media Session per questi elementi, verranno visualizzati nella finestra Picture-in-Picture e potrai gestirli.
navigator.mediaSession.setActionHandler('previoustrack', function () {
// User clicked "Previous Track" button.
});
navigator.mediaSession.setActionHandler('nexttrack', function () {
// User clicked "Next Track" button.
});
Per vedere come funziona, prova l'esempio di sessione multimediale ufficiale.
Ottenere le dimensioni della finestra Picture in picture
Se vuoi regolare la qualità del video quando entra e esce dal Picture in picture, devi conoscere le dimensioni della finestra Picture in picture e ricevere una notifica se un utente ne modifica manualmente le dimensioni.
L'esempio seguente mostra come ottenere la larghezza e l'altezza della finestra Picture in Picture quando viene creata o ridimensionata.
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.
}
Ti consiglio di non eseguire il collegamento direttamente all'evento di ridimensionamento, poiché ogni piccola modifica apportata alle dimensioni della finestra Picture-in-Picture attiverà un evento separato che potrebbe causare problemi di prestazioni se esegui un'operazione dispendiosa a ogni ridimensionamento. In altre parole, l'operazione di ridimensionamento attiverà gli eventi più volte e molto rapidamente. Per risolvere il problema, ti consiglio di utilizzare tecniche comuni come il throttling e il debounce.
Supporto delle funzionalità
L'API web Picture-in-Picture potrebbe non essere supportata, quindi devi rilevarla per fornire il miglioramento progressivo. Anche se è supportata, questa opzione potrebbe essere stata disattivata dall'utente o da un criterio di autorizzazione. Fortunatamente, puoi utilizzare
il nuovo valore booleano document.pictureInPictureEnabled
per determinare questo.
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.');
}
Applicato a un elemento del pulsante specifico per un video, ecco come potresti gestire la visibilità del pulsante 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;
}
Supporto video MediaStream
Gli oggetti MediaStream che riproducono video (ad es. getUserMedia()
, getDisplayMedia()
,
canvas.captureStream()
) supportano anche la modalità Picture-in-Picture in Chrome 71. Ciò significa che puoi mostrare una finestra Picture in Picture contenente lo stream video della webcam dell'utente, lo stream video del display o persino un elemento canvas. Tieni presente che l'elemento video non deve essere collegato al DOM per attivare la modalità Picture-in-Picture, come mostrato di seguito.
Mostrare la webcam dell'utente nella finestra 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();
Mostrare il display nella finestra 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();
Mostrare l'elemento della tela nella finestra 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();
Se combini canvas.captureStream()
con l'API Media Session, puoi ad esempio creare una finestra di playlist audio in Chrome 74. Consulta l'esempio di playlist audio ufficiale.
Samples, demo e codelab
Consulta il nostro esempio ufficiale Picture in picture per provare l'API web Picture in picture.
Seguiranno demo e codelab.
Passaggi successivi
Innanzitutto, consulta la pagina dello stato dell'implementazione per sapere quali parti dell'API sono attualmente implementate in Chrome e in altri browser.
Ecco cosa puoi aspettarti di vedere nel prossimo futuro:
- Gli sviluppatori web potranno aggiungere controlli Picture-in-Picture personalizzati.
- Verrà fornita una nuova API web per visualizzare oggetti
HTMLElement
arbitrari in una finestra mobile.
Supporto browser
L'API web Picture-in-Picture è supportata in Chrome, Edge, Opera e Safari. Per informazioni dettagliate, visita la pagina MDN.
Risorse
- Stato della funzionalità di Chrome: https://www.chromestatus.com/feature/5729206566649856
- Bug di implementazione di Chrome: https://crbug.com/?q=component:Blink>Media>PictureInPicture
- Specifica dell'API web Picture-in-Picture: https://wicg.github.io/picture-in-picture
- Problemi relativi alle specifiche: https://github.com/WICG/picture-in-picture/issues
- Esempio: https://googlechrome.github.io/samples/picture-in-picture/
- Polyfill di Picture in picture non ufficiale: https://github.com/gbentaieb/pip-polyfill/
Un grande ringraziamento a Mounir Lamouri e Jennifer Apacible per il loro lavoro sul Picture in Picture e per l'aiuto fornito per la stesura di questo articolo. Un grazie di cuore a tutti coloro che sono coinvolti nell'impegno di standardizzazione.