自訂 PWA's 標題列的視窗控制項

使用視窗控制項旁邊的標題列區域,讓 PWA 更貼近應用程式。

如果您記得「讓 PWA 更貼近應用程式體驗」一文,可以複習先前提到的自訂應用程式標題列一文,瞭解如何打造更類似應用程式的使用體驗。以下是 macOS Podcasts 應用程式的顯示範例。

macOS 播客應用程式標題列,顯示媒體控制項按鈕,以及目前播放中 Podcast 的中繼資料。
自訂標題列可讓 PWA 有如特定平台的應用程式。

現在您可能會想對「播客」是平台專屬的 macOS 應用程式,且這類應用程式不會在瀏覽器中執行,因此您不必執行瀏覽器規則就能執行所需操作。是的,但好消息是,視窗控制項重疊功能是本文的這個主題,不久後就能為 PWA 建立類似的使用者介面。

視窗控制項重疊元件

「視窗控制項」疊加層包含四個子功能:

  1. 網頁應用程式資訊清單中 "display_override" 欄位的 "window-controls-overlay" 值,
  2. CSS 環境變數 titlebar-area-xtitlebar-area-ytitlebar-area-widthtitlebar-area-height
  3. 將先前專屬 CSS 屬性 -webkit-app-region 標準化為 app-region 屬性,以便定義網頁內容中的可拖曳區域。
  4. 透過 window.navigatorwindowControlsOverlay 成員查詢及處理視窗控制項區域的機制。

什麼是視窗控制項重疊

標題列區域是指視窗控制項左側或右側的空間 (也就是最小化、最大化、關閉等按鈕),且通常包含應用程式的標題。視窗控制項重疊讓漸進式網頁應用程式 (PWA) 提供類似於應用程式的使用體驗,將現有的完整寬度標題列替換為包含視窗控制項的小型疊加層。這可讓開發人員將自訂內容放在之前由瀏覽器控管的標題列區域。

目前狀態

步驟 狀態
1. 建立說明 完成
2. 建立規格的初始草稿 完成
3. 收集意見回饋並改進設計 進行中
4. 來源試用 課程結束
5. 啟動 完成 (在 Chromium 104 中)

如何使用視窗控制項重疊功能

window-controls-overlay 新增至網頁應用程式資訊清單

在網頁應用程式資訊清單中,將 "window-controls-overlay" 新增為主要 "display_override" 成員,漸進式網頁應用程式即可選擇加入視窗控制項重疊:

{
  "display_override": ["window-controls-overlay"]
}

只有在符合下列所有條件時,系統才會顯示視窗控制項重疊:

  1. 應用程式「不會」在瀏覽器中開啟,而是會在獨立的 PWA 視窗中開啟。
  2. 資訊清單包含 "display_override": ["window-controls-overlay"]。(後面允許其他值)。
  3. PWA 是在電腦作業系統上執行。
  4. 目前的來源與安裝 PWA 的來源相符。

結果是空白的標題列區域,且在左側或右側,顯示一般視窗控制項 (視作業系統而定)。

應用程式視窗顯示空白標題列,左側有視窗控制項。
可供自訂內容的空白標題列。

將內容移至標題列

現在標題列有更多空間,您可以移動其位置。我在本文中建構了 Wikimedia 精選內容 PWA。例如,搜尋文章標題中的文字時,應用程式的功能就很實用。搜尋功能的 HTML 程式碼如下所示:

<div class="search">
  <img src="logo.svg" alt="Wikimedia logo." width="32" height="32" />
  <label>
    <input type="search" />
    Search for words in articles
  </label>
</div>

如要將這個 div 移至標題列,必須使用部分 CSS:

.search {
  /* Make sure the `div` stays there, even when scrolling. */
  position: fixed;
  /**
   * Gradient, because why not. Endless opportunities.
   * The gradient ends in `#36c`, which happens to be the app's
   * `<meta name="theme-color" content="#36c">`.
   */
  background-image: linear-gradient(90deg, #36c, #131313, 33%, #36c);
  /* Use the environment variable for the left anchoring with a fallback. */
  left: env(titlebar-area-x, 0);
  /* Use the environment variable for the top anchoring with a fallback. */
  top: env(titlebar-area-y, 0);
  /* Use the environment variable for setting the width with a fallback. */
  width: env(titlebar-area-width, 100%);
  /* Use the environment variable for setting the height with a fallback. */
  height: env(titlebar-area-height, 33px);
}

您可以在下方螢幕截圖中查看這個程式碼的效果。標題列已完全回應。當您調整 PWA 視窗大小時,標題列會做出回應,就好像包含一般 HTML 內容一樣。

應用程式視窗,在標題列中包含搜尋列。
新的標題列已啟用且回應式。

判斷標題列的哪些部分可以拖曳

以上螢幕截圖顯示您已完成設定,但您尚未完成。因為視窗控制項按鈕不是拖曳區域,而標題列的其餘部分則包含搜尋小工具,因此 PWA 視窗不再是可拖曳的區域 (不侷限於極小的區域)。如要修正這個問題,請使用 app-region CSS 屬性,並將值設為 drag。在具體情況下,您可以將 input 元素以外的所有項目設為可拖曳。

/* The entire search `div` is draggable… */
.search {
  -webkit-app-region: drag;
  app-region: drag;
}

/* …except for the `input`. */
input {
  -webkit-app-region: no-drag;
  app-region: no-drag;
}

設定好這個 CSS 後,使用者就能照常拖曳 divimglabel 來拖曳應用程式視窗。只有 input 元素是互動式的,因此使用者可以輸入搜尋查詢。

功能偵測

您可以測試 windowControlsOverlay 的存在,以偵測視窗控制項疊加層的支援功能:

if ('windowControlsOverlay' in navigator) {
  // Window Controls Overlay is supported.
}

使用 windowControlsOverlay 查詢視窗控制區域

到目前為止,程式碼只有一個問題:在某些平台上,視窗控制項位於右側,其他的控制項則在左側。更糟的是,Chrome 選單「三點圖示」的位置也會根據平台變更的位置。也就是說,線性漸層背景圖片必須動態調整,才能執行 #131313maroonmaroon#131313maroon 執行,使其與標題列的 maroon 背景顏色融為一體 (由 <meta name="theme-color" content="maroon"> 決定)。實際做法為查詢 navigator.windowControlsOverlay 屬性的 getTitlebarAreaRect() API。

if ('windowControlsOverlay' in navigator) {
  const { x } = navigator.windowControlsOverlay.getTitlebarAreaRect();
  // Window controls are on the right (like on Windows).
  // Chrome menu is left of the window controls.
  // [ windowControlsOverlay___________________ […] [_] [■] [X] ]
  if (x === 0) {
    div.classList.add('search-controls-right');
  }
  // Window controls are on the left (like on macOS).
  // Chrome menu is right of the window controls overlay.
  // [ [X] [_] [■] ___________________windowControlsOverlay [⋮] ]
  else {
    div.classList.add('search-controls-left');
  }
} else {
  // When running in a non-supporting browser tab.
  div.classList.add('search-controls-right');
}

與先前一樣,經過修改的程式碼現在使用上述程式碼動態設定的兩個類別,而不是直接提供 .search 類別 CSS 規則中的背景圖片。

/* For macOS: */
.search-controls-left {
  background-image: linear-gradient(90deg, #36c, 45%, #131313, 90%, #36c);
}

/* For Windows: */
.search-controls-right {
  background-image: linear-gradient(90deg, #36c, #131313, 33%, #36c);
}

判斷是否顯示視窗控制項疊加層

無論如何,視窗控制項疊加層都不會在標題列區域顯示。雖然在不支援「視窗控制項重疊」功能的瀏覽器上自然不會出現此元素,但有相關 PWA 在分頁中執行時,也不一定會出現。如要偵測這種情況,您可以查詢 windowControlsOverlayvisible 屬性:

if (navigator.windowControlsOverlay.visible) {
  // The window controls overlay is visible in the title bar area.
}

您也可以在 JavaScript 和/或 CSS 中使用 display-mode 媒體查詢:

// Create the query list.
const mediaQueryList = window.matchMedia('(display-mode: window-controls-overlay)');

// Define a callback function for the event listener.
function handleDisplayModeChange(mql) {
  // React on display mode changes.
}

// Run the display mode change handler once.
handleDisplayChange(mediaQueryList);

// Add the callback function as a listener to the query list.
mediaQueryList.addEventListener('change', handleDisplayModeChange);
@media (display-mode: window-controls-overlay) { 
  /* React on display mode changes. */ 
}

收到幾何圖形變更的通知

使用 getTitlebarAreaRect() 查詢視窗控制項重疊區域可滿足一次性作業 (例如根據視窗控制項的位置設定正確的背景圖片),但在某些情況下,則需要更精細的控制項。舉例來說,可能的用途可能是根據可用空間調整視窗控制項重疊,並在視窗控制項重疊顯示足夠的空間時,直接在視窗控制項疊加畫面中加入笑話。

顯示在狹窄視窗中的視窗控制項重疊區域,內含短文字。
標題列控制項可配合較小的視窗調整。

如要取得幾何圖形變更的通知,可以訂閱 navigator.windowControlsOverlay.ongeometrychange 或設定 geometrychange 事件的事件監聽器。只有在視窗控制項疊加層 (也就是 navigator.windowControlsOverlay.visibletrue 時) 可見時,才會觸發這個事件。

const debounce = (func, wait) => {
  let timeout;
  return function executedFunction(...args) {
    const later = () => {
      clearTimeout(timeout);
      func(...args);
    };
    clearTimeout(timeout);
    timeout = setTimeout(later, wait);
  };
};

if ('windowControlsOverlay' in navigator) {
  navigator.windowControlsOverlay.ongeometrychange = debounce((e) => {
    span.hidden = e.titlebarAreaRect.width < 800;
  }, 250);
}

除了將函式指派給 ongeometrychange 之外,您也可以新增事件監聽器至 windowControlsOverlay,如下所示。如要瞭解這兩者之間的差異,請參閱 MDN

navigator.windowControlsOverlay.addEventListener(
  'geometrychange',
  debounce((e) => {
    span.hidden = e.titlebarAreaRect.width < 800;
  }, 250),
);

於分頁和非支援的瀏覽器執行時的相容性

要考量的情況有兩種:

  • 應用程式在支援視窗控制項重疊的瀏覽器中執行,但在瀏覽器分頁中使用應用程式的情況。
  • 應用程式在瀏覽器不支援視窗控制項重疊的情況下執行。

在這兩種情況下,針對視窗控制項重疊建構的 HTML 預設都會以內嵌方式顯示 (如同一般 HTML 內容),且 env() 變數的備用值會在定位時啟動。在支援的瀏覽器上,您還可以決定不要顯示視窗控制項重疊指定的 HTML,方法是查看疊加層的 visible 屬性;如果回報為 false,則隱藏該 HTML 內容。

在瀏覽器分頁中執行的 PWA,內文的視窗控制項會重疊顯示。
在舊版瀏覽器上,標題列的控制項可以輕鬆顯示在內文中。

提醒您,非支援的瀏覽器完全不會考慮 "display_override" 網頁應用程式資訊清單屬性,或是無法辨識 "window-controls-overlay",因此會依據備用鏈的下一個可能值,例如 "standalone"

以獨立模式執行的 PWA,會在主體中重疊顯示視窗控制項。
在舊版瀏覽器上,標題列的控制項可以輕鬆顯示在內文中。

使用者介面注意事項

雖然您可能很想在「視窗控制項」的「重疊」區域建立傳統下拉式選單,但我們不建議這麼做。這樣做會違反 macOS 的設計規範,也就是使用者預期螢幕頂端選單列 (包括系統提供的選單列和自訂選單) 的平台。

如果應用程式提供全螢幕體驗,請審慎評估是否要將視窗控制項重疊加入全螢幕檢視畫面。建議您在 onfullscreenchange 事件啟動時重新安排版面配置。

操作示範

我已經建立了一個示範,可以在不同支援和不支援的瀏覽器中,以及已安裝和未安裝的狀態中盡情發揮。如要使用實際的視窗控制項重疊體驗,您必須安裝應用程式。下圖顯示兩個螢幕截圖,說明大致內容。Glitch 提供應用程式的原始碼

內含視窗控制項疊加畫面的 Wikimedia 精選內容試用版應用程式。
歡迎試用這款試用版應用程式。

視窗控制項中的搜尋功能完全正常運作:

Wikimedia 精選內容試用版應用程式,含有視窗控制項重疊,且正在搜尋「cleopa...」字詞,醒目顯示有一篇包含相符字詞「Cleopatra」的文章。
使用「視窗控制項」疊加層的搜尋功能。

安全性考量

Chromium 團隊根據「控管強大的網路平台功能存取權」一文中定義的核心原則 (包括使用者控制項、資訊公開和人體工學) 設計及實作 Window Controls Overlay API。

假冒

讓網站對於標題列有部分控制權,可讓開發人員在過去由瀏覽器控管的信任區域,冒用內容。在 Chromium 瀏覽器中,獨立模式目前會在初次啟動時顯示標題列,網頁標題會在左側顯示網頁標題,右側則顯示網頁來源 (後面有「設定及其他」按鈕和視窗控制項)。經過幾秒後,原始文字就會消失。如果瀏覽器設定為由右至左 (RTL) 語言,此版面配置會翻轉,讓原始文字顯示在左側。如果原始設定與疊加層右側邊緣之間的邊框間距不足,系統就會開啟視窗控制項重疊,以假冒來源。舉例來說,「evil.ltd」來源可附加在信任的網站「google.com」,讓使用者誤以為來源可信。我們打算保留這項原始文字,讓使用者知道應用程式的來源,並確保其符合預期。如果是設定 RTL 的瀏覽器,來源文字的右側必須有足夠的邊框間距,以防惡意網站將不安全的來源附加在信任的來源。

數位指紋採集

啟用視窗控制項重疊和可拖曳區域不會產生功能偵測以外的重要隱私權疑慮。不過,由於視窗控制項按鈕的大小和位置在各種作業系統上差異,navigator.windowControlsOverlay.getTitlebarAreaRect() 方法會傳回 DOMRect,該位置和維度會顯示瀏覽器執行時所用作業系統的相關資訊。目前開發人員可以透過使用者代理程式字串找到 OS,但基於數位指紋採集疑慮,以下討論關於凍結通用 Analytics (分析) 字串和統合 OS 版本。瀏覽器社群正在努力瞭解視窗控制項大小在不同平台上重疊的頻率,因為目前的假設是在各個 OS 版本中穩定地穩定,因此對觀察次要 OS 版本沒有幫助。雖然這可能是指紋列印問題,但僅適用於採用自訂標題列功能的已安裝 PWA,且不適用於一般瀏覽器用途。此外,navigator.windowControlsOverlay API 也無法用於內嵌在 PWA 中的 iframe。

如果在 PWA 中前往其他來源,即使符合上述條件並透過視窗控制項重疊啟動,系統仍會改回使用一般的獨立標題列。這是為了適應導覽至不同來源時顯示的黑色長條。返回原始來源後,系統就會再次使用視窗控制項重疊。

來源外導覽的黑色網址列。
當使用者前往其他來源時,系統會顯示黑色長條。

意見回饋:

Chromium 團隊想瞭解你的 Window Controls Overlay API 使用體驗。

告訴我們 API 設計

該 API 有什麼功能不如預期嗎?或者您需要實作提案的方法或屬性嗎?對於安全性模型有任何疑問或意見嗎?在對應的 GitHub 存放區上提交規格問題,或是將您的想法新增至現有問題中。

回報導入問題

你在 Chromium 的實作方式中發現錯誤嗎?或者,實作項目是否與規格不同? 請前往 new.crbug.com 回報錯誤。請務必盡可能提供詳細的細節、重現簡易操作說明,並在「元件」方塊中輸入 UI>Browser>WebAppInstallsGlitch 適合用來快速分享簡單快速的提案,

展現對 API 的支援

您打算使用 Window Controls Overlay API 嗎?您的公開支援可協助 Chromium 團隊決定各項功能的優先順序,以及向其他瀏覽器廠商說明這項功能有多重要。

請在 Tweet 中附上 #WindowControlsOverlay 標記並傳送至 @ChromiumDev,讓我們知道您的使用位置和方式。

實用連結

特別銘謝

視窗控制項重疊是由 Microsoft Edge 團隊的 Amanda Baker 實作及指定。本文由 Joe MedleyKenneth Rohde Christiansen 審查。SigmundUnsplash 上提供的主頁橫幅。