1 序言
2017 年 9 月 12 日凌晨,苹果在乔布斯剧院发布了 iPhone X。iPhone X 正面的全面屏上方有一条刘海,对于如何适配 iPhone X,苹果的 Human Interface Guidelines 文档有给出相关的说明,但是对于前端开发者来说,我们还应该关注到更多的东西。
iOS 11 新引入了若干个 Webkit API 来解决带来的适配问题,使用这些新的 API 我们可以更好的利用 iPhone X 屏幕的特性。
2 相关概念
在了解新 API 之前需要先了解苹果设备屏幕的相关概念,接下来简单的对之进行说明。
2.1 安全区域(Safe Area)
在苹果的 UIKit文档 中,提到了安全区域。
简单的说,将内容放在这样的 Safe area 可以确保内容不会被设备圆角(corners),传感器外壳(sensor housing,齐刘海)以及底部的 Home Indicator 遮挡。
3 相关 API
3.1 viewport-fit
3.1.1 基础介绍
iOS 11 与早期的版本有个不同的地方,Webview 内容将会尊重所谓的安全区域。这意味着,如果你有一个标题栏固定在顶部 position: fixed; top: 0
。它将会在屏幕顶部下面的 20px 开始渲染。当你向下滚动时,它会移动到状态栏的后面。当你向上滚动时,它会再次下降到状态栏下面,而且在 20px 的间隙中,内容会透出。在 Understanding the WebView Viewport in iOS 11 这篇文章中的视频可以看到实际是怎么样的。不过这个恼人的问题通过给 viewport-fit
进行配置即可解决。
viewport-fit
可以设置可视窗口(Visual Viewport)的大小。在设备的物理屏幕上看到的初始布局视窗。在圆形屏幕上,当前屏幕上显示的部分是圆形的,但是 Viewport 却是矩形的。因此,根据 Viewport 的大小,页面的某些部可能被剪切。
viewport-fit
可以设置可视视窗的大小,也就是裁剪区域,viewport-fit
接收以下三个值:
contain
:viewport 中可以显示整个页面的内容,也就是说那些 position 设置为 fixed 的元素会相对于 iOS 11 的安全区域定位。注意: 设置该属性值时,
border-boundary: display
和shape-inside: display
会失效。cover
:页面的内容完全覆盖整个 viewport。也就是说那些position: fixed
的元素会相对于 viewport 定位,这就可能导致页面中有的内容会被遮盖。auto
:默认值,表现与 contain 一致。
CSS Round Display Level 1 中有给出了一些对于这个属性的建议:
当在非矩形显示器上设置 viewport 的边界框(viewport Bounding Box)大小时,必须考虑以下因素:
- 由于 viewport 边界框(viewport Bounding Box)的面积大于显示区域,导致剪切区域
- 在 Viewport 边界框和显示区域之间存在间隙
开发人员可以决定哪一个因素比较重要。如果必须确保 web 页面的没有任何一部分被隐藏,那么避免发生剪裁的优先级要高于处理在 viewport 的边界框和屏幕的边框之间的间隙。如果不想让 web 页面被缩小导致可读性上降低,那么最好将
viewport-fit
设置为 cover ,但是要注意页面被裁剪的部分。
我们直接看规范中给出的例子:
1 | @viewport (viewport-fit: contain) { |
当将 viewport-fit
设置为 contain 时,viewport 会以显示完整的、最大的矩形页面为依据,去展示 web 内容。在 iPhone X 中可视区域内多余的区域会以白色的背景色显示。
1 | @viewport { |
当将 viewport-fit
设置为 cover 时,初始的 viewport 会受限于非矩形的展示区域,以填满展示区域为依据进行展示。
3.1.2 实际应用
使用 viewport-fit
只需在 viewport 的声明中增加 viewport-fit
的配置即可。
1 | <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover"/> |
通过给 viewport-fit
设置 cover 可以页面内容充满 iPhone X 的屏幕。下面是不作处理的 iPhone X 打开页面,可以看到有两条白边。
设置了 viewport-fit 为 cover 之后:
3.2 safe-area-inset-*
基本介绍
新出的 iPhone X 形状不规则,其状态栏的高不再是 20px,而且在摄像头和扬声器的设置下,你的标题栏内容将会完全无法访问到。需要注意的是,这也适用于固定在屏幕底部的页脚,它将被麦克风阻塞。在一开始的关于安全区域的图中就可以看到,底部是有一定区域在 safe area 之外。
在上面一个小节,通过设置 viewport-fit
我们将页面的内容铺满了整个屏幕,但是可以看到“齐刘海”会将一部分的内容遮挡,如果我们知道屏幕边缘到 safe area 边缘的距离,那就可以通过设置 padding
属性来解决这个问题了。
在设置 viewport-fit=cover
之后,Web 中会新增四个常量:
safe-area-inset-top
:可视区域顶部边缘到安全区域的距离(以CSS像素为单位)safe-area-inset-right
:可视区域最右边到安全区域的距离(以CSS像素为单位)safe-area-inset-left
:可视区域最左边到安全区域的距离(以CSS像素为单位)safe-area-inset-bottom
:可视区域底部边缘到安全区域的距离(以CSS像素为单位)
对应地苹果添加了一个方法,将安全区域布局指南暴露给 CSS 。他们添加类似一个CSS的变量概念,叫作 CSS constants 。通过 constant()
就可以获取到上面的几个常量。
通过给页面的容器元素增加以下的样式属性:
1 | padding: constant(safe-area-inset-top) constant(safe-area-inset-right) constant(safe-area-inset-bottom) constant(safe-area-inset-left); |
可以解决内容被遮挡的问题。
要注意在 Darryl Pogue 的文章中说到:
Note: iOS 11.0 uses the constant() syntax, but future versions will only support env()!
在 iOS 11 中仍使用 constant()
,但是在未来的版本将会抛弃 constant()
改用 env()
。对此 Darryl Pogue 给出的一个关于 header 的样式例子如下,简单做了渐进增强。
1 | header { |
3.3 max() & min ()
3.3.1 基本介绍
max() 和 min() 未当前版本未包含的功能
如果你在网站设计中采用了 safe-area-inset-*
,你可能会注意到,除了安全区域插入之外,你还需要指定最小的内填充。在上面页面中,如果我们使用了 constant(safe-area-inset-left)
,当设备旋转回垂直方向时,左边的安全插入距离变成了 0px,文本便紧贴着屏幕边缘了。
min()
和 max()
来实现,这两个函数会在未来的 Safari 技术预览版中可用。这两个函数都接受任意数量的参数,并返回最小值或最大值。它们能用在calc()里面,或者彼此嵌套。并且这两个函数都允许使用calc()作为参数,就像使用数字一样。
3.3.2 实际应用
在处理左右边距时使用max():
1 | @supports(padding: max(0px)) { |
使用
@support()
做功能检测很必要。
3.4 theme color
iPhone X 在 portrait 模式下(竖屏)状态栏和 URL 地址栏会有一定的透明度,所以他们会显示半透明的网页背景色。
通过声明 theme-color
即可:
1 | <meta name="theme-color" content="#676767"> |
4 简单的总结
本文提及的 API:
viewport-fit
:有contain
、cover
和auto
三个取值。safe-area-inset-*
:与 safe area 有关的几个常量,在设置viewport-fit=cover
后可以使用max()
和min()
:在 CSS 中便利地获取最大值和最小值
5 补充扩展
显然除了使用以上的 API 去适配 iPhone X,还会有其他情况要使用别的适配方式。
在张鑫旭大佬的这篇文章中有讲到这种效果如何实现,可以了解一下。
参考资料