突破矩形框:探索PWA窗口控件覆盖技术

本文详细介绍了渐进式Web应用(PWA)的窗口控件覆盖功能,通过CSS环境变量和JavaScript API实现标题栏自定义,提升应用与操作系统的集成度,并解决拖拽和布局适配问题。

突破矩形框

CSS 是关于样式化盒子的。 事实上,整个网络都是由盒子组成的,从浏览器视口到页面上的元素。但偶尔会有新功能出现,让我们重新思考设计方法。

例如,圆形显示屏使得玩转圆形剪辑区域变得有趣。移动屏幕的凹口和虚拟键盘为最佳组织内容提供了挑战,以避免它们。而双屏或可折叠设备让我们重新思考如何在多种不同设备形态中最佳利用可用空间。

网络平台的这些最新发展使得设计产品既更具挑战性又更有趣。它们是我们突破矩形框的绝佳机会。

我想讨论一个与上述类似的新功能:渐进式Web应用(PWA)的窗口控件覆盖

渐进式Web应用模糊了应用和网站之间的界限。它们结合了两者的优点。一方面,它们像网站一样稳定、可链接、可搜索且响应迅速。另一方面,它们提供额外的强大功能,可以离线工作,并像原生应用一样读取文件。

作为设计表面,PWA非常有趣,因为它们挑战我们思考混合Web和设备原生用户界面可能是什么。特别是在桌面设备上,我们有超过40年的历史告诉我们应用应该是什么样子,而打破这种思维模式可能很困难。

但归根结底,桌面上的PWA受限于它们出现的窗口:一个顶部带有标题栏的矩形。

这就是典型桌面PWA应用的样子:

当然,作为PWA的作者,你可以选择标题栏的颜色(使用Web应用清单的theme_color属性),但仅此而已。

如果我们能跳出这个框框思考,并重新获得应用整个窗口的实际空间呢?这样做将使我们有机会让我们的应用更美观,并感觉更融入操作系统。

这正是窗口控件覆盖所提供的。这个新的PWA功能使得利用应用的整个表面区域成为可能,包括通常出现标题栏的地方。

关于标题栏和窗口控件

让我们从解释标题栏和窗口控件是什么开始。

标题栏是显示在应用窗口顶部的区域,通常包含应用的名称。窗口控件是使最小化、最大化或关闭应用窗口成为可能的操作元素或按钮,也显示在顶部。

窗口控件覆盖移除了标题栏和窗口控件区域的物理约束。它释放了应用窗口的完整高度,使标题栏和窗口控制按钮能够覆盖在应用程序的Web内容之上。

如果你在桌面计算机上阅读本文,快速浏览一下其他应用。很可能它们已经在做类似的事情。事实上,你用来阅读本文的网页浏览器使用顶部区域来显示标签页。

Spotify将专辑封面一直显示到应用窗口的顶部边缘。

Microsoft Word使用可用的标题栏空间来显示自动保存和搜索功能等。

此功能的全部意义在于允许你使用自己的内容利用这个空间,同时提供一种处理窗口控制按钮的方法。它使你能在一系列平台上提供这种修改后的体验,而不会对不支持窗口控件覆盖的浏览器或设备上的体验产生不利影响。毕竟,PWA全是关于渐进式增强的,所以这个功能是一个机会,可以在有额外空间时增强你的应用以使用它。

使用功能

在本文的其余部分,我们将在一个演示应用上工作,以了解更多关于使用该功能的信息。

演示应用称为1DIV。它是一个简单的CSS游乐场,用户可以使用CSS和单个HTML元素创建设计。

该应用有两个页面。第一个列出你创建的现有CSS设计:

第二个页面使你能够创建和编辑CSS设计:

由于我添加了一个简单的Web清单和服务工作者,我们可以将应用作为PWA安装在桌面上。这是在macOS上的样子:

在Windows上:

我们的应用看起来不错,但第一页中的白色标题栏浪费了空间。在第二页中,如果设计区域一直延伸到应用窗口的顶部,那将非常好。

让我们使用窗口控件覆盖功能来改进这一点。

启用窗口控件覆盖

该功能目前仍处于实验阶段。要尝试它,你需要在支持的浏览器中启用它。

截至目前,它已在Chromium中实现,作为Microsoft和Google之间的合作。因此,我们可以在Chrome或Edge中通过转到内部的about://flags页面并启用桌面PWA窗口控件覆盖标志来使用它。

使用窗口控件覆盖

要使用该功能,我们需要将以下display_override成员添加到我们的Web应用清单文件中:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
{
  "name": "1DIV",
  "description": "1DIV is a mini CSS playground",
  "lang": "en-US",
  "start_url": "/",
  "theme_color": "#ffffff",
  "background_color": "#ffffff",
  "display_override": [
    "window-controls-overlay"
  ],
  "icons": [
    ...
  ]
}

表面上,该功能真的很容易使用。这个清单更改是我们使标题栏消失并将窗口控件变为覆盖层所需的唯一东西。

然而,为了为所有用户提供良好的体验,无论他们使用什么设备或浏览器,并在我们的设计中充分利用标题栏区域,我们需要一些CSS和JavaScript代码。

这是应用现在的样子:

标题栏消失了,这是我们想要的,但我们的徽标、搜索字段和NEW按钮部分被窗口控件覆盖,因为现在我们的布局从窗口顶部开始。

在Windows上类似,不同之处在于关闭、最大化和最小化按钮出现在右侧,与PWA控制按钮分组在一起:

使用CSS避免窗口控件

随着该功能,引入了新的CSS环境变量:

  • titlebar-area-x
  • titlebar-area-y
  • titlebar-area-width
  • titlebar-area-height

你使用这些变量与CSS env()函数来将你的内容定位在标题栏原本会出现的位置,同时确保它不会与窗口控件重叠。在我们的案例中,我们将使用两个变量来定位我们的标题,其中包含徽标、搜索栏和NEW按钮。

1
2
3
4
5
6
header {
  position: absolute;
  left: env(titlebar-area-x, 0);
  width: env(titlebar-area-width, 100%);
  height: var(--toolbar-height);
}

titlebar-area-x变量给出了从视口左侧到标题栏出现位置的距离,而titlebar-area-width是其宽度。(记住,这不等于整个视口的宽度,只是标题栏部分,如前所述,不包括窗口控件。)

通过这样做,我们确保我们的内容完全可见。我们还定义了回退值(env()函数中的第二个参数),用于当变量未定义时(例如在不支持的浏览器上,或当窗口控件覆盖功能被禁用时)。

现在我们的标题适应其环境,并且感觉窗口控制按钮不是事后添加的。应用看起来更像原生应用。

更改窗口控件背景颜色以混合

现在让我们仔细看看我们的第二页:CSS游乐场编辑器。

不好。我们的CSS演示区域确实一直延伸到顶部,这是我们想要的,但窗口控件作为白色矩形出现在其上的方式相当刺眼。

我们可以通过更改应用的主题颜色来修复这个问题。有几种定义方式:

  • PWA可以在Web应用清单文件中使用theme_color清单成员定义主题颜色。然后操作系统以不同方式使用此颜色。在桌面平台上,它用于为标题栏和窗口控件提供背景颜色。
  • 网站也可以使用theme-color元标签。浏览器使用它来自定义网页周围的UI颜色。对于PWA,此颜色可以覆盖清单的theme_color

在我们的案例中,我们可以将清单theme_color设置为白色,为我们的应用提供正确的默认颜色。操作系统将在应用安装时读取此颜色值,并使用它使窗口控件背景颜色为白色。此颜色适用于我们带有演示列表的主页。

theme-color元标签可以在运行时使用JavaScript更改。所以我们可以这样做,在打开演示时用正确的演示背景颜色覆盖白色。

这是我们将使用的函数:

1
2
3
function themeWindow(bgColor) {
  document.querySelector("meta[name=theme-color]").setAttribute('content', bgColor);
}

有了这个,我们可以想象如何使用颜色和CSS过渡产生从列表页到演示页的平滑变化,并使窗口控制按钮与应用的其余界面混合。

拖拽窗口

现在,完全去掉标题栏确实有一个重要的可访问性后果:移动应用窗口要困难得多。

标题栏为用户提供了一个相当大的区域来点击和拖拽,但通过使用窗口控件覆盖功能,这个区域变得仅限于控制按钮所在的位置,用户必须非常精确地瞄准这些按钮之间来移动窗口。

幸运的是,这可以使用CSS与app-region属性修复。此属性目前仅在基于Chromium的浏览器中支持,并且需要-webkit-供应商前缀。

要使应用的任何元素成为窗口的拖拽目标,我们可以使用以下:

1
-webkit-app-region: drag;

也可以显式使元素不可拖拽:

1
-webkit-app-region: no-drag;

这些选项对我们很有用。我们可以使整个标题成为拖拽目标,但使其中的搜索字段和NEW按钮不可拖拽,以便它们仍然可以正常使用。

然而,因为编辑器页面不显示标题,用户在编辑代码时将无法拖拽窗口。所以让我们使用不同的方法。我们将在我们的标题之前创建另一个元素,也是绝对定位的,并专用于拖拽窗口。

1
2
<div class="drag"></div>
<header>...</header>
1
2
3
4
5
6
7
.drag {
  position: absolute;
  top: 0;
  width: 100%;
  height: env(titlebar-area-height, 0);
  -webkit-app-region: drag;
}

使用上面的代码,我们使可拖拽区域跨越整个视口宽度,并使用titlebar-area-height变量使其与标题栏原本的高度一样高。这样,我们的可拖拽区域与窗口控制按钮对齐,如下所示。

而且,现在,确保我们的搜索字段和按钮保持可用:

1
2
3
4
header .search,
header .new {
  -webkit-app-region: no-drag;
}

使用上面的代码,用户可以点击和拖拽标题栏原本所在的位置。这是一个用户期望能够用来在桌面上移动窗口的区域,我们没有打破这个期望,这很好。

适应窗口调整大小

对于应用来说,知道窗口控件覆盖是否可见以及其大小何时改变可能很有用。在我们的案例中,如果用户使窗口非常窄,将没有足够的空间容纳搜索字段、徽标和按钮,所以我们希望将它们向下推一点。

窗口控件覆盖功能带有一个我们可以使用的JavaScript API:navigator.windowControlsOverlay

API提供三个有趣的东西:

  • navigator.windowControlsOverlay.visible 让我们知道覆盖层是否可见。
  • navigator.windowControlsOverlay.getBoundingClientRect() 让我们知道标题栏区域的位置和大小。
  • navigator.windowControlsOverlay.ongeometrychange 让我们知道大小或可见性何时改变。

让我们使用这个来了解标题栏区域的大小,并在它太窄时移动标题向下。

1
2
3
4
5
6
if (navigator.windowControlsOverlay) {
  navigator.windowControlsOverlay.addEventListener('geometrychange', () => {
    const { width } = navigator.windowControlsOverlay.getBoundingClientRect();
    document.body.classList.toggle('narrow', width < 250);
  });
}

在上面的例子中,如果标题栏区域窄于250px,我们在应用的主体上设置narrow类。我们可以用媒体查询做类似的事情,但使用windowControlsOverlay API对于我们的用例有两个优点:

  • 它仅在功能被支持和使用时触发;否则我们不想调整设计。
  • 我们获得跨操作系统的标题栏区域大小,这很棒,因为窗口控件的大小在Mac和Windows上不同。使用媒体查询无法让我们确切知道剩余多少空间。
1
2
3
4
5
.narrow header {
  top: env(titlebar-area-height, 0);
  left: 0;
  width: 100%;
}

使用上面的CSS代码,我们可以在窗口太窄时移动我们的标题向下以避开窗口控制按钮,并相应地移动缩略图向下。

三十像素的激动人心设计机会

使用窗口控件覆盖功能,我们能够将我们的简单演示应用转变为在桌面设备上感觉更加集成的东西。 something that reaches out of the usual window constraints and provides a custom experience for its users.

实际上,此功能只给我们大约30像素的额外空间,并带来如何处理窗口控件的挑战。然而,这个额外空间和那些挑战可以转化为激动人心的设计机会。

各种形状和形式的设备不断被发明,网络不断演变以适应它们。新功能被添加到网络平台,以允许我们,Web作者,与那些设备更深入地集成。从手表或可折叠设备到台式计算机,我们需要演变我们的Web设计方法。现在为Web构建让我们跳出矩形框思考。

所以让我们拥抱这个。让我们使用我们已经掌握的标准技术,并尝试新想法,为所有设备提供量身定制的体验,全部来自一个代码库!

如果你有机会尝试窗口控件覆盖功能并对其有反馈,你可以在规范的存储库上打开问题。此功能的开发仍处于早期阶段,你可以帮助使其更好。或者,你可以查看该功能的现有文档,或此演示应用及其源代码。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计