自定义 PWA 标题栏的窗口控件叠加层

使用窗口控件旁边的标题栏区域,让您的 PWA 更像是一个应用。

还记得我写的让 PWA 感觉更像是应用这篇文章吗?您还记得吗? 我之前提到过自定义应用的标题栏 打造更接近应用的体验的策略其外观示例如下: 显示的是 macOS“播客”应用。

<ph type="x-smartling-placeholder">
</ph> 一个 macOS 播客应用标题栏,显示了与当前播放的播客相关的媒体控件按钮和元数据。 <ph type="x-smartling-placeholder">
</ph> 自定义标题栏让您的 PWA 更像是一个平台专用应用。

现在,您可能会想要反驳“播客”这一 macOS 应用, 可执行所需的操作,而无需使用浏览器 规则。的确如此,但好消息是,窗口控件叠加层功能是 通过本文,您可以很快为 PWA 创建类似的界面。

窗口控件叠加层组件

窗口控件叠加层由四个子功能组成:

  1. "display_override" 字段的 "window-controls-overlay" 值: Web 应用清单。
  2. CSS 环境变量 titlebar-area-xtitlebar-area-ytitlebar-area-widthtitlebar-area-height
  3. 将先前专有的 CSS 属性 -webkit-app-region 标准化为 app-region 属性,用于定义 Web 内容中的可拖动区域。
  4. 一种通过 window.navigatorwindowControlsOverlay 位成员。

什么是窗口控件叠加层

标题栏区域是指窗口控件(即 按钮来最小化、最大化、关闭等),且通常包含应用的标题。窗户 Controls 叠加层可让渐进式 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"]
}

仅当满足以下所有条件时,窗口控件叠加层才会显示:

  1. 应用不会在浏览器中打开,而是在单独的 PWA 窗口中打开。
  2. 清单包含 "display_override": ["window-controls-overlay"]。(其他值为 之后允许。)
  3. PWA 在桌面设备操作系统上运行。
  4. 当前来源与安装了 PWA 的来源一致。

这会导致一个空白的标题栏区域,左侧显示常规窗口控件, 具体取决于操作系统

<ph type="x-smartling-placeholder">
</ph> 一个带有空白标题栏的应用窗口,左侧带有窗口控件。 <ph type="x-smartling-placeholder">
</ph> 一个空白标题栏,已准备好显示自定义内容。

将内容移入标题栏

既然标题栏中有空间了,您就可以将标题移到那里了。在本文中, 构建了 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 内容组成一样, 的确如此

<ph type="x-smartling-placeholder">
</ph> 一个在标题栏中有一个搜索栏的应用窗口。 <ph type="x-smartling-placeholder">
</ph> 新的标题栏处于有效状态且可以自适应。

确定标题栏的哪些部分是可拖动的

尽管上面的屏幕截图表明您已完成操作,但您尚未完成全部操作。PWA 窗口为 不再可拖动(除了一个非常小的区域),因为窗口控件按钮无法拖动 而标题栏的其余部分则由搜索微件组成。使用以下方法解决此问题 值为 dragapp-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 后,用户就可以像往常一样通过拖动 divimg、 或 label。只有 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() 查询窗口控件叠加层区域足以进行一次性查询 例如根据窗口控件的位置设置正确的背景图片, 在其他情况下,则需要进行更精细的控制。例如,一个可能的应用场景 根据可用空间调整窗口控件叠加层,并直接在窗口中添加笑话 (在有足够的空间时)叠加层。

<ph type="x-smartling-placeholder">
</ph> 带有缩短文本的窄窗口上的窗口控件叠加区域。 <ph type="x-smartling-placeholder">
</ph> 适用于窄窗口的标题栏控件。

您可以通过订阅 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() 变量的后备值 都会影响定位的位置在支持的浏览器中,您还可以决定不显示 通过检查叠加层的 visible 属性为窗口控件叠加层指定 HTML,以及 它会报告 false,然后隐藏该 HTML 内容。

<ph type="x-smartling-placeholder">
</ph> 在浏览器标签页中运行的 PWA,其正文中显示了窗口控件叠加层。 <ph type="x-smartling-placeholder">
</ph> 在旧版浏览器上,标题栏的控件可以很容易地显示在正文中。

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

<ph type="x-smartling-placeholder">
</ph> 独立模式下运行的 PWA,正文中显示窗口控件叠加层。 <ph type="x-smartling-placeholder">
</ph> 在旧版浏览器上,标题栏的控件可以很容易地显示在正文中。

界面注意事项

虽然您可能很想,但我们不建议在“窗口控件叠加层”区域创建经典下拉菜单。这样做会违反 macOS 设计指南 用户期望在其上使用菜单栏(系统提供的菜单栏和 自定义自定义选项)。

如果您的应用提供全屏体验,请仔细考虑这样是否有意义 窗口控件叠加层,使其成为全屏视图的一部分。您或许可以 重新排列布局时 onfullscreenchange 事件触发。

演示

我创建了一个演示,您可以在 支持和不支持的浏览器以及安装状态和非安装状态的浏览器不同。对于 实际的窗口控件叠加层体验,则需要安装应用。您可在下方查看两个屏幕截图,了解预期结果。通过 Glitch 上提供了应用的源代码

<ph type="x-smartling-placeholder">
</ph> 带有窗口控件叠加层的维基媒体精选内容演示应用。 <ph type="x-smartling-placeholder">
</ph> 演示版应用可供实验。

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

<ph type="x-smartling-placeholder">
</ph> 维基媒体精选内容演示应用,带有窗口控件叠加层,可有效搜索“cleopa...”一词突出显示其中一篇文章含有匹配字词“克利奥帕特拉”。 <ph type="x-smartling-placeholder">
</ph> 使用窗口控件叠加层的搜索功能。

安全注意事项

Chromium 团队按照核心原则设计和实现了 Window Controls Overlay API (如控制对强大的 Web 平台功能的访问权限中所述),包括 透明度和人体工学设计。

仿冒邮件

只让网站部分控制标题栏,让开发者可以在 之前是一个由浏览器控制的可信区域。目前,在 Chromium 浏览器中是独立的 模式包括一个标题栏,首次启动时该标题栏会在左侧显示网页标题; 右侧是网页的来源(后跟“设置及其他”按钮和窗口 控件)。几秒钟后,源文本将消失。如果浏览器设置为从右到左 (RTL) 语言,则此布局会翻转,使原文位于左侧。这会打开 如果原点和 叠加层的右边缘例如,来源“evil.ltd”可以带有 网站“google.com”,导致用户相信该来源值得信赖。计划保持 源文本,以便用户了解应用的出处,并确保与他们的 预期。对于配置 RTL 的浏览器,原点右侧必须有足够的内边距 文本,以防止恶意网站在不安全的来源中附加可信来源。

数字“指纹”收集

启用窗口控件叠加层和可拖动区域不会姿势 除了功能检测之外,相当多的隐私问题。然而,由于 不同尺寸和位置上的窗口控制按钮 系统、 navigator.windowControlsOverlay.getTitlebarAreaRect() 方法会返回 DOMRect 其位置和尺寸可揭示操作系统的相关信息 浏览器的运行状态目前,开发者已经能够发现 ,但由于存在数字“指纹”收集方面的问题, 讨论冻结 UA 字符串和统一操作系统版本。这里有 持续努力了解 窗口控件叠加层的大小会随当前变化而变化, 它们在各操作系统版本中都相当稳定, 这对于观察次要操作系统版本非常有用。虽然这是一个潜在的 指纹问题,此问题仅适用于使用自定义 标题栏功能,不适用于常规的浏览器用法。此外, navigator.windowControlsOverlay API 将不适用于 PWA 内嵌入的 iframe。

在 PWA 内导航到其他来源会导致 PWA 回退到正常的独立模式 标题栏,即使它满足上述条件并随窗口控件叠加层一起启动也是如此。 这是为了配合导航到其他原点时出现的黑色栏。更新后 导航回原始原点时,将再次使用窗口控件叠加层。

<ph type="x-smartling-placeholder">
</ph> 外部导航的黑色网址栏。 <ph type="x-smartling-placeholder">
</ph> 当用户导航到其他出发地时,系统会显示黑色条。

反馈

Chromium 团队希望了解您使用 Window Controls Overlay API 的体验。

向我们介绍 API 设计

API 是否有什么无法按预期运行?或者是否缺少方法 需要哪些资源或属性来实现您的想法?对安全性有疑问或意见 模型?在相应的 GitHub 代码库中提交规范问题,或将您的想法添加到 现有问题。

报告实现存在的问题

您是否发现了 Chromium 实现方面的错误?或者,实现是否与规范不同? 在 new.crbug.com 上提交 bug。请务必提供尽可能多的细节信息 简单的重现说明,并在 Components 中输入 UI>Browser>WebAppInstalls 方框。Glitch 非常适用于分享轻松快速的重现问题。

表示对 API 的支持

您打算使用 Window Controls Overlay API 吗?您的公开支持对 Chromium 团队有所帮助 确定各项功能的优先级,并向其他浏览器供应商展示支持这些功能的重要性。

@ChromiumDev 发送一条推文,附上 #WindowControlsOverlay # 标签,并告诉我们您使用它的地点和方式。

实用链接

致谢

窗口控件叠加层由 Microsoft Edge 团队的 Amanda Baker。 本文由 Joe MedleyKenneth Rohde Christiansen。主打图片提供方 Sigmund,来自 Un 创立网站