Obtén información sobre las pantallas conectadas y posiciona las ventanas en relación con ellas.
API de Window Management
La API de Window Management te permite enumerar las pantallas conectadas a tu máquina y colocar ventanas en pantallas específicas.
Casos de uso sugeridos
Estos son algunos ejemplos de sitios que pueden usar esta API:
- Los editores de gráficos multiventana al estilo de Gimp pueden colocar varias herramientas de edición en ventanas posicionadas con precisión.
- Las mesas de operaciones virtuales pueden mostrar las tendencias del mercado en varias ventanas, cada una de las cuales se puede ver en modo de pantalla completa.
- Las apps de diapositivas pueden mostrar notas del orador en la pantalla principal interna y la presentación en un proyector externo.
Cómo usar la API de Window Management
El problema
Lamentablemente, el enfoque probado para controlar ventanas, Window.open()
, no es consciente de las pantallas adicionales. Si bien algunos aspectos de esta API parecen un poco arcaicos, como su parámetro windowFeatures
DOMString
, nos ha servido bien a lo largo de los años. Para especificar la posición de una ventana, puedes pasar las coordenadas como left
y top
(o screenX
y screenY
, respectivamente) y pasar el tamaño deseado como width
y height
(o innerWidth
y innerHeight
, respectivamente). Por ejemplo, para abrir una ventana de 400 × 300 a 50 píxeles de la izquierda y 50 píxeles de la parte superior, este es el código que podrías usar:
const popup = window.open(
'https://example.com/',
'My Popup',
'left=50,top=50,width=400,height=300',
);
Para obtener información sobre la pantalla actual, consulta la propiedad window.screen
, que muestra un objeto Screen
. Este es el resultado en mi MacBook Pro de 13":
window.screen;
/* Output from my MacBook Pro 13″:
availHeight: 969
availLeft: 0
availTop: 25
availWidth: 1680
colorDepth: 30
height: 1050
isExtended: true
onchange: null
orientation: ScreenOrientation {angle: 0, type: "landscape-primary", onchange: null}
pixelDepth: 30
width: 1680
*/
Como la mayoría de las personas que trabajan en tecnología, tuve que adaptarme a la nueva realidad laboral y configurar mi oficina en casa personal. La mía se ve como en la foto de abajo (si te interesa, puedes leer los detalles completos sobre mi configuración). El iPad que está junto a mi MacBook está conectado a la laptop a través de Sidecar, por lo que, cuando lo necesito, puedo convertir rápidamente el iPad en una segunda pantalla.
Si quiero aprovechar la pantalla más grande, puedo colocar la ventana emergente del ejemplo de código anterior en la segunda pantalla. Lo hago así:
popup.moveTo(2500, 50);
Esta es una suposición aproximada, ya que no hay forma de conocer las dimensiones de la segunda pantalla. La información de window.screen
solo abarca la pantalla integrada, pero no la pantalla del iPad. El width
informado de la pantalla integrada era de 1680
píxeles, por lo que pasar a 2500
píxeles podría funcionar para mover la ventana al iPad, ya que sé que se encuentra a la derecha de mi MacBook. ¿Cómo puedo hacer esto en el caso general? Resulta que hay una forma mejor que adivinar. Esa forma es la API de Window Management.
Detección de atributos
Para comprobar si la API de Window Management es compatible, usa lo siguiente:
if ('getScreenDetails' in window) {
// The Window Management API is supported.
}
El permiso window-management
Antes de poder usar la API de Window Management, debo pedirle permiso al usuario para hacerlo. El permiso window-management
se puede consultar con la API de Permissions de la siguiente manera:
let granted = false;
try {
const { state } = await navigator.permissions.query({ name: 'window-management' });
granted = state === 'granted';
} catch {
// Nothing.
}
Mientras se usan los navegadores con el nombre de permiso anterior y el nuevo, asegúrate de usar código defensivo cuando solicites permiso, como en el siguiente ejemplo.
async function getWindowManagementPermissionState() {
let state;
// The new permission name.
try {
({ state } = await navigator.permissions.query({
name: "window-management",
}));
} catch (err) {
return `${err.name}: ${err.message}`;
}
return state;
}
document.querySelector("button").addEventListener("click", async () => {
const state = await getWindowManagementPermissionState();
document.querySelector("pre").textContent = state;
});
El navegador puede elegir mostrar el mensaje de permiso de forma dinámica en el primer intento de usar cualquiera de los métodos de la nueva API. Sigue leyendo para obtener más información.
La propiedad window.screen.isExtended
Para saber si más de una pantalla está conectada a mi dispositivo, accedo a la propiedad window.screen.isExtended
. Muestra true
o false
. En mi configuración, muestra true
.
window.screen.isExtended;
// Returns `true` or `false`.
El método getScreenDetails()
Ahora que sé que la configuración actual es multipantalla, puedo obtener más información sobre la segunda pantalla con Window.getScreenDetails()
. Si llamo a esta función, se mostrará un mensaje de permiso que me pregunta si el sitio puede abrir y colocar ventanas en mi pantalla. La función muestra una promesa
que se resuelve con un objeto ScreenDetailed
. En mi MacBook Pro 13 con un iPad conectado, esto incluye un campo screens
con dos objetos ScreenDetailed
:
await window.getScreenDetails();
/* Output from my MacBook Pro 13″ with the iPad attached:
{
currentScreen: ScreenDetailed {left: 0, top: 0, isPrimary: true, isInternal: true, devicePixelRatio: 2, …}
oncurrentscreenchange: null
onscreenschange: null
screens: [{
// The MacBook Pro
availHeight: 969
availLeft: 0
availTop: 25
availWidth: 1680
colorDepth: 30
devicePixelRatio: 2
height: 1050
isExtended: true
isInternal: true
isPrimary: true
label: "Built-in Retina Display"
left: 0
onchange: null
orientation: ScreenOrientation {angle: 0, type: "landscape-primary", onchange: null}
pixelDepth: 30
top: 0
width: 1680
},
{
// The iPad
availHeight: 999
availLeft: 1680
availTop: 25
availWidth: 1366
colorDepth: 24
devicePixelRatio: 2
height: 1024
isExtended: true
isInternal: false
isPrimary: false
label: "Sidecar Display (AirPlay)"
left: 1680
onchange: null
orientation: ScreenOrientation {angle: 0, type: "landscape-primary", onchange: null}
pixelDepth: 24
top: 0
width: 1366
}]
}
*/
La información sobre las pantallas conectadas está disponible en el array screens
. Observa cómo el valor de left
para el iPad comienza en 1680
, que es exactamente el width
de la pantalla integrada. Esto me permite determinar exactamente cómo se organizan las pantallas de forma lógica (una al lado de la otra, una encima de la otra, etcétera). Ahora también hay datos para cada pantalla que muestran si es una isInternal
o una isPrimary
. Ten en cuenta que la pantalla integrada no es necesariamente la pantalla principal.
El campo currentScreen
es un objeto activo que corresponde al window.screen
actual. El objeto se actualiza en las posiciones de ventanas en varias pantallas o en los cambios de dispositivo.
El evento screenschange
Lo único que falta ahora es una forma de detectar cuándo cambia la configuración de la pantalla. Un evento nuevo, screenschange
, hace exactamente eso: se activa cada vez que se modifica la constelación de la pantalla. (Ten en cuenta que "pantallas" está en plural en el nombre del evento). Esto significa que el evento se activa cada vez que se conecta o desconecta una pantalla nueva o existente (física o virtualmente en el caso de Sidecar).
Ten en cuenta que debes buscar los detalles de la nueva pantalla de forma asíncrona, ya que el evento screenschange
no proporciona estos datos. Para buscar los detalles de la pantalla, usa el objeto en vivo de una interfaz Screens
almacenada en caché.
const screenDetails = await window.getScreenDetails();
let cachedScreensLength = screenDetails.screens.length;
screenDetails.addEventListener('screenschange', (event) => {
if (screenDetails.screens.length !== cachedScreensLength) {
console.log(
`The screen count changed from ${cachedScreensLength} to ${screenDetails.screens.length}`,
);
cachedScreensLength = screenDetails.screens.length;
}
});
El evento currentscreenchange
Si solo me interesan los cambios en la pantalla actual (es decir, el valor del objeto currentScreen
en vivo), puedo escuchar el evento currentscreenchange
.
const screenDetails = await window.getScreenDetails();
screenDetails.addEventListener('currentscreenchange', async (event) => {
const details = screenDetails.currentScreen;
console.log('The current screen has changed.', event, details);
});
El evento change
Por último, si solo me interesan los cambios en una pantalla concreta, puedo escuchar el evento change
de esa pantalla.
const firstScreen = (await window.getScreenDetails())[0];
firstScreen.addEventListener('change', async (event) => {
console.log('The first screen has changed.', event, firstScreen);
});
Nuevas opciones de pantalla completa
Hasta ahora, podías solicitar que los elementos se mostraran en el modo de pantalla completa a través del método apropiadamente llamado requestFullScreen()
. El método toma un parámetro options
en el que puedes pasar FullscreenOptions
. Hasta ahora, su única propiedad fue navigationUI
.
La API de Window Management agrega una nueva propiedad screen
que te permite determinar en qué pantalla iniciar la vista de pantalla completa. Por ejemplo, si quieres que la pantalla principal sea en pantalla completa, haz lo siguiente:
try {
const primaryScreen = (await getScreenDetails()).screens.filter((screen) => screen.isPrimary)[0];
await document.body.requestFullscreen({ screen: primaryScreen });
} catch (err) {
console.error(err.name, err.message);
}
Polyfill
No es posible realizar un polyfill de la API de Window Management, pero puedes usar un shim para su forma para que puedas codificar exclusivamente para la nueva API:
if (!('getScreenDetails' in window)) {
// Returning a one-element array with the current screen,
// noting that there might be more.
window.getScreenDetails = async () => [window.screen];
// Set to `false`, noting that this might be a lie.
window.screen.isExtended = false;
}
Los otros aspectos de la API, es decir, los diversos eventos de cambio de pantalla y la propiedad screen
de FullscreenOptions
, simplemente nunca se activarán o se ignorarán de forma silenciosa, respectivamente, en los navegadores que no son compatibles.
Demostración
Si eres como yo, te mantienes al tanto del desarrollo de las diversas criptomonedas. (En realidad, no lo hago porque amo este planeta, pero, por el bien de este artículo, supongamos que sí). Para hacer un seguimiento de las criptomonedas que tengo, desarrollé una app web que me permite ver los mercados en todas las situaciones de la vida, como desde la comodidad de mi cama, donde tengo una configuración decente de una sola pantalla.
Como se trata de criptomonedas, los mercados pueden ser muy activos en cualquier momento. Si esto sucede, puedo moverme rápidamente a mi escritorio, donde tengo una configuración de varias pantallas. Puedo hacer clic en la ventana de cualquier moneda y ver rápidamente los detalles completos en una vista de pantalla completa en la pantalla opuesta. A continuación, se muestra una foto reciente de mi toma durante el último baño de sangre de YCY. Me tomó completamente desprevenida y me dejó con las manos en la cara.
Puedes jugar con la demo incorporada a continuación o ver su código fuente en glitch.
Seguridad y permisos
El equipo de Chrome diseñó e implementó la API de Window Management con los principios básicos definidos en Controlling Access to Powerful Web Platform Features, incluidos el control del usuario, la transparencia y la ergonomía. La API de Window Management expone información nueva sobre las pantallas conectadas a un dispositivo, lo que aumenta la superficie de huellas digitales de los usuarios, en especial, aquellos que tienen varias pantallas conectadas de forma coherente a sus dispositivos. Como una mitigación de esta preocupación por la privacidad, las propiedades de pantalla expuestas se limitan al mínimo necesario para los casos de uso de posiciones comunes. Se requiere el permiso del usuario para que los sitios obtengan información de varias pantallas y coloquen ventanas en otras pantallas. Si bien Chromium muestra etiquetas de pantalla detalladas, los navegadores pueden mostrar etiquetas menos descriptivas (o incluso vacías).
Control de usuarios
El usuario tiene el control total de la exposición de su configuración. Pueden aceptar o rechazar el mensaje de permiso y revocar un permiso otorgado anteriormente a través de la función de información del sitio en el navegador.
Control empresarial
Los usuarios de Chrome Enterprise pueden controlar varios aspectos de la API de Window Management, como se describe en la sección relevante de la configuración de los grupos de políticas atómicas.
Transparencia
El hecho de si se otorgó el permiso para usar la API de Window Management se expone en la información del sitio del navegador y también se puede consultar a través de la API de Permissions.
Persistencia de permisos
El navegador conserva los otorgamientos de permisos. El permiso se puede revocar a través de la información del sitio del navegador.
Comentarios
El equipo de Chrome quiere conocer tus experiencias con la API de Window Management.
Cuéntanos sobre el diseño de la API
¿Hay algo en la API que no funciona como esperabas? ¿O faltan métodos o propiedades que necesitas para implementar tu idea? ¿Tienes alguna pregunta o comentario sobre el modelo de seguridad?
- Informa un problema de especificación en el repositorio de GitHub correspondiente o agrega tus comentarios a un problema existente.
Denuncia un problema con la implementación
¿Encontraste un error en la implementación de Chrome? ¿O la implementación es diferente de la especificación?
- Informa un error en new.crbug.com. Asegúrate de incluir tantos detalles como sea posible, instrucciones simples para reproducirlo y, luego, ingresa
Blink>Screen>MultiScreen
en el cuadro Components. Glitch es excelente para compartir reproducciones rápidas y fáciles.
Cómo mostrar compatibilidad con la API
¿Piensas usar la API de Window Management? Tu apoyo público ayuda al equipo de Chrome a priorizar las funciones y les muestra a otros proveedores de navegadores lo importante que es admitirlas.
- Comparte cómo planeas usarlo en la conversación de Discourse de WICG.
- Envía un tuit a @ChromiumDev con el hashtag
#WindowManagement
y cuéntanos dónde y cómo lo usas. - Pídeles a otros proveedores de navegadores que implementen la API.
Vínculos útiles
- Borrador de especificaciones
- Explicación pública
- Demo de la API de Window Management | Fuente de la demo de la API de Window Management
- Error de seguimiento de Chromium
- Entrada de ChromeStatus.com
- Componente Blink:
Blink>Screen>MultiScreen
- Revisión de TAG
- Intención de experimentar
Agradecimientos
Victor Costan, Joshua Bell y Mike Wasserman editaron las especificaciones de la API de Window Management. Mike Wasserman y Adrienne Walker implementaron la API. Joe Medley, François Beaufort y Kayce Basques revisaron este artículo. Agradecemos a Laura Torrent Puig por las fotos.