El método moderno
La API web de Picture-in-Picture (PIP) permite que los sitios web creen una ventana de video flotante que siempre esté sobre otras ventanas para que los usuarios puedan seguir consumiendo contenido multimedia mientras interactúan con otros sitios de contenido o aplicaciones en su dispositivo.
En el siguiente ejemplo, se muestra cómo crear un botón que los usuarios pueden usar para activar o desactivar la función de Picture-in-Picture en un video.
togglePipButton.addEventListener("click", async (event) => {
togglePipButton.disabled = true;
try {
if (video !== document.pictureInPictureElement) {
await video.requestPictureInPicture();
} else {
await document.exitPictureInPicture();
}
} finally {
togglePipButton.disabled = false;
}
});
video.addEventListener("enterpictureinpicture", (event) => {
togglePipButton.classList.add("on");
});
video.addEventListener("leavepictureinpicture", (event) => {
togglePipButton.classList.remove("on");
});
/* Feature detection */
if ("pictureInPictureEnabled" in document) {
// Set button ability depending on whether Picture-in-Picture can be used.
setPipButton();
video.addEventListener("loadedmetadata", setPipButton);
video.addEventListener("emptied", setPipButton);
} else {
// Hide button if Picture-in-Picture is not supported.
togglePipButton.hidden = true;
}
function setPipButton() {
togglePipButton.disabled =
video.readyState === 0 ||
!document.pictureInPictureEnabled ||
video.disablePictureInPicture;
}
El método clásico
Antes de que estuviera disponible la API web de Picture-in-Picture, no había forma de crear una ventana de video flotante que siempre estuviera sobre otras ventanas. Sin embargo, es posible reproducir el video sobre otros elementos de la página web con CSS.
En el siguiente ejemplo, se muestra cómo mostrar tu video sobre otros elementos en la esquina inferior derecha de tu página web cuando el usuario hace clic en un botón.
toggleFakePipButton.addEventListener("click", (event) => {
video.classList.toggle("fake-pip");
});
/* Place the video in the bottom right corner on top of everything else via CSS. */
video.fake-pip {
position: fixed;
z-index: 1000;
bottom: 10px;
right: 10px;
}
Lecturas adicionales
- Especificación de Picture-in-Picture de W3C
- Cómo mirar un video con Picture-in-Picture
- API de Picture-in-Picture de MDN
HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link
rel="icon"
href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>📺</text></svg>"
/>
<title>How to add Picture-in-Picture to video controls</title>
</head>
<body>
<h1>How to add Picture-in-Picture to video controls</h1>
<button id="togglePipButton">Picture-in-Picture</button>
<button id="toggleFakePipButton">Fake Picture-in-Picture</button>
<video autoplay muted playsinline loop src="https://storage.googleapis.com/media-session/caminandes/short.mp4"></video>
</body>
</html>CSS
:root {
color-scheme: dark light;
}
html {
box-sizing: border-box;
}
*,
*:before,
*:after {
box-sizing: inherit;
}
body {
margin: 1rem;
font-family: system-ui, sans-serif;
}
button:before {
content: "Enter ";
}
button.on:before {
content: "Leave ";
}
video {
display: block;
margin-top: 10px;
max-width: 100%;
}
video.fake-pip {
position: fixed;
z-index: 1000;
bottom: 10px;
right: 10px;
}
JS
const video = document.querySelector('video');
const togglePipButton = document.querySelector('#togglePipButton');
const toggleFakePipButton = document.querySelector('#toggleFakePipButton');
togglePipButton.addEventListener("click", async (event) => {
togglePipButton.disabled = true;
try {
if (video !== document.pictureInPictureElement) {
await video.requestPictureInPicture();
} else {
await document.exitPictureInPicture();
}
} finally {
togglePipButton.disabled = false;
}
});
video.addEventListener("enterpictureinpicture", (event) => {
togglePipButton.classList.add("on");
});
video.addEventListener("leavepictureinpicture", (event) => {
togglePipButton.classList.remove("on");
});
/* Feature detection */
if ("pictureInPictureEnabled" in document) {
// Hide fake PiP button if Picture-in-Picture is supported.
toggleFakePipButton.hidden = true;
// Set button ability depending on whether Picture-in-Picture can be used.
setPipButton();
video.addEventListener("loadedmetadata", setPipButton);
video.addEventListener("emptied", setPipButton);
} else {
// Hide button if Picture-in-Picture is not supported.
togglePipButton.hidden = true;
}
function setPipButton() {
togglePipButton.disabled =
video.readyState === 0 ||
!document.pictureInPictureEnabled ||
video.disablePictureInPicture;
}
/* Fake Picture-in-Picture */
toggleFakePipButton.addEventListener("click", (event) => {
toggleFakePipButton.classList.toggle("on");
video.classList.toggle("fake-pip");
});