阅读 446
App 中 H5 与原生的元素尺寸保持统一?

App 中 H5 与原生的元素尺寸保持统一?

本文考虑不同的受众,采用结论先行的方式(如「第一部分:综述」),关心技术原理和细节再看「第二部分」,如果想进一步对直接认知加以辨析,再看「第三部分」。

通过本文将会知道(What you'll learn):

  • 编码中的元素尺寸单位,pt、dp、px、rem,有何区别?
  • 点和点分辨率的含义,以及和元素尺寸单位的关系?
  • 一个元素在不同机型下的物理尺寸如何计算?
  • 一个元素在同一个机型下,用不同单位来实现,有何区别?
  • App 中 H5 与原生的元素尺寸保持统一,技术可否实现?
  • 每种单位实现字号,分别会受系统字号设置影响么?
  • 手机横向每英寸点数,和倍率、PPI 的关系?

第一部分:综述

背景

设计小姐姐:用户在 App 的原生页面和 H5 页面之间跳转,两边元素整体的尺寸可否保持一致?比如原生和 H5 的设计稿各有一个 10px 的正方形元素,这两个元素在用户感知的尺寸保持一致(在任意机型,两者的尺寸要么等比变化,要么都不变化)。

我抿了口茶,说道:那肯定不行的(似乎缺了点底气)。

设计小姐姐:……?

我:我看看吧。

—— 设计同学的想法, 是 App 中存在原生页面和 H5 页面,不希望用户在两种页面中浏览,感受出来整体 size 的不同。这个需求可以理解,至于能不能做到,需要调研下。

调研结论

目前的 App 中页面的情况是 “原生页面都按 pt(iOS)dp(Android) 作为单位,而前端 H5 都是按 rem”。那么要想实现两边统一:

  • 1、原生与 H5 对齐:据说原生代价很大,且笔者不懂原生,所以本文不详细说。
  • 2、前端 H5 与原生对齐
    • (1)H5 改用 px :H5 设计稿和原生设计稿一样,标注出不同屏幕下的适配规律,且前端开发、测试、产品回归也要留意在不同手机屏幕验收(不建议,H5 开发心智负担较大,且不符合前端开发习惯,容易出现故障,下文详述)
    • (2)H5 仍然用 rem 作为单位:个别页面需要元素和原生对齐,就中间固定宽度,左右两边留白。(需确认设计同学是否可接受,Case By Case)

如果你觉得 “业界前端都按 rem,所以不需要考虑统一”,那恭喜你,本剧终~。

如果你仍然想让 “H5 与原生对齐” 捏?那么可以继续往下看了。

第二部分:详解 “H5 与原生对齐?”

名词解析

为解析清晰,下文会直接用这里的名词和简写。

1、长度单位

  • cm(厘米):1 厘米 = 0.01 米
  • mm(毫米):1 毫米 = 0.1 厘米
  • inch(英寸):1 英寸 = 2.54 厘米 = 25.4 毫米

2、手机横向尺寸

本文在计量元素尺寸时,会将 inch 转为 cm 或者 mm,便于国内用户更好感知。

手机官方给的尺寸,是屏幕对角线的英寸数。如 iPhone12 是 6.1 inch:

image.png

其横纵向像素分辨率是 1170 X 2532(点分辨率是 390 X 844),每个像素(点)认为是完全正方形,所以该手机横向尺寸 xx 满足:

x/6.1=390/Math.sqrt(390390+844844)x/6.1 = 390 / Math.sqrt(390 * 390 + 844 * 844)

得出 iPhone12 横向尺寸 xx 为 2.558 inch,折合为 6.4992cm。有图有真相(因为实际尺寸不是 6.1,而是 6.06,参考 iphone-12/specs ,所以会稍微会少一点点):

特别注意,因为手机屏幕纵横比不一致,所以官方尺寸更大的手机,横向尺寸不一定更大(如下表,小米 6X 官方尺寸更大,但是横向尺寸更小):

机型官方尺寸横向尺寸
Oppo R9s/R115.5 inch6.84895cm
小米 6X5.99 inch6.804176cm

3、用户感知的元素大小

页面最终服务于用户,所以元素实际的大小不重要,重要的是用户感知的大小。因为页面元素的尺寸一般都是长宽等比例变化的,所以本文仅度量横向尺寸(纵向尺寸同理)。

用户感知的元素大小,等同于元素渲染在屏幕后,所占据的物理尺寸(也就是用尺子可以度量的尺寸),也称为视觉尺寸。详细来说:

  • 同一个手机上,不同元素:用户感知的尺寸,由其渲染出来后的物理尺寸决定。
  • 同一个页面元素,在不同手机上:用户感知的尺寸,也是由与该元素渲染出来后的物理尺寸决定(与不同手机屏幕的宽度关系不大)。

如果说一个手机屏幕横向有 XX 毫米,屏幕被分为 tt 个单位,而有一个元素占据 nn 个单位,那么用户可以感知的的该元素(物理宽度)就是:(X/t)n(X/t) * n 毫米。

4、点和点分辨率

手机官方会给出屏幕分辨率(单位是像素)和倍率(一般为 2 或 3,倍率是 1 的老手机,市面上几乎找不到了)。

而手机的点分辨率 = 屏幕渲染分辨率 / 倍率,如下表:

机型官方屏幕尺寸渲染分辨率倍率点分辨率(X)点分辨率(Y)
iPhone 6/6s/7/84.7750 x 13342375667
iPhone 12/136.11170 x 25323390844
iPhone (12/13) Pro Max6.71284 x 27783428926
Oppo R9s/R115.51080 x 19203360640
小米 6x5.991080 x 21603360720

以 iPhone12 为例,点分辨率是 390 X 844,代表屏幕横向被分为 390 个单位也就是 390 个点,同理,纵向被分为 844 个单位也就是 844 点。

在原生开发中,iOS 将这个「点」叫做 pt,Android 将这个「点」叫做 dp。

在 H5 的 CSS 开发中,用 390px 宽的 Banner 也可以刚好占满 iPhone12 的宽度,所以这里 1 个「点」的尺寸,也等同于前端开发中的 1 px。

特别注意:这里的 px 是前端 CSS 开发中的样式单位,前端开发中一个移动端页面宽度的 px 数 = 其点分辨率中的横向(X 方向)点数,不同于屏幕分辨率中的 px。两者关系是 “前者乘以设备倍率才是后者”)。这个问题上,原生和 H5 同学容易因为知识背景不同,沟通出分歧。

总结来说:

  • 「点分辨率」代表屏幕的横纵方向,被分为多少个独立像素(也就是「点」)
  • 每 1 个点,在原生开发中代表 1 个 pt 或 dp,在 H5 CSS 开发中代表 1 个 px

H5 与原生对齐?

在上文「名词解析」的「4、点和点分辨率」中,我们知道 1px = 1pt = 1dp。那么关于本文「H5 如何与原生对齐」就已经有答案了 ——> “H5 改用 px 作为单位,即可与 pt 或 dp 一致”。

但是移动端 H5 开发,业界一般都是用 rem 来适配不同屏幕宽度:

前端只需要在 375px 基础上开发完,针对较宽/窄的手机屏幕,等比放大/缩小根结点的基准字号,即可实现渲染出来的整个页面等比放大/缩小。具体做法是,设置 html { font-size: calc(100vw / 3.75) }。在屏幕宽度是 npx 的手机上,根节点字号就是(n/3.75)px(n/3.75)px

如果要改用 px 作为单位,存在一定的心智负担:

前端在 375px 基础上开发完后,需要看不同屏幕(iPhone 就有 320、390、414、428px 等宽度),是否会出现元素错位(比如一些元素掉到下一行或者跑到上一行),严重的话「被错位元素遮挡」可能导致在某些机型上,用户无法点击的故障。

确实需要让 H5 与原生对齐,可以商量两种方式:

(1)前端改用 px 作为单位:业务设计稿要考虑 320、375、400+ 等不同屏幕下自适应的规则,必要情况加以标注,而且测试、产品、设计等同学验收,也需要考虑不同机型。

(2)前端仍然用 rem 作为单位:大部分页面不和原生统一,个别设计确实有强需求的页面,则在较小的屏幕下开发,在较大的屏幕上“自适应”,比如 “中间区域固定 px 宽度,左右两边留白”。这种实现,和原生看上去元素大小接近(如字号,元素尺寸),但留白位置仍然有区别(原生页面留白在元素间距上,H5 则在页面左右两侧)。如下图:

image.png

第三部分:领域概念辨析

第一、二部分已经完成了「 H5 与原生的元素尺寸保持统一」的问题解析。但笔者在调研该问题时,发现该领域存在「直觉认知不准确」、「网上充斥着误导性的说法」的问题,分分钟让你怀疑人生(不要问我怎么知道 😭),所以增加该部分加以解析说明。

名词解析(续)

  • Q:这些名词为何不放在第二部分的「名词解析」中?
  • A: 因为这部分名次与对齐问题”真的“没关系,放一起会加重误区。

1、分辨率(点分辨率、渲染分辨率、物理分辨率)

(1)点分辨率(Points):「点」是抽象单位,指代横纵向坐标系上的单位数。

(2)渲染分辨率(Rendered Pixels):页面上的点,乘以倍率(通常是 1/2/3)就是渲染出来的像素数。倍率越高,渲染出来的像素越多。

(3)物理分辨率(Physical Pixels):设备屏幕的实际像素数,可能少于页面渲染出来的像素数,这时候需要除以一个因子来适配,我们叫「投到屏幕的缩放因子」。通常为 1 就是不缩放,但也有 iPhone 6+/6s+/7+/8+ 的因子是 1.15,而 iPhone 12mini/13mini则是 1.041。

image.png

2、倍率(density)

倍率(density),手机厂商设定,是根据当前像素密度指定将 dp 单位转换为像素时所必须使用的缩放系数。在中密度屏幕(mdpi)上,density 等于 1.0(也就是 160dpi);在高密度屏幕(hdpi)上,它等于 1.5;在超高密度屏幕(xhdpi)上,等于 2.0;在超超高密度屏幕(xxhdpi),等于 3.0。此数字是一个系数,用其乘以 dp 单位,即可得出当前屏幕的实际像素数。详见 Android 开发者文档-density

目前市面上的手机一般是 2 和 3,详见 屏幕尺寸大全

3、屏幕密度(PPI)

屏幕密度 PPI(Pixel Per Inch),在屏幕斜对角线方向,每英寸的像素数,即像素密度。如下公式,分子分母都是屏幕对角线维度的像素数和英寸数,其中,X 和 Y 是物理分辨率(px)。

image.png

PPI 的设计使得可以把较大的物理像素显示到真实屏幕上。以 iPhone 6s+ 为例 :

PPI=Math.sqrt(10801080+19201920)/5.5=401(px/inch)PPI = Math.sqrt(1080*1080+1920*1920)/5.5 = 401 (px/inch)
机型官方屏幕尺寸渲染分辨率物理分辨率屏幕密度(PPI)
iPhone 6+/6s+/7+/8+5.51242 x 22081080 x 1920401

特别注意:在度量 “屏幕上 N 个单位(pt/dp/px)的元素占据多大视觉尺寸?” 的问题上,切勿使用 PPI。因为前者需要点分辨率和横向尺寸,后者采用物理分辨率和斜对角线的尺寸。所以不要因为 PPI 冠以 “每英寸的像素数” 之名,就被它骗了。

Tip:DPI(dot per inch),每英寸多少点,1dpi 表示打印设备上每英寸分布着 1 个打印点。对于屏幕来讲,DPI 就是 PPI,所以本文统一使用 PPI。

4、像素(Pixel,单位 px)

像素(Pixel),画面中最小的点。

2010 年之前,1px 是可测量的物理单位,1 英寸可以容纳 163px(普通显示屏 PPI 为 163)。但自从 iPhone 引入 retina 显示屏,1 英寸可以容纳 326px(retina 显示屏 PPI 为 326,倍率为 2)。

image.png

这时设计师给定的 4px X  2px 的图标,在不同 PPI 屏幕下占据不同视觉尺寸。

image.png

所以不同屏幕,设计师需要切不同图像分辨率的图,比如 2x 和 3x 图(一般来说,移动端开发需要用 3x 图了,因为 2x 图在市面普遍 3x 的设备上会不清晰)。

5、pt(苹果自定义单位)

pt(points),点,是独立像素的意思。Apple 引入 pt 单位,就是解决不同 ppi 设备下视觉尺寸问题。

在 163 ppi(即163 px/inch)屏幕中,1pt = 1px = 1/163 inch ~=0.16 mm。

在 326 ppi 屏幕中,1pt = 2px。开发用系统提供的该单位,上述图标展示问题可以完美解决:

image.png

Tip:注意印刷中的 pt 不同于 iOS 中的 pt,印刷中 1pt = 1/72 英寸,不要混淆了。

6、dp(安卓为设计而创造的独立单位)

dp(density-independent pixel),和 Apple 类似,安卓创造了 dp 作为密度无关像素。

7、rem

rem(font size of the root element),相对于根元素的字体大小的单位。如果前端开发同学设置了根字号 font-size 为 100px,那么在 375px 宽度的屏幕上 0.12rem 就是 12px ,在 320px 宽度的屏幕上是 12*320/375 =10.24px。也就是说以 rem 为单位的元素,px 数会随着屏幕横向点数(也就是逻辑像素)等比变化。

效果如下:

iPhone4(320 X 480)iPhone6(375 X 667)
image.pngimage.png

误区

本文此前部分已经解析了各种概念,但仍然有些许直觉认知会干扰我们的判断,所以增加「误区」这部分,一一解析。

❌ 原生开发中的 1px,等同于前端 H5 页面 CSS 中的 1px

机型官方屏幕尺寸渲染分辨率物理分辨率倍率点分辨率
iPhone 6+/6s+/7+/8+5.51242 x 22081080 x 19203414 x 736

原生开发中的 px 代表渲染分辨率(Rendered Pixels)中的 px。

而前端 H5 页面 CSS 中的 px,则是点分辨率(Points)中的 px。

具体 375px 设计稿上,一个 10px 的设计元素:

原生开发中若用 px 来实现,则需要乘以倍率,假设是 3,则得出 30px。

前端 H5 开发中,CSS 只需要是 10px 即可,无视倍率/PPI 等。

这种方式两边开发出来的元素,在不同机型上的尺寸都是相同的。也就是说:

前端 H5 页面 CSS 中的 1px = 原生开发中的 1px * 倍率

❌ 1dp = (DPI/160) px,也就是 dp / px 随着屏幕 DPI 等比变化?

注意:前文 1dp = 1px 中,px 是 H5 CSS 开发中的 px,而这里的是屏幕实际渲染像素,实际上 = CSS 中的 px * 设备倍率。

Q:1dp = (DPI/160) px,到 320dpi 时,1dp 就相当于两个像素,也就是 dp / px 随着屏幕 DPI 等比变化?

A:这么说是不准确的。具体以 Oppo R9s 为例,官方 DPI(PPI)是 401,但其打印出来密度(屏幕每英寸点密度)是 480,dp/px (倍率) = 480/160 = 3(3 !== 401/160)。

dp / px = 倍率(如上文关于「倍率」的名词解析),而 DPI / 160 很多时候是除不尽的小数。

❌ 1pt = 1/163 英寸?

Q:1pt = 1/163 英寸,也就是 pt 是可度量的物理单位?

A:pt 是抽象的单位,不能用物理单位来衡量。实在要计算不同机型上,1pt 横向代表多少尺寸,结合前文,可知计算方式。

(1)计算手机横向尺寸 xx(单位 inch)。 xx 满足:

x=官方尺寸横向点数/Math.sqrt(横向点数横向点数+纵向点数纵向点数)x = 官方尺寸 * 横向点数 / Math.sqrt(横向点数 * 横向点数 + 纵向点数 * 纵向点数)

(2)计算 1 个点(也就是 1pt)的尺寸 tt(单位 inch),tt 满足:

t=手机横向尺寸数x/横向点数=官方尺寸/Math.sqrt(横向点数∗横向点数+纵向点数∗纵向点数)t = 手机横向尺寸数 x / 横向点数 = 官方尺寸 / Math.sqrt(横向点数∗横向点数+纵向点数∗纵向点数)

部分机型计算结果如下:

image.png

❌ PPI 可以表示手机横向每英寸的点(也就是像素 px)数目?

Q:PPI 是“每英寸的像素数” ,也说明手机横向每英寸的点数?

A:这么说不大准确,但两者间确实有计算关系。从上一个问题中,可以看出:横向每英寸点数 = Math.sqrt(横向点数∗横向点数+纵向点数∗纵向点数) / 官方尺寸 = 斜对角线上的点数 / 官方尺寸。而 PPI = 斜对角线上的物理像素数 / 官方尺寸。

而斜对角线上的点数 = 渲染像素数 / 倍率,物理像素数 = 渲染像素数 / 投到屏幕的缩放因子。

得出:点数 / 物理像素数 = 投到屏幕的缩放因子 / 倍率。

所以 横向每英寸点数 / PPI = 投到屏幕的缩放因子 / 倍率。但一般更精确点不用官方的 PPI 来算,因为 PPI 都给的是整数(虽然英寸也是)。

❌ 字号用 px 方式写,较大手机屏幕不会放大,而 rem 则会放大?

Q:在 iPhone12 和 iPhone (12/13) Pro Max 中对比发现,原生页面17 pt/dp 文字,渲染出来的尺寸一样。可以推断出 H5 字号用 px 方式写,较大屏幕不会放大,而 rem 则会放大。

A:在不同屏幕宽度的手机上,点(pt/dp/px)和 rem 是否会放大,不能根据直接认知,而应该有缜密的计算方式。根据上一个问题(“1pt = 1/163 英寸?”)可知,每 1 个点的尺寸计算方式。

而用 rem 方式来写 1px 的元素,实际实现则会与屏幕宽度等比放大,放大倍数 = 屏幕横向点数 / 375。计算公式如下:

image.png

375px 设计稿标准下,通过 px 和 rem 来实现的区别:

  • px 则根据多个参数计算,屏幕较宽可能不放大(iPhone 12 vs iPhone 12 Pro Max),官方尺寸更大实际效果也可能缩小(小米 6x vs HUAWEI P30)。
  • rem 实现,px 数会随屏幕横向点数/375 等比放大,但实际尺寸不等比(这个点说明,H5 开发同学,在 Chrome 开发者工具上预览效果变大,实际在真实设备上情况会有不同,可能造成设计走查和前端认知的差异。)。

较大手机屏幕上,px 和 rem 是否会放大,并没有一定的规律 部分机型计算结果如下:

image.png

❌ 字号用 px 方式写,设备分辨率越高的手机上,字越小?

Q:用 px 实现,设备分辨率越高的手机上,字越小

A:这里的设备分辨率应该是物理分辨率(Physical Pixels),参考上一个问题,我们知道了每个点横向尺寸的计算方式,加上分辨率可以看到: image.png 「用 px 实现」要考虑,这里是 H5 用 px 还是原生用 px。

H5 用 px 实现,可以看上表中“物理分辨率”和“每个点(1pt/dp/px)横向尺寸(inch)”两列不存在必然关系,可以看出点的尺寸,iPhone 整体随物理分辨率变大。而 Oppo R9s、R11 小米 6x、HUAWEI P30 三款手机横向物理分辨率相同,但点的尺寸却不同。

原生用 px 实现 1px 的尺寸 = H5 用 px 实现 1px 的尺寸 / 倍率,在其他参数一致的情况下,倍率越高的手机,原生 1px 的尺寸就越小。但很多手机其他参数不同,所以同样不能简单地说倍率越大字越小。

❌ 竞品 App 原生页面中,手机越大字越大,所以他们一定是特别做了等比放大的技术方案?

Q:竞品 App 原生页面中,对比了两款手机,发现大的手机,字显得越大,所以他们是特别做了等比放大的技术方案?

A:参考上一个问题可以看出,原生用默认单位(iOS 用 pt,Android 用 dp),在尺寸较大的手机上,默认单位占的尺寸整体较大。当然也有特例,如 小米 6x 比 Oppo R9s / R11 官方尺寸更大,但其横向尺寸更小,默认单位尺寸也更小。所以直觉不一定为实,要用本文的计算公式来看,比较合理。

即使不特别做等比放大技术,原生默认字号单位,也有可能在多款手机上发现 “手机越大字越大”。所以未必能说他们和 H5 rem 一样做了等比放大技术

❌ 用某些单位(如 px),字体展示大小受系统字号影响?

Q:系统字号变大,App 中原生 / H5 页面字体,发现有些会受影响,规律如何?

A:目前在几款 Android+iOS 手机上测,H5 用 px 和 rem 都是不受系统字体影响的(页面开发,所有文字都会设置字号,漏设置的话也会继承到根结点的字号)。原生开发用 dp/pt/px 也是不受影响,只有个别地方漏设置,未指定字号的才会受到系统字号影响。

原生 / H5 开发,指定字号就不受系统字号设置影响,不指定用了系统默认字号,才会受设置的影响

结语

本文从 “App 中 H5 与原生的元素尺寸保持统一” 问题出发,分析技术上如何实现。 进一步对分辨率、倍率、px、rem 等概念进行辨析。

附几种机型的汇总表如下:

image.png

  • 横向尺寸(cm):屏幕尺寸 * 2.54 * 横向点数 / Math.sqrt(横向点数 * 横向点数 + 纵向点数 * 纵向点数)
  • 每个点(1pt/dp/px)横向尺寸(inch):横向尺寸(inch)/ 横向点数。再乘以 2.54 * 10 就是每个点(1pt/dp/px)横向尺寸(mm)。
  • 18个点横向尺寸(mm)- px 实现:每个点(1pt/dp/px)横向尺寸(mm) * 18
  • 18个点横向尺寸(mm)- rem 实现:18个点横向尺寸(mm)- px 实现 * 横向点数是375几倍

从“横向点数是375几倍”这列开始,右侧的所有列都是根据手机参数计算出来的,如果需要增加更多机型,只要拿到其尺寸、渲染分辨率、倍率即可。

文章分类
前端
文章标签