Gerson Lázaro

Picture in Picture API: Tus videos flotando mientras navegas

Picture in Picture API: Tus videos flotando mientras navegas

Esta es la segunda entrega de la serie sobre APIs de Javascript. Al final del artículo encontrarás referencias actualizadas al resto de artículos de la serie a medida que sean publicados.

¿Qué es Picture in Picture?

Seguramente has notado una tendencia cada vez más creciente de los dispositivos a permitirte ver videos de un sitio web o aplicación como una ventana flotante que se mantiene visible en pantalla incluso mientras estás navegando otra web o aplicación. Esto es Picture in Picture y aunque su interfaz puede variar, generalmente se ve así:

Picture in Picture API

Algunos navegadores activan este comportamiento por defecto en sus reproductores de video pero puedes llevarlo más allá: Abrir o cerrar el modo Picture in Picture desde el código, controlar la reproducción en la ventana flotante e incluso realizar acciones cuando la ventana se abre, se cierra o se redimensiona. Para esto existe Picture in Picture API.

En este artículo vamos a construir esta demo de Picture in Picture API paso a paso.

Abrir y cerrar Picture In Picture

Cuando tenemos un <video> referenciado en javascript, este expone el método requestPictureInPicture() que directamente nos permite abrir el modo Picture in Picture.

const video = document.querySelector('.video') 
video.requestPictureInPicture()

Cuando la ventana flotante se encuentra abierta, en el document tendremos disponible:

  • document.pictureInPictureElement: Guarda una referencia al elemento del DOM que está siendo visualizado en la ventana flotante.
  • document.exitPictureInPicture(): Método que cierra la ventana flotante y devuelve la reproducción a la ventana original.

Con esto en mente, podemos abrir o cerrar la ventana flotante según el valor almacenado en document.pictureInPictureElement de la siguiente manera:

const video = document.querySelector('.video') 
if (video !== document.pictureInPictureElement) await video.requestPictureInPicture()
else await document.exitPictureInPicture()

Validar disponibilidad de picture in picture

La propiedad document.pictureInPictureEnabled indica si el modo de imagen en imagen está disponible o no. Por defecto esta propiedad es true si el navegador soporta el API y si no existen políticas de seguridad que lo estén bloqueando.

Eventos disponibles

Tenemos 3 eventos diferentes que nos permiten ejecutar acciones en momentos especificos del flujo de Picture in Picture API. Los dos más comunes son:

  • enterpictureinpicture: Se dispara cuando la ventana flotante abre. Esto puede ocurrir al llamar a video.requestPictureInPicture() como se vió anteriormente, pero también cuando se dispara el picture in picture desde la propia interfaz del navegador. Algunos navegadores añaden un botón para este modo en el reproductor de video, mientras otros lo hacen con una opción contextual en el clic derecho sobre el video.
  • leavepictureinpicture: Se dispara cuando la ventana flotante se cierra. Al igual que en el caso anterior, esto puede ocurrir tanto por un llamado a document.exitPictureInPicture() como por interacciones con el navegador que cierren la ventana flotante.

Supongamos que queremos cambiar el color de fondo de la página cuando el video abre en una ventana flotante y volver al color original cuando esta ventana se cierre. Podríamos hacerlo del siguiente modo:

/* Css */
body {
  background-color: #FFF;
  transition: all .3s ease-in-out;
}

.picture-in-picture {
  background-color: #000;
}
// Javascript
const onEnterPictureInPicture = () => {
  document.body.classList.add('picture-in-picture')
}

const onLeavePictureInPicture = () => {
  document.body.classList.remove('picture-in-picture')
}

const video = document.querySelector('.video') 
video.addEventListener('enterpictureinpicture', onEnterPictureInPicture)
video.addEventListener('leavepictureinpicture', onLeavePictureInPicture)

De este modo el body tendrá un color de fondo blanco por defecto, pero cuando se abra la ventana flotante añadirá la clase .picture-in-picture que le cambiará su color a blanco. Al cerrar la ventana se eliminará la clase y el color volverá a blanco.

El tercer evento relacionado al picture in picture es el resize, aunque este es un poco diferente. Los dos eventos anteriores, como vimos, son propios del mismo <video>. En cambio el resize no es del video sino de la ventana externa. ¿Y como accedemos a esta ventana? Podemos accederlo desde el event.pictureInPictureWindow. Supongamos que queremos cambiar colores aleatoriamente cuando esta ventana sea redireccionada:

const onResizePictureInPicture = (event) => {
  const { width, height } = event.currentTarget
  document.body.style.backgroundColor = `rgb(${width % 255}, ${height % 255}, 150)`
}

const onEnterPictureInPicture = (event) => {
  document.body.classList.add('picture-in-picture')
  event.pictureInPictureWindow.addEventListener('resize', onResizePictureInPicture)
}

const onLeavePictureInPicture = (event) => {
  document.body.classList.remove('picture-in-picture')
  document.body.style.backgroundColor = ''
  event.pictureInPictureWindow.removeEventListener('resize', onResizePictureInPicture)
}

video.addEventListener('enterpictureinpicture', onEnterPictureInPicture)
video.addEventListener('leavepictureinpicture', onLeavePictureInPicture)

En este caso añadimos el eventListener para el resize desde el evento de enterpictureinpicture y lo eliminamos desde el evento de leavepictureinpicture. Adicionalmente en el resize el evento incluye una propiedad currentTarget que nos permite obtener el width y el height de la ventana flotante. En este caso hemos usado esas medidas para calcular un nuevo color, pero podría utilizarse para cualquier otra funcionalidad. Podemos probar abriendo la ventana flotante y redimensionandola, viendo como el color de fondo de la página cambia en vivo.

Algo a tener en cuenta del resize es que no hay un comportamiento uniforme entre navegadores. Cuando se abre la ventana flotante algunos navegadores lo hacen directamente en un tamaño fijo, mientras otros inician en un rectangulo de 0px * 0px que inmediatamente se redimensiona a su tamaño default. Si bien esto es imperceptible al ojo, si puede hacer que en algunos navegadores se lance un resize justo después del enterpictureinpicture y en otros no.

El código completo del demo planteado se encuentra aquí y puede ejecutarse aquí.

Compatibilidad de navegadores

Al momento de escribir este artículo, la API de Picture in Picture está disponible en los principales navegadores exceptuando firefox (tanto desktop como mobile). En el caso de Firefox si bien es posible usar videos en modo Picture in Picture, solo se puede interactuar con estos a través de los controles del navegador, no a través de la API. Para ver actualizaciones futuras de compatibilidad recomiendo ver CanIUse.

La especificación de la API tiene actualmente el status de "Borrador de la W3C", lo que implica que es un trabajo en progreso y podría modificarse en el futuro.

Otras APIs en esta serie

Este artículo hace parte de una serie (en construcción) de artículos sobre APIs de Javascript. Actualmente la serie consta de las siguientes entradas:

Otros Artículos

Otros Artículos