Pantalla en pantalla para cualquier elemento, no solo para <video>

François Beaufort
François Beaufort

Navegadores compatibles

  • Chrome: 116.
  • Edge: 116.
  • Firefox: No es compatible.
  • Safari: No se admite.

Origen

La API de Document Picture-in-Picture permite abrir una ventana siempre en la parte superior que se puede propagar con contenido HTML arbitrario. Extiende la API de Pantalla en pantalla existente para <video>, que solo permite que se coloque un elemento <video> HTML en una ventana de Pantalla en pantalla.

La ventana de pantalla en pantalla de la API de Document Picture-in-Picture es similar a una ventana en blanco del mismo origen que se abre a través de window.open(), con algunas diferencias:

  • La ventana de Pantalla en pantalla flota sobre otras ventanas.
  • La ventana de Pantalla en pantalla nunca dura más que la ventana de apertura.
  • No se puede navegar por la ventana de pantalla en pantalla.
  • El sitio web no puede establecer la posición de la ventana de pantalla en pantalla.
Ventana de pantalla en pantalla que reproduce el video del avance de Sintel.
Una ventana de pantalla en pantalla creada con la API de Document Picture-in-Picture (demo).

Estado actual

Paso Estado
1. Crea una explicación Completar
2. Crea un borrador inicial de la especificación En curso
3. Recopila comentarios y itera en el diseño En curso
4. Prueba de origen Completar
5. Lanzamiento Completado (Computadoras de escritorio)

Casos de uso

Reproductor de video personalizado

Un sitio web puede proporcionar una experiencia de video en pantalla en pantalla con la API de Picture-in-Picture para <video> existente, pero es muy limitada. La ventana de Pantalla en pantalla existente acepta pocas entradas y tiene una capacidad limitada para aplicarles diseño. Con un documento completo en pantalla en pantalla, el sitio web puede proporcionar controles y entradas personalizados (por ejemplo, subtítulos, playlists, control de avance rápido, Me gusta y No me gusta en los videos) para mejorar la experiencia de video en pantalla en pantalla del usuario.

Videoconferencias

Es común que los usuarios salgan de la pestaña del navegador durante una sesión de videoconferencia por varios motivos (por ejemplo, presentar otra pestaña a la llamada o realizar varias tareas a la vez) y, al mismo tiempo, quieran ver la llamada, por lo que es un caso de uso principal para la función pantalla en pantalla. Una vez más, la experiencia actual que puede proporcionar un sitio web de videoconferencias a través de la API de Picture-in-Picture para <video> es limitada en estilo y entrada. Con un documento completo en pantalla en pantalla, el sitio web puede combinar fácilmente varias transmisiones de video en una sola ventana de PiP sin tener que depender de hacks de lienzo y proporcionar controles personalizados, como enviar un mensaje, silenciar a otro usuario o levantar la mano.

Productividad

Las investigaciones demuestran que los usuarios necesitan más formas de ser productivos en la Web. El modo de documento en pantalla en pantalla les brinda a las apps web la flexibilidad para lograr más. Ya sea para editar texto, tomar notas, crear listas de tareas, enviar mensajes y chatear, o usar herramientas de diseño y desarrollo, las apps web ahora pueden mantener su contenido siempre accesible.

Interfaz

Propiedades

documentPictureInPicture.window
Devuelve la ventana de Pantalla en pantalla actual, si la hay. De lo contrario, muestra null.

Métodos

documentPictureInPicture.requestWindow(options)

Muestra una promesa que se resuelve cuando se abre una ventana de Pantalla en pantalla. La promesa se rechaza si se la llama sin un gesto del usuario. El diccionario options contiene los siguientes miembros opcionales:

width
Establece el ancho inicial de la ventana de Pantalla en pantalla.
height
Establece la altura inicial de la ventana de Pantalla en pantalla.
disallowReturnToOpener
Oculta el botón "volver a la pestaña" en la ventana de pantalla en pantalla si es verdadero. Es falso de forma predeterminada.
preferInitialWindowPlacement
Abre la ventana de Pantalla en pantalla en su posición y tamaño predeterminados si es verdadero. Es falso de forma predeterminada.

Eventos

documentPictureInPicture.onenter
Se activa en documentPictureInPicture cuando se abre una ventana de pantalla en pantalla.

Ejemplos

El siguiente código HTML configura un reproductor de video personalizado y un elemento de botón para abrirlo en una ventana de Pantalla en pantalla.

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

Abre una ventana de Pantalla en pantalla.

El siguiente código JavaScript llama a documentPictureInPicture.requestWindow() cuando el usuario hace clic en el botón para abrir una ventana de pantalla en pantalla en blanco. La promesa que se muestra se resuelve con un objeto JavaScript de la ventana de imagen en imagen. El reproductor de video se mueve a esa ventana con 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);
});

Cómo establecer el tamaño de la ventana de Pantalla en pantalla

Para establecer el tamaño de la ventana de pantalla en pantalla, configura las opciones width y height de documentPictureInPicture.requestWindow() en el tamaño de ventana de pantalla en pantalla deseado. Chrome puede restringir los valores de la opción si son demasiado grandes o demasiado pequeños para adaptarse a un tamaño de ventana fácil de usar.

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);
});

Oculta el botón "volver a la pestaña" de la ventana de Pantalla en pantalla

Para ocultar el botón de la ventana de pantalla en pantalla que le permite al usuario volver a la pestaña del selector, establece la opción disallowReturnToOpener de documentPictureInPicture.requestWindow() en 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,
  });
});

Abre la ventana de Pantalla en pantalla en su posición y tamaño predeterminados.

Para no volver a usar la posición o el tamaño de la ventana de pantalla en pantalla anterior, establece la opción preferInitialWindowPlacement de documentPictureInPicture.requestWindow() en true.

pipButton.addEventListener("click", async () => {
  // Open a Picture-in-Picture window in its default position / size.
  const pipWindow = await documentPictureInPicture.requestWindow({
    preferInitialWindowPlacement: true,
  });
});

Cómo copiar hojas de estilo a la ventana de Pantalla en pantalla

Para copiar todas las hojas de estilo CSS de la ventana de origen, realiza un bucle a través de styleSheets vinculadas explícitamente en el documento o incorporadas en él y adjúntalo a la ventana de pantalla en pantalla. Ten en cuenta que esta es una copia única.

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);
});

Cómo controlar cuándo se cierra la ventana de pantalla en pantalla

Escucha el evento "pagehide" de la ventana para saber cuándo se cierra la ventana de pantalla en pantalla (ya sea porque el sitio web la inició o porque el usuario la cerró de forma manual). El controlador de eventos es un buen lugar para volver a sacar los elementos de la ventana de pantalla en pantalla, como se muestra aquí.

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);
  });
});

Cierra la ventana de pantalla en pantalla de forma programática con el método close().

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

Escucha cuando el sitio web entra en el modo Pantalla en pantalla

Escucha el evento "enter" en documentPictureInPicture para saber cuándo se abre una ventana de pantalla en pantalla. El evento contiene un objeto window para acceder a la ventana pantalla en pantalla.

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

Cómo acceder a los elementos de la ventana Pantalla en pantalla

Accede a los elementos de la ventana de pantalla en pantalla desde el objeto que muestra documentPictureInPicture.requestWindow() o con documentPictureInPicture.window, como se muestra a continuación.

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

Controla eventos desde la ventana Pantalla en pantalla

Crea botones y controles, y responde a los eventos de entrada del usuario, como "click", como lo harías normalmente en 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);

Cómo cambiar el tamaño de la ventana de la función Pantalla en pantalla

Usa los métodos de ventana resizeBy() y resizeTo() para cambiar el tamaño de la ventana de la función Pantalla en pantalla. Ambos métodos requieren un gesto del usuario.

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);

Enfocar la ventana del selector

Usa el método Window focus() para enfocar la ventana del selector desde la ventana de pantalla en pantalla. Este método requiere un gesto del usuario.

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

Modo de pantalla en pantalla de CSS

Usa el modo de visualización picture-in-picture del CSS para escribir reglas de CSS específicas que solo se apliquen cuando (parte de la) app web se muestre en el modo de pantalla en pantalla.

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

Detección de atributos

Para verificar si la API de Document Picture-in-Picture es compatible, usa lo siguiente:

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

Demostraciones

Reproductor de VideoJS

Puedes reproducir la demostración del reproductor VideoJS de la API de Document Picture-in-Picture. Asegúrate de revisar el código fuente.

Técnica pomodoro

Tomodoro, una app web de pomodoro, también aprovecha la API de Document Picture-in-Picture cuando está disponible. Consulta su solicitud de extracción de GitHub.

Tomodoro, una app web de pomodoro.
Ventana de Pantalla en pantalla en Tomodoro.

Comparte tus comentarios

Informa problemas en GitHub con sugerencias y preguntas.