使用窗口控件旁边的标题栏区域,让 PWA 更像应用。
如果您还记得我的文章让 PWA 更像应用,您可能还记得我曾提到自定义应用标题栏是打造更像应用体验的一种策略。以下示例展示了 macOS 版“播客”应用中的显示效果。

现在,您可能会提出异议,认为“播客”是一款平台专用的 macOS 应用,它不在浏览器中运行,因此可以随心所欲,而不必遵守浏览器的规则。没错,但好消息是,本文的主题“窗口控件叠加层”功能很快就能让您为 PWA 创建类似的用户界面。
窗口控件叠加层组件
窗口控件叠加层包含以下四个子功能:
- Web 应用清单中
"display_override"
字段的"window-controls-overlay"
值。 - CSS 环境变量
titlebar-area-x
、titlebar-area-y
、titlebar-area-width
和titlebar-area-height
。 - 之前专有的 CSS 属性
-webkit-app-region
已标准化为app-region
属性,用于在 Web 内容中定义可拖动区域。 - 一种通过
window.navigator
的windowControlsOverlay
成员查询和解决窗口控件区域问题的机制。
什么是窗口控件叠加层
标题栏区域是指窗口控件(即用于最小化、最大化、关闭等的按钮)左侧或右侧的空间,通常包含应用的标题。借助窗口控件叠加层,渐进式 Web 应用 (PWA) 可以通过将现有的全宽度标题栏替换为包含窗口控件的小型叠加层,提供更像应用的感觉。这样一来,开发者便可以在之前由浏览器控制的标题栏区域中放置自定义内容。
当前状态
步骤 | 状态 |
---|---|
1. 创建说明 | 完成 |
2. 创建规范的初始草稿 | 完成 |
3. 收集反馈并迭代设计 | 进行中 |
4. 源试用 | 完成 |
5. 发布 | 完成(在 Chromium 104 中) |
如何使用窗口控件叠加层
将 window-controls-overlay
添加到 Web 应用清单
渐进式 Web 应用可以通过在 Web 应用清单中添加 "window-controls-overlay"
作为主要 "display_override"
成员来选择启用窗口控件叠加层:
{
"display_override": ["window-controls-overlay"]
}
仅当满足以下所有条件时,窗口控件叠加层才会显示:
- 应用不会在浏览器中打开,而是在单独的 PWA 窗口中打开。
- 清单包含
"display_override": ["window-controls-overlay"]
。(之后允许使用其他值)。 - PWA 正在桌面操作系统上运行。
- 当前来源与安装 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 内容组成一样做出反应,而实际上它确实是由常规 HTML 内容组成的。

确定标题栏的哪些部分可拖动
虽然上面的屏幕截图显示您已完成,但实际上您尚未完成。PWA 窗口不再可拖动(除了很小的区域),因为窗口控件按钮不是拖动区域,而标题栏的其余部分由搜索 widget 组成。使用值为 drag
的 app-region
CSS 属性可解决此问题。在具体情况下,除了 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,用户可以像往常一样通过拖动 div
、img
或 label
来拖动应用窗口。只有 input
元素是可互动的,因此可以输入搜索查询。
功能检测
通过测试 windowControlsOverlay
是否存在,可以检测到对窗口控件叠加层的支持:
if ('windowControlsOverlay' in navigator) {
// Window Controls Overlay is supported.
}
使用 windowControlsOverlay
查询窗口控件区域
目前的代码存在一个问题:在某些平台上,窗口控件位于右侧,而在其他平台上,窗口控件位于左侧。更糟糕的是,“三点状”Chrome 菜单的位置也会根据平台而变化。这意味着,线性渐变背景图片需要动态调整,以从 #131313
变为 maroon
或从 maroon
变为 #131313
再变为 maroon
,从而与由 <meta name="theme-color" content="maroon">
确定的标题栏 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 在标签页中运行时,也不会显示该按钮。如需检测这种情况,您可以查询 windowControlsOverlay
的 visible
属性:
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.visible
为 true
时),才会触发此事件。
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),
);
在标签页中运行以及在不支持的浏览器中运行时的兼容性
您需要考虑以下两种可能的情况:
- 应用在支持 Window Controls Overlay 的浏览器中运行,但应用是在浏览器标签页中使用的。
- 应用在不支持窗口控件叠加层的浏览器中运行的情况。
在这两种情况下,默认情况下,为窗口控件叠加层构建的 HTML 将以内嵌方式显示,就像常规 HTML 内容一样,并且 env()
变量的回退值将用于定位。在支持的浏览器中,您还可以通过检查叠加层的 visible
属性来决定是否显示为窗口控件叠加层指定的 HTML,如果该属性报告 false
,则隐藏该 HTML 内容。

请注意,不支持的浏览器将完全不考虑 "display_override"
Web 应用清单属性,或者不识别 "window-controls-overlay"
,因此会根据回退链使用下一个可能的值,例如 "standalone"
。

界面注意事项
虽然在窗口控件叠加层区域中创建经典下拉菜单可能很有吸引力,但我们不建议这样做。这样做会违反 macOS 设计指南,在该平台上,用户希望在屏幕顶部看到菜单栏(包括系统提供的菜单栏和自定义菜单栏)。
如果您的应用提供全屏体验,请仔细考虑窗口控件叠加层是否应成为全屏视图的一部分。当 onfullscreenchange
事件触发时,您可能需要重新排列布局。
演示
我创建了一个演示,您可以在支持和不支持的浏览器中以及在已安装和未安装状态下试用该演示。如需体验实际的窗口控件叠加层,您需要安装该应用。您可以在下方看到两张屏幕截图,了解预期效果。应用的源代码可在 Glitch 上获取。

窗口控件叠加层中的搜索功能可完全正常运行:

安全注意事项
Chromium 团队在设计和实现 Window Controls Overlay API 时,遵循了控制对强大的 Web 平台功能的访问权限中定义的核心原则,包括用户控制、透明度和人机工程学。
仿冒
允许网站部分控制标题栏,这会给开发者留下空间,让他们可以在之前受信任的浏览器控制区域中欺骗性地显示内容。目前,在 Chromium 浏览器中,独立模式包含一个标题栏,该标题栏在首次启动时会在左侧显示网页的标题,在右侧显示网页的来源(后跟“设置及更多”按钮和窗口控件)。几秒钟后,原始文本会消失。如果浏览器设置为从右到左 (RTL) 的语言,此布局会翻转,使源文本位于左侧。这会打开窗口控件叠加层,以在来源与叠加层右边缘之间的内边距不足时伪造来源。例如,来源“evil.ltd”可以附加可信网站“google.com”,从而让用户误以为来源可信。我们计划保留此原始文本,以便用户了解应用的来源,并确保其符合自己的预期。对于配置为 RTL 的浏览器,来源文本的右侧必须有足够的内边距,以防止恶意网站将不安全的来源附加到可信的来源。
数字“指纹”收集
启用窗口控件叠加和可拖动区域不会带来严重隐私问题,但功能检测除外。不过,由于各操作系统中窗口控制按钮的大小和位置各不相同,navigator.
方法会返回一个 DOMRect
,其位置和尺寸会泄露浏览器所运行的操作系统的信息。目前,开发者已经可以从用户代理字符串中发现操作系统,但由于存在指纹识别问题,因此有讨论建议冻结用户代理字符串并统一操作系统版本。浏览器社区目前正在努力了解窗口控件叠加层的大小在各个平台上的变化频率,因为目前的假设是这些控件在各个操作系统版本中都相当稳定,因此无法用于观察次要操作系统版本。虽然这是一个潜在的指纹识别问题,但它仅适用于使用自定义标题栏功能的已安装 PWA,不适用于常规浏览器使用。此外,navigator.
API 将无法用于嵌入在 PWA 中的 iframe。
导航
如果 PWA 内的来源不同,即使满足上述条件并使用窗口控件叠加层启动,也会回退到正常的独立标题栏。这是为了适应导航到不同来源时出现的黑条。在导航回原始来源后,系统将再次使用窗口控件叠加层。

反馈
Chromium 团队希望了解您在使用 Window Controls Overlay API 方面的体验。
介绍 API 设计
API 是否存在某些方面未按预期运行?或者,是否有缺少的方法或属性需要您来实现自己的想法?对安全模型有疑问或意见?在相应的 GitHub 代码库中提交规范问题,或在现有问题中添加您的想法。
报告实现方面的问题
您是否发现了 Chromium 的实现存在 bug?或者实现是否与规范不同?
请前往 new.crbug.com 提交 bug。请务必尽可能详细地说明问题,提供简单的重现说明,并在组件框中输入 UI>Browser>WebAppInstalls
。
显示对 API 的支持
您打算使用 Window Controls Overlay API 吗?您的公开支持有助于 Chromium 团队确定功能优先级,并向其他浏览器供应商展示支持这些功能的重要性。
请向 @ChromiumDev 发送包含 #WindowControlsOverlay
主题标签的 Twitter 微博,告诉我们您在何处以及如何使用它。
实用链接
致谢
Window Controls Overlay 由 Microsoft Edge 团队的 Amanda Baker 实现和指定。 本文由 Joe Medley 和 Kenneth Rohde Christiansen 审核。主打图片提供者:Sigmund;来源:Unsplash。