取得已連線螢幕的相關資訊,並相對於這些螢幕放置視窗。
發布日期:2020 年 9 月 14 日
視窗管理 API
Window Management API 可讓您列舉連線至電腦的螢幕,並將視窗放置在特定螢幕上。
建議用途
可能使用這項 API 的網站包括:
- 多視窗圖像編輯器 (如 Gimp) 可將各種編輯工具放在精確定位的視窗中。
- 虛擬交易平台可在多個視窗中顯示市場趨勢,且所有視窗都能以全螢幕模式檢視。
- 投影片應用程式可以在內部主螢幕上顯示演講者備忘稿,並透過外部投影機顯示簡報。
如何使用 Window Management API
經過時間考驗的視窗控制方法 Window.open() 無法辨識額外畫面,雖然這個 API 的某些方面似乎有點過時,例如 windowFeatures
DOMString 參數,但多年來一直為我們提供良好的服務。如要指定視窗的位置,可以將座標分別傳遞為 left 和 top (或 screenX 和 screenY),並將所需大小分別傳遞為 width 和 height (或 innerWidth 和 innerHeight)。舉例來說,如要在距離左側 50 像素和頂端 50 像素的位置開啟 400×300 的視窗,可以使用下列程式碼:
const popup = window.open(
'https://example.com/',
'My Popup',
'left=50,top=50,width=400,height=300',
);
您可以查看 window.screen 屬性,取得目前畫面的相關資訊,該屬性會傳回 Screen 物件。這是 MacBook Pro 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
*/
和大多數科技業人士一樣,我必須適應 2020 年的工作模式,並在家中設置個人辦公室。我的設定如相片所示 (如有興趣,請參閱完整設定詳細資料)。我將 MacBook 旁的 iPad 透過「螢幕共享」連線到筆電,因此需要時可以快速將 iPad 變成第二個螢幕。
如果想善用大螢幕,可以將程式碼範例中的彈出式視窗放到第二個螢幕上。我會這樣做:
popup.moveTo(2500, 50);
由於無法得知第二個螢幕的尺寸,因此這只是粗略的估計值。「資訊」window.screen只會顯示內建螢幕的資訊,不會顯示 iPad 螢幕的資訊。回報的內建螢幕 width
為 1680 像素,因此移至 2500 像素可能能將視窗移至 iPad,因為我知道視窗位於 MacBook 的右側。How
can I do this in the general case? 事實上,有比猜測更好的方法。也就是 Window Management API。
特徵偵測
如要檢查是否支援 Window Management API,請使用:
if ('getScreenDetails' in window) {
// The Window Management API is supported.
}
window-management 權限
使用 Window Management API 前,我必須先徵求使用者授權。window-management 權限可透過 Permissions API 查詢,如下所示:
let granted = false;
try {
const { state } = await navigator.permissions.query({ name: 'window-management' });
granted = state === 'granted';
} catch {
// Nothing.
}
使用新舊權限名稱的瀏覽器並存時,請務必在要求權限時使用防禦性程式碼,如範例所示。
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;
});
瀏覽器可以選擇在首次嘗試使用新 API 的任何方法時,動態顯示權限提示。請繼續閱讀以瞭解詳情!
window.screen.isExtended 屬性
如要瞭解裝置是否連接多個螢幕,請存取 window.screen.isExtended 屬性。傳回 true 或 false。就我的設定而言,這會傳回 true。
window.screen.isExtended;
// Returns `true` or `false`.
getScreenDetails() 方法
現在我知道目前的設定是多螢幕,因此可以使用 Window.getScreenDetails() 取得第二個螢幕的詳細資訊。呼叫這個函式會顯示權限提示,詢問我是否允許網站在畫面上開啟及放置視窗。函式會傳回 Promise,並以 ScreenDetailed 物件解析。在連線 iPad 的 MacBook Pro 13 上,這包括含有兩個 ScreenDetailed 物件的 screens 欄位:
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
}]
}
*/
您可以在 screens 陣列中找到已連結螢幕的相關資訊。請注意,iPad 的 left 值從 1680 開始,這正是內建螢幕的 width。這樣我才能確切判斷螢幕的邏輯排列方式 (並排、重疊等)。現在每個畫面也有資料,可顯示畫面是否為 isInternal 或 isPrimary。請注意,內建螢幕不一定是主要螢幕。
currentScreen 欄位是與目前 window.screen 對應的即時物件。物件會在跨螢幕視窗放置位置或裝置變更時更新。
screenschange 活動
現在只差偵測螢幕設定變更的方法了。新事件 screenschange 的用途正是如此:只要螢幕星座圖經過修改,就會觸發這個事件。(請注意,事件名稱中的「screens」是複數。)也就是說,每當插入或拔除新螢幕或現有螢幕 (如果是「螢幕共享」則為實體或虛擬),就會觸發事件。
您需要非同步查詢新畫面的詳細資料,screenschange 事件本身不會提供這項資料。如要查詢畫面詳細資料,請使用來自快取 Screens 介面的即時物件。
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;
}
});
currentscreenchange 活動
如果我只對目前畫面 (也就是即時物件 currentScreen 的值) 的變更感興趣,可以監聽 currentscreenchange 事件。
const screenDetails = await window.getScreenDetails();
screenDetails.addEventListener('currentscreenchange', async (>event) = {
const details = screenDetails.currentScreen;
console.log('The current screen has changed.', event, details);
});
change 活動
最後,如果我只對特定畫面的變更感興趣,可以監聽該畫面的 change 事件。
const firstScreen = (await window.getScreenDetails())[0];
firstScreen.addEventListener('change', async (>event) = {
console.log('The first screen has changed.', event, firstScreen);
});
新的全螢幕選項
到目前為止,您可以透過適當命名的 requestFullScreen() 方法,要求以全螢幕模式顯示元素。這個方法會採用 options 參數,您可以在其中傳遞 FullscreenOptions。目前為止,唯一的屬性是 navigationUI。Window Management API 新增了 screen 屬性,可讓您決定要在哪個螢幕上啟動全螢幕檢視畫面。舉例來說,如要將主要畫面設為全螢幕:
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
您無法對 Window Management API 進行 Polyfill,但可以墊片其形狀,以便專門針對新 API 編寫程式碼:
if (!('getScreenDetails' in window)) {
// Returning a one-element array with the current screen,
// noting that there might be more.
window.getScreenDetails = as>ync () = [window.screen];
// Set to `false`, noting that this might be a lie.
window.screen.isExtended = false;
}
API 的其他層面 (即各種螢幕變更事件和 FullscreenOptions 的 screen 屬性) 永遠不會觸發,或會遭到不支援的瀏覽器忽略。
示範
如果您密切關注各種加密貨幣的發展,可以在應用程式中設定單一畫面,輕鬆監控市場。(我非常不希望這樣,因為我熱愛這個星球,但為了本文,請假設我希望如此。)
由於這是加密貨幣,市場隨時可能變得忙碌。如果發生這種情況,我可以在辦公桌上快速切換到多螢幕設定。我只要點選任一幣別的視窗,就能在對面螢幕的全螢幕檢視畫面中,快速查看完整詳細資料。這是我最近在上次 YCY 大屠殺期間拍的照片。我完全沒料到會發生這種事,當下雙手遮臉。
安全性和權限
Chrome 團隊已根據「控管強大的網頁平台功能存取權」中定義的核心原則 (包括使用者控制、透明度和人體工學),設計及實作 Window Management API。Window Management API 會公開裝置連線螢幕的新資訊,增加使用者的指紋辨識介面,尤其是經常在裝置上連接多個螢幕的使用者。為減輕這項隱私權疑慮,公開的螢幕屬性僅限於常見刊登位置用途所需的最低限度。
網站必須取得使用者授權,才能取得多螢幕資訊,並在其他螢幕上放置視窗。Chromium 會傳回詳細的畫面標籤, 但瀏覽器可以傳回較不具描述性的標籤 (甚至是空白標籤)。
使用者控制項
使用者可完全掌控設定的曝光度。使用者可以接受或拒絕權限提示,並透過瀏覽器的網站資訊功能,撤銷先前授予的權限。
企業控管
Chrome Enterprise 使用者可以控管 Window Management API 的多個層面,詳情請參閱原子政策群組設定的相關章節。
透明度
瀏覽器的網站資訊會顯示是否已授予使用 Window Management API 的權限,您也可以使用 Permissions API 查詢這項資訊。
權限保留
瀏覽器會保留授予的權限。如要撤銷權限,請透過瀏覽器的網站資訊進行。
意見回饋
API 是否有任何不符合預期的地方?或者,是否有缺少的屬性或方法,導致您無法實作想法?對安全模型有任何問題或意見嗎?
- 在對應的 GitHub 存放區中提出規格問題,或在現有問題中加入您的想法。
- 針對 Chrome 的實作方式提出錯誤報告。請務必盡可能提供詳細資訊、重現步驟,並在「Components」方塊中輸入
Blink>Screen>MultiScreen。
支援 API
您打算使用 Window Management API 嗎?您的公開支持有助於 Chrome 團隊排定功能優先順序,並向其他瀏覽器供應商展現支援這些功能的重要性。
- 在 WICG Discourse 討論串中分享您的使用計畫。
- 使用主題標記
#WindowManagement傳送推文給 @ChromiumDev,告訴我們您在何處使用這項功能,以及使用方式。 - 要求其他瀏覽器供應商實作 API。
資源
- 規格草案
- 公開說明
- Window Management API 試用版 | Window Management API 試用版來源
- Chromium 追蹤錯誤
- ChromeStatus.com 項目
- Blink 元件:
Blink>Screen>MultiScreen - TAG 審查
- 實驗意圖
特別銘謝
Window Management API 規格是由 Victor Costan、Joshua Bell 和 Mike Wasserman 編輯。這個 API 是由 Mike Wasserman 和 Adrienne Walker 實作。本文由 Joe Medley、François Beaufort 和 Kayce Basques 審查。感謝 Laura Torrent Puig 提供相片。