使用 Window Management API

本指南介紹瞭如何使用 視窗管理 API。下面示例程式碼摘自我們的 多視窗學習環境 示例(請參閱 原始碼)。

特性檢測

您可以透過檢查當前 window 例項中是否存在 getScreenDetails 來進行視窗管理 API 的功能檢測。例如,如果 API 受支援,您可能希望提供一個按鈕來開啟多視窗顯示;如果不支援,則提供不同的體驗,例如建立指向不同頁面的連結。

js
if ("getScreenDetails" in window) {
  // The Window Management API is supported
  createButton();
} else {
  // The Window Management API is not supported
  createLinks(sites);
}

基本用法

視窗管理 API 的核心是 Window.getScreenDetails() 方法,它返回一個物件,其中包含使用者系統上所有可用螢幕的詳細資訊。

js
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() 來開啟和管理視窗,但上述資訊能更好地幫助您在多螢幕環境中執行此操作。例如,一個實用函式可能如下所示:

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

然後,您可以這樣呼叫此函式並在特定螢幕上開啟視窗:

js
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 陣列。這樣,您就可以在關閉一個視窗時關閉所有視窗。

js
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() 輪詢方法似乎最適合檢測多個視窗關閉的情況。使用 beforeunloadpagehidevisibilitychange 等事件被證明是不可靠的,因為當同時開啟多個視窗時,焦點/可見性的快速變化似乎會過早地觸發處理函式。

注意:上述示例的一個問題是,它在計算中使用常量值來表示 Chrome 視窗 UI 部分的大小(WINDOW_CHROME_XWINDOW_CHROME_Y),以使視窗大小計算正確。要在 API 的其他未來實現中建立精確大小的視窗,您需要維護一個小型瀏覽器 UI 大小庫,並使用瀏覽器檢測來發現哪個瀏覽器正在渲染您的應用程式,併為計算選擇正確的大小。或者,您也可以依賴不那麼精確的視窗大小。

處理瀏覽器彈出視窗阻止程式

在現代瀏覽器中,出於安全原因,每次呼叫 Window.open() 都需要一個單獨的使用者手勢事件。這可以防止網站向用戶傳送大量視窗。但是,這對多視窗應用程式造成了問題。為了解決此限制,您可以設計您的應用程式以:

  • 一次最多開啟一個新視窗。
  • 重用現有視窗以顯示不同頁面。
  • 建議使用者更新其瀏覽器設定以允許多個視窗。

在我們的演示應用程式中,我們選擇了第三種方案。我們的 openWindow() 函式包含以下部分:

js
// …

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() 函式來關閉在開始阻止之前成功開啟的任何視窗,並顯示一個 彈出元素,解釋如何停用彈出視窗阻止程式。

簡單的單視窗每顯示器案例

如果您想在每個可用顯示器上開啟一個視窗,該視窗的寬度和高度都與顯示器相同,您可以使用類似這樣的模式:

js
// 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 提供了一些用於響應可用螢幕變化的事件。

ScreenDetails screenschange 事件

當螢幕連線到或從系統中斷開時觸發。

ScreenDetails currentscreenchange 事件

當視窗的當前螢幕以某種方式發生變化時觸發。

Screen change 事件

當特定螢幕以某種方式發生變化時觸發。

因此,例如,您可以使用 screenschange 事件來檢測可用螢幕何時發生變化(例如,當螢幕插入或拔出時),報告該變化,關閉所有視窗,並更新窗口布局以適應新配置。

js
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 螢幕上將其設為全屏:

js
try {
  const primaryScreen = (await getScreenDetails()).screens.find(
    (screen) => screen.isPrimary,
  );
  await document.body.requestFullscreen({ screen: primaryScreen });
} catch (err) {
  console.error(err.name, err.message);
}