Funkcja obraz w obrazie umożliwia oglądanie filmów w pływającym oknie (zawsze nad innymi oknami), dzięki czemu mogą obserwować, co oglądają podczas korzystania z innych witryn lub aplikacji.
Za pomocą interfejsu Picture-in-Picture Web API możesz uruchamiać obrazy w obrazie w elementach wideo w swojej witrynie i nimi zarządzać. Wypróbuj tę funkcję za pomocą naszej oficjalnej próbki funkcji obraz w obrazie.
Wprowadzenie
We wrześniu 2016 r. Safari dodała obsługę obrazu w obrazie za pomocą interfejsu WebKit API w systemie macOS Sierra. Sześć miesięcy później Chrome automatycznie odtwarzał filmy w trybie obrazu w obrazie na urządzeniach mobilnych z Androidem O, korzystając z natywnego interfejsu API Androida. Sześć miesięcy później ogłosiliśmy zamiar utworzenia i standaryzacji interfejsu Web API, który jest zgodny z przeglądarką Safari, który umożliwi programistom internetowym tworzenie i kontrolowanie wszystkich funkcji związanych z obrazem w obrazie. I o to właśnie chodzi.
Wejdź do kodu
Włącz tryb obrazu w obrazie
Zacznijmy od elementu wideo i sposobu, w jaki użytkownik może z niego korzystać, np. przycisku.
<video id="videoElement" src="https://example.com/file.mp4"></video>
<button id="pipButtonElement"></button>
Obrazy w obrazie należy wysyłać tylko w odpowiedzi na gest użytkownika. Nie można tego robić w ramach obietnicy zwracanej przez funkcję videoElement.play()
. Dzieje się tak, ponieważ obietnice jeszcze nie propagują gestów użytkowników. Zamiast tego wywołaj requestPictureInPicture()
w module obsługi kliknięć pipButtonElement
, jak pokazano poniżej. To Ty odpowiadasz za to, co się stanie, gdy użytkownik kliknie dwa razy.
pipButtonElement.addEventListener('click', async function () {
pipButtonElement.disabled = true;
await videoElement.requestPictureInPicture();
pipButtonElement.disabled = false;
});
Po rozwiązaniu obietnicy Chrome zmniejszy film do małego okna, które użytkownik będzie mógł przesuwać i ustawiać nad innymi oknami.
Gotowe. Brawo! Możesz przestać czytać i udać się do zasłużonego urlopu. Niestety, nie zawsze tak jest. Obietnica może zostać odrzucona z dowolnego z tych powodów:
- System nie obsługuje trybu obrazu w obrazie.
- Na tym dokumencie nie można korzystać z funkcji obraz w obrazie ze względu na restrykcyjne zasady dotyczące uprawnień.
- Metadane filmu nie zostały jeszcze wczytane (
videoElement.readyState === 0
). - Plik wideo zawiera tylko dźwięk.
- W elemencie wideo występuje nowy atrybut
disablePictureInPicture
. - Wywołanie nie zostało wykonane w ramach modułu obsługi zdarzeń gestów (np. kliknięcia przycisku). Od wersji Chrome 74 ten problem dotyczy tylko sytuacji, gdy w trybie obrazu w obrazie nie ma jeszcze żadnego elementu.
W sekcji Obsługa funkcji poniżej dowiesz się, jak włączyć lub wyłączyć przycisk na podstawie tych ograniczeń.
Dodajmy blok try...catch
, aby wychwycić te potencjalne błędy i poinformować użytkownika, co się dzieje.
pipButtonElement.addEventListener('click', async function () {
pipButtonElement.disabled = true;
try {
await videoElement.requestPictureInPicture();
} catch (error) {
// TODO: Show error message to user.
} finally {
pipButtonElement.disabled = false;
}
});
Element video działa tak samo niezależnie od tego, czy działa w obrazie w obrazie, czy też nie: wywoływane są zdarzenia, a metody wywoływania działają. Odzwierciedla zmiany stanu w oknie obrazu w obrazie (np. odtwarzanie, wstrzymanie, przewijanie itp.), a także możliwość automatycznej zmiany stanu w JavaScript.
Wyłącz obraz w obrazie
Przełączmy przycisk, aby otworzyć i zamknąć tryb obrazu w obrazie. Najpierw musimy sprawdzić, czy obiektem tylko do odczytu document.pictureInPictureElement
jest nasz element wideo. Jeśli nie, wysyłamy prośbę
o włączenie obrazu w obrazie (jak powyżej). Jeśli go nie widzisz, zadzwonimy pod numer document.exitPictureInPicture()
. Oznacza to, że film pojawi się z powrotem na oryginalnej karcie. Pamiętaj, że ta metoda również zwraca obietnicę.
...
try {
if (videoElement !== document.pictureInPictureElement) {
await videoElement.requestPictureInPicture();
} else {
await document.exitPictureInPicture();
}
}
...
Słuchanie zdarzeń typu obraz w obrazie
Systemy operacyjne zazwyczaj ograniczają obraz w obrazie do jednego okna, więc implementacja Chrome jest zgodna z tym wzorcem. Oznacza to, że użytkownicy mogą w danym momencie odtwarzać tylko jeden film w trybie obrazu w obrazie. Użytkownicy wyłączają obraz w obrazie nawet wtedy, gdy o to nie prosisz.
Nowe moduły obsługi zdarzeń enterpictureinpicture
i leavepictureinpicture
pozwalają nam dostosowywać środowisko do potrzeb użytkowników. Może to być wszystko – od przeglądania katalogu
filmów po czat w transmisji na żywo.
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.
});
Dostosowywanie okna obrazu w obrazie
Chrome 74 obsługuje przyciski odtwarzania/wstrzymania, poprzedniej i następnej ścieżki w oknie obrazu w obrazie, którym można sterować za pomocą interfejsu Media Session API.
Domyślnie przycisk odtwarzania/wstrzymania w oknie obrazu w obrazie jest zawsze wyświetlany, chyba że podczas odtwarzania filmu są odtwarzane obiekty MediaStream (np. getUserMedia()
, getDisplayMedia()
czy canvas.captureStream()
) lub czas trwania MediaSource jest ustawiony na +Infinity
(np. w przypadku transmisji na żywo). Aby przycisk odtwarzania/wstrzymania był zawsze widoczny, skonfiguruj moduły obsługi działań w sesji multimediów dla zdarzeń multimedialnych „Odtwarzanie” i „Wstrzymaj”, jak poniżej.
// 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.
});
Opcje okien „Poprzednia ścieżka” i „Następna ścieżka” działają podobnie. Po ustawieniu modułów obsługi sesji multimedialnych dla tych działań będą one wyświetlane w oknie obrazu w obrazie i będziesz mieć możliwość ich obsługi.
navigator.mediaSession.setActionHandler('previoustrack', function () {
// User clicked "Previous Track" button.
});
navigator.mediaSession.setActionHandler('nexttrack', function () {
// User clicked "Next Track" button.
});
Aby zobaczyć, jak to działa, wypróbuj oficjalną przykładową sesję medialną.
Ustaw rozmiar okna obrazu w obrazie
Jeśli chcesz dostosować jakość filmu po otwarciu i wyjściu filmu, musisz znać rozmiar okna obrazu w obrazie i otrzymywać powiadomienia, gdy użytkownik ręcznie zmieni rozmiar okna.
Poniższy przykład pokazuje, jak uzyskać szerokość i wysokość okna obrazu w obrazie podczas jego tworzenia lub zmiany jego rozmiaru.
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.
}
Sugerujemy, aby nie łączyć bezpośrednio zdarzenia zmiany rozmiaru, ponieważ każda mała zmiana rozmiaru okna obrazu w obrazie spowoduje uruchomienie osobnego zdarzenia, które może powodować problemy z wydajnością, jeśli przy każdej zmianie rozmiaru wykonujesz kosztowne operacje. Innymi słowy, operacja zmiany rozmiaru będzie wywoływać zdarzenia w nieskończoność bardzo szybko. Aby rozwiązać ten problem, zalecamy użycie popularnych technik, takich jak ograniczanie i wyciszanie reklam.
Obsługa funkcji
Interfejs Picture-in-Picture Web API może nie być obsługiwany, więc musisz go wykryć, aby korzystać z progresywnych ulepszeń. Nawet jeśli jest obsługiwana, użytkownik może ją wyłączyć lub wyłączyć przez zasadę uprawnień. Aby to określić, możesz użyć nowej wartości logicznej document.pictureInPictureEnabled
.
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.');
}
Stosowany do konkretnego elementu przycisku.
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;
}
Obsługa wideo MediaStream
Odtwarzane w filmach obiekty MediaStream (np. getUserMedia()
, getDisplayMedia()
,
canvas.captureStream()
) również obsługują obraz w obrazie w Chrome 71. Oznacza to, że możesz wyświetlić okno obrazu w obrazie, które zawiera strumień wideo z kamery internetowej użytkownika, strumień wideo z sieci reklamowej, a nawet element canvas. Pamiętaj, że aby można było korzystać z obrazu w obrazie, jak pokazano poniżej, element video nie musi być dołączony do modelu DOM.
Pokazuj obraz z kamery internetowej użytkownika w oknie obrazu w obrazie
const video = document.createElement('video');
video.muted = true;
video.srcObject = await navigator.mediaDevices.getUserMedia({video: true});
video.play();
// Later on, video.requestPictureInPicture();
Pokaż ekran w oknie obrazu w obrazie
const video = document.createElement('video');
video.muted = true;
video.srcObject = await navigator.mediaDevices.getDisplayMedia({video: true});
video.play();
// Later on, video.requestPictureInPicture();
Pokazuj element na płótnie w oknie obrazu w obrazie
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();
Dzięki połączeniu canvas.captureStream()
z interfejsem Media Session API możesz na przykład utworzyć okno playlisty audio w Chrome 74. Wypróbuj oficjalną próbkę playlisty audio.
Przykłady, wersje demonstracyjne i ćwiczenia z programowania
Zapoznaj się z naszym oficjalnym przykładem funkcji Obraz w obrazie, aby wypróbować interfejs Picture-in-Picture Web API.
Zostaną wyświetlone prezentacje i ćwiczenia z programowania.
Co dalej
Po pierwsze, wejdź na stronę stanu implementacji, aby dowiedzieć się, które części interfejsu API są obecnie wdrożone w Chrome i innych przeglądarkach.
W najbliższej przyszłości:
- Programiści stron internetowych będą mogli dodawać niestandardowe elementy sterujące obrazu w obrazie.
- Zostanie udostępniony nowy interfejs Web API do wyświetlania dowolnych obiektów
HTMLElement
w pływającym oknie.
Obsługiwane przeglądarki
Interfejs Picture-in-Picture Web API jest obsługiwany w przeglądarkach Chrome, Edge, Opera i Safari. Więcej informacji znajdziesz w MDN.
Zasoby
- Stan funkcji Chrome: https://www.chromestatus.com/feature/5729206566649856
- Błędy implementacji Chrome: https://crbug.com/?q=component:Blink>Media>PictureInPicture
- Specyfikacja internetowego interfejsu API obrazu w obrazie: https://wicg.github.io/picture-in-picture
- Problemy ze specyfikacją: https://github.com/WICG/picture-in-picture/issues
- Przykład: https://googlechrome.github.io/samples/picture-in-picture/
- Nieoficjalny kod polyfill obrazu w obrazie: https://github.com/gbentaieb/pip-polyfill/
Dziękujemy Mounir Lamouri i Jennifer Apacible za pracę nad obrazem w obrazie. Pomogę też w tym artykule. Serdecznie dziękujemy za zaangażowanie w standaryzację.