特性检测
您可以通过检查当前 window 实例中是否存在 getScreenDetails 来进行窗口管理 API 的功能检测。例如,如果 API 受支持,您可能希望提供一个按钮来打开多窗口显示;如果不支持,则提供不同的体验,例如创建指向不同页面的链接。
if ("getScreenDetails" in window) {
// The Window Management API is supported
createButton();
} else {
// The Window Management API is not supported
createLinks(sites);
}
基本用法
窗口管理 API 的核心是 Window.getScreenDetails() 方法,它返回一个对象,其中包含用户系统上所有可用屏幕的详细信息。
const screenDetails = await window.getScreenDetails();
// Return the number of screens
const noOfScreens = screenDetails.screens.length;
调用 getScreenDetails() 时,用户将被要求允许管理所有显示器上的窗口(可以通过 Permissions.query() 查询 window-management 来检查此权限的状态)。如果用户授予权限,则会返回一个 ScreenDetails 对象。此对象包含以下属性:
screens:一个ScreenDetailed对象的数组,每个对象包含有关系统可用独立屏幕的详细信息(如下所示)。此数组还有助于通过screens.length确定可用屏幕的数量。currentScreen:一个ScreenDetailed对象,包含有关当前浏览器窗口显示所在的屏幕的详细信息。
ScreenDetailed 对象继承了 Screen 接口的属性,并包含有关将窗口放置在特定屏幕上的有用信息。
注意:您可以使用 Window.screen.isExtended 属性根据用户是否拥有多个屏幕来控制功能。如果设备有多个屏幕,则返回 true;否则返回 false。
打开窗口
您仍然需要使用 Window.open() 来打开和管理窗口,但上述信息能更好地帮助您在多屏幕环境中执行此操作。例如,一个实用函数可能如下所示:
// Array to hold references to the currently open windows
const windowRefs = [];
// …
function openWindow(left, top, width, height, url) {
const windowFeatures = `left=${left},top=${top},width=${width},height=${height}`;
const windowRef = window.open(
url,
"_blank", // needed for it to open in a new window
windowFeatures,
);
if (windowRef === null) {
// If the browser is blocking new windows, close any windows that were
// able to open and display instructions to help the user fix this problem
closeAllWindows();
popoverElem.showPopover();
} else {
// Store a reference to the window in the windowRefs array
windowRefs.push(windowRef);
}
}
然后,您可以这样调用此函数并在特定屏幕上打开窗口:
const screen1 = screenDetails.screens[0];
const screen2 = screenDetails.screens[1];
// Windows will be a third the width and the full height of the screen
// The available width of screen1, minus 3 times the horizontal browser chrome
// width, divided by 3
const windowWidth = Math.floor((screen1.availWidth - 3 * WINDOW_CHROME_X) / 3);
// The available height of screen1, minus the vertical browser chrome width
const windowHeight = Math.floor(screen1.availHeight - WINDOW_CHROME_Y);
// Open a window a third of the width and the entire height of the primary screen
openWindow(
screen1.availLeft,
screen1.availTop,
windowWidth,
windowHeight,
sites[1].url,
);
// …
关闭所有窗口
在打开每个窗口后,我们将一个引用添加到 windowRefs 数组。这样,您就可以在关闭一个窗口时关闭所有窗口。
function closeAllWindows() {
// Loop through all window refs and close each one
windowRefs.forEach((windowRef) => {
windowRef.close();
});
windowRefs = [];
}
// Check whether one of our popup windows has been closed
// If so, close them all
closeMonitor = setInterval(checkWindowClose, 250);
function checkWindowClose() {
if (windowRefs.some((windowRef) => windowRef.closed)) {
closeAllWindows();
clearInterval(closeMonitor);
}
}
注意:在我们的实验中,上面显示的 setInterval() 轮询方法似乎最适合检测多个窗口关闭的情况。使用 beforeunload、pagehide 或 visibilitychange 等事件被证明是不可靠的,因为当同时打开多个窗口时,焦点/可见性的快速变化似乎会过早地触发处理函数。
注意:上述示例的一个问题是,它在计算中使用常量值来表示 Chrome 窗口 UI 部分的大小(WINDOW_CHROME_X 和 WINDOW_CHROME_Y),以使窗口大小计算正确。要在 API 的其他未来实现中创建精确大小的窗口,您需要维护一个小型浏览器 UI 大小库,并使用浏览器检测来发现哪个浏览器正在渲染您的应用程序,并为计算选择正确的大小。或者,您也可以依赖不那么精确的窗口大小。
处理浏览器弹出窗口阻止程序
在现代浏览器中,出于安全原因,每次调用 Window.open() 都需要一个单独的用户手势事件。这可以防止网站向用户发送大量窗口。但是,这对多窗口应用程序造成了问题。为了解决此限制,您可以设计您的应用程序以:
- 一次最多打开一个新窗口。
- 重用现有窗口以显示不同页面。
- 建议用户更新其浏览器设置以允许多个窗口。
在我们的演示应用程序中,我们选择了第三种方案。我们的 openWindow() 函数包含以下部分:
// …
if (windowRef === null) {
// If the browser is blocking new windows, close any windows that were
// able to open and display instructions to help the user fix this problem
closeAllWindows();
popoverElem.showPopover();
} else {
// Store a reference to the window in the windowRefs array
windowRefs.push(windowRef);
}
// …
如果浏览器阻止了新窗口,则生成的 windowRef 将为 null。在这种情况下,我们会运行 closeAllWindows() 函数来关闭在开始阻止之前成功打开的任何窗口,并显示一个 弹出元素,解释如何禁用弹出窗口阻止程序。
简单的单窗口每显示器案例
如果您想在每个可用显示器上打开一个窗口,该窗口的宽度和高度都与显示器相同,您可以使用类似这样的模式:
// Open a window on each screen of the device
for (const screen of screenDetails.screens) {
openWindow(
screen.availLeft,
screen.availTop,
screen.availWidth,
screen.availHeight,
url,
);
}
窗口管理事件
窗口管理 API 提供了一些用于响应可用屏幕变化的事件。
ScreenDetailsscreenschange事件-
当屏幕连接到或从系统中断开时触发。
ScreenDetailscurrentscreenchange事件-
当窗口的当前屏幕以某种方式发生变化时触发。
Screenchange事件-
当特定屏幕以某种方式发生变化时触发。
因此,例如,您可以使用 screenschange 事件来检测可用屏幕何时发生变化(例如,当屏幕插入或拔出时),报告该变化,关闭所有窗口,并更新窗口布局以适应新配置。
screenDetails.addEventListener("screenschange", () => {
// If the new number of screens is different to the old number of screens,
// report the difference
if (screenDetails.screens.length !== noOfScreens) {
console.log(
`The screen count changed from ${noOfScreens} to ${screenDetails.screens.length}`,
);
}
// If the windows are open, close them and then open them again
// So that they fit with the new screen configuration
if (windowRefs.length > 0) {
closeAllWindows();
openWindows();
}
});
requestFullscreen() 屏幕选项
窗口管理 API 在 requestFullscreen() 方法中添加了一个新的 screen 选项,允许您指定要在哪个屏幕上将元素置于全屏模式。例如,如果您想在主 OS 屏幕上将其设为全屏:
try {
const primaryScreen = (await getScreenDetails()).screens.find(
(screen) => screen.isPrimary,
);
await document.body.requestFullscreen({ screen: primaryScreen });
} catch (err) {
console.error(err.name, err.message);
}