Picture in Picture API: Tus videos flotando mientras navegas
octubre 26, 2024 - 15 minutos de lectura
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í:
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 avideo.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 adocument.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
WEB Share API: Comparte contenido de forma nativa desde la web
octubre 05, 2024
14 minutos de lectura
Viewport Units en CSS: ¿Por qué existen y cómo usarlas? - Parte 2
septiembre 21, 2024
15 minutos de lectura
Viewport Units en CSS: ¿Por qué existen y cómo usarlas? - Parte 1
septiembre 21, 2024
9 minutos de lectura