移动端适配总结(一)原理篇

导读

移动端适配,是我们在开发中经常会遇到的,这里面可能会遇到非常多的问题:

  • 1px问题
  • UI图完美适配方案
  • iPhoneX适配方案
  • 横屏适配
  • 高清屏图片模糊问题
  • ...

上面这些问题可能我们在开发中已经知道如何解决,但是问题产生的原理,以及解决方案的原理可能会模糊不清。在解决这些问题的过程中,我们往往会遇到非常多的概念:像素、分辨率、PPIDPIDPDIPDPR、视口等等,你真的能分清这些概念的意义吗?

本文将从移动端适配的基础概念出发,探究移动端适配各种问题的解决方案和实现原理。

一、英寸

一般用英寸描述屏幕的物理大小,如电脑显示器的1722,手机显示器的4.85.7等使用的单位都是英寸。

需要注意,上面的尺寸都是屏幕对角线的长度:

英寸(inch,缩写为in)在荷兰语中的本意是大拇指,一英寸就是指甲底部普通人拇指的宽度。

英寸和厘米的换算:1英寸 = 2.54 厘米

二、分辨率

2.1 像素

像素即一个小方块,它具有特定的位置和颜色。

图片、电子屏幕(手机、电脑)就是由无数个具有特定颜色和特定位置的小方块拼接而成。

像素可以作为图片或电子屏幕的最小组成单位。

下面我们使用sketch打开一张图片:

将这些图片放大即可看到这些像素点:

通常我们所说的分辨率有两种,屏幕分辨率和图像分辨率。

2.2 屏幕分辨率

屏幕分辨率指一个屏幕具体由多少个像素点组成。

下面是apple的官网上对手机分辨率的描述:

iPhone XS MaxiPhone SE的分辨率分别为2688 x 12421136 x 640。这表示手机分别在垂直和水平上所具有的像素点数。

当然分辨率高不代表屏幕就清晰,屏幕的清晰程度还与尺寸有关。

2.3 图像分辨率

我们通常说的图片分辨率其实是指图片含有的像素数,比如一张图片的分辨率为800 x 400。这表示图片分别在垂直和水平上所具有的像素点数为800400

同一尺寸的图片,分辨率越高,图片越清晰。

2.4 PPI

PPI(Pixel Per Inch):每英寸包括的像素数。

PPI可以用于描述屏幕的清晰度以及一张图片的质量。

使用PPI描述图片时,PPI越高,图片质量越高,使用PPI描述屏幕时,PPI越高,屏幕越清晰。

在上面描述手机分辨率的图片中,我们可以看到:iPhone XS MaxiPhone SEPPI分别为458326,这足以证明前者的屏幕更清晰。

由于手机尺寸为手机对角线的长度,我们通常使用如下的方法计算PPI:

\frac{\sqrt{水平像素点数^2+垂直像素点数^2}}{尺寸}

iPhone 6PPI\frac{\sqrt{1334^2+750^2}}{4.7}=325.6,那它每英寸约含有326个物理像素点。

2.5 DPI

DPI(Dot Per Inch):即每英寸包括的点数。

这里的点是一个抽象的单位,它可以是屏幕像素点、图片像素点也可以是打印机的墨点。

平时你可能会看到使用DPI来描述图片和屏幕,这时的DPI应该和PPI是等价的,DPI最常用的是用于描述打印机,表示打印机每英寸可以打印的点数。

一张图片在屏幕上显示时,它的像素点数是规则排列的,每个像素点都有特定的位置和颜色。

当使用打印机进行打印时,打印机可能不会规则的将这些点打印出来,而是使用一个个打印点来呈现这张图像,这些打印点之间会有一定的空隙,这就是DPI所描述的:打印点的密度。

在上面的图像中我们可以清晰的看到,打印机是如何使用墨点来打印一张图像。

所以,打印机的DPI越高,打印图像的精细程度就越高,同时这也会消耗更多的墨点和时间。

三、设备独立像素

实际上,上面我们描述的像素都是物理像素,即设备上真实的物理单元。

下面我们来看看设备独立像素究竟是如何产生的:

智能手机发展非常之快,在几年之前,我们还用着分辨率非常低的手机,比如下面左侧的白色手机,它的分辨率是320x480,我们可以在上面浏览正常的文字、图片等等。

但是,随着科技的发展,低分辨率的手机已经不能满足我们的需求了。很快,更高分辨率的屏幕诞生了,比如下面的黑色手机,它的分辨率是640x940,正好是白色手机的两倍。

理论上来讲,在白色手机上相同大小的图片和文字,在黑色手机上会被缩放一倍,因为它的分辨率提高了一倍。这样,岂不是后面出现更高分辨率的手机,页面元素会变得越来越小吗?

然而,事实并不是这样的,我们现在使用的智能手机,不管分辨率多高,他们所展示的界面比例都是基本类似的。乔布斯在iPhone4的发布会上首次提出了Retina Display(视网膜屏幕)的概念,它正是解决了上面的问题,这也使它成为一款跨时代的手机。

iPhone4使用的视网膜屏幕中,把2x2个像素当1个像素使用,这样让屏幕看起来更精致,但是元素的大小却不会改变。

如果黑色手机使用了视网膜屏幕的技术,那么显示结果应该是下面的情况,比如列表的宽度为300个像素,那么在一条水平线上,白色手机会用300个物理像素去渲染它,而黑色手机实际上会用600个物理像素去渲染它。

我们必须用一种单位来同时告诉不同分辨率的手机,它们在界面上显示元素的大小是多少,这个单位就是设备独立像素(Device Independent Pixels)简称DIPDP。上面我们说,列表的宽度为300个像素,实际上我们可以说:列表的宽度为300个设备独立像素。

打开chrome的开发者工具,我们可以模拟各个手机型号的显示情况,每种型号上面会显示一个尺寸,比如iPhone X显示的尺寸是375x812,实际iPhone X的分辨率会比这高很多,这里显示的就是设备独立像素。

3.1 设备像素比

设备像素比device pixel ratio简称dpr,即物理像素和设备独立像素的比值。

web中,浏览器为我们提供了window.devicePixelRatio来帮助我们获取dpr

css中,可以使用媒体查询min-device-pixel-ratio,区分dpr

@media (-webkit-min-device-pixel-ratio: 2), (min-device-pixel-ratio: 2) {
}

React Native中,我们也可以使用PixelRatio.get()来获取DPR

当然,上面的规则也有例外,iPhone 6、7、8 Plus的实际物理像素是1080 x 1920,在开发者工具中我们可以看到:它的设备独立像素是414 x 736,设备像素比为3,设备独立像素和设备像素比的乘积并不等于1080 x 1920,而是等于1242 x 2208

实际上,手机会自动把1242 x 2208个像素点塞进1080 * 1920个物理像素点来渲染,我们不用关心这个过程,而1242 x 2208被称为屏幕的设计像素。我们开发过程中也是以这个设计像素为准。

实际上,从苹果提出视网膜屏幕开始,才出现设备像素比这个概念,因为在这之前,移动设备都是直接使用物理像素来进行展示。

紧接着,Android同样使用了其他的技术方案来实现DPR大于1的屏幕,不过原理是类似的。由于Android屏幕尺寸非常多、分辨率高低跨度非常大,不像苹果只有它自己的几款固定设备、尺寸。所以,为了保证各种设备的显示效果,Android按照设备的像素密度将设备分成了几个区间:

当然,所有的Android设备不一定严格按照上面的分辨率,每个类型可能对应几种不同分辨率,所以,每个Android手机都能根据给定的区间范围,确定自己的DPR,从而拥有类似的显示。当然,仅仅是类似,由于各个设备的尺寸、分辨率上的差异,设备独立像素也不会完全相等,所以各种Android设备仍然不能做到在展示上完全相等。

3.2 移动端开发

iOSAndroidReact Native开发中样式单位其实都使用的是设备独立像素。

iOS的尺寸单位为ptAndroid的尺寸单位为dpReact Native中没有指定明确的单位,它们其实都是设备独立像素dp

在使用React Native开发App时,UI给我们的原型图一般是基于iphone6的像素给定的。

为了适配所有机型,我们在写样式时需要把物理像素转换为设备独立像素:例如:如果给定一个元素的高度为200px(这里的px指物理像素,非CSS像素),iphone6的设备像素比为2,我们给定的height应为200px/2=100dp

当然,最好的是,你可以和设计沟通好,所有的UI图都按照设备独立像素来出。

我们还可以在代码(React Native)中进行pxdp的转换:

import { PixelRatio } from "react-native";

const dpr = PixelRatio.get();

/**
 * px转换为dp
 */
export function pxConvertTodp(px) {
  return px / dpr;
}

/**
 * dp转换为px
 */
export function dpConvertTopx(dp) {
  return PixelRatio.getPixelSizeForLayoutSize(dp);
}

3.3 WEB 端开发

在写CSS时,我们用到最多的单位是px,即CSS像素,当页面缩放比例为100%时,一个CSS像素等于一个设备独立像素。

但是CSS像素是很容易被改变的,当用户对浏览器进行了放大,CSS像素会被放大,这时一个CSS像素会跨越更多的物理像素。

页面的缩放系数 = CSS像素 / 设备独立像素

3.4 关于屏幕

这里多说两句Retina屏幕,因为我在很多文章中看到对Retina屏幕的误解。

Retina屏幕只是苹果提出的一个营销术语:

在普通的使用距离下,人的肉眼无法分辨单个的像素点。

为什么强调普通的使用距离下呢?我们来看一下它的计算公式:

a=2arctan(h/2d)

a代表人眼视角,h代表像素间距,d代表肉眼与屏幕的距离,符合以上条件的屏幕可以使肉眼看不见单个物理像素点。

它不能单纯的表达分辨率和PPI,只能一种表达视觉效果。

让多个物理像素渲染一个独立像素只是Retina屏幕为了达到效果而使用的一种技术。而不是所有DPR > 1的屏幕就是Retina屏幕。

比如:给你一块超大尺寸的屏幕,即使它的PPI很高,DPR也很高,在近距离你也能看清它的像素点,这就不算Retina屏幕。

我们经常见到用KP这个单位来形容屏幕:

P代表的就是屏幕纵向的像素个数,1080P即纵向有1080个像素,分辨率为1920X1080的屏幕就属于1080P屏幕。

我们平时所说的高清屏其实就是屏幕的物理分辨率达到或超过1920X1080的屏幕。

K代表屏幕横向有几个1024个像素,一般来讲横向像素超过2048就属于2K屏,横向像素超过4096就属于4K屏。

四、视口

视口(viewport)代表当前可见的计算机图形区域。在Web浏览器术语中,通常与浏览器窗口相同,但不包括浏览器的UI, 菜单栏等——即指你正在浏览的文档的那一部分。

一般我们所说的视口共包括三种:布局视口、视觉视口和理想视口,它们在屏幕适配中起着非常重要的作用。

4.1 布局视口

布局视口(layout viewport):当我们以百分比来指定一个元素的大小时,它的计算值是由这个元素的包含块计算而来的。当这个元素是最顶级的元素时,它就是基于布局视口来计算的。

所以,布局视口是网页布局的基准窗口,在PC浏览器上,布局视口就等于当前浏览器的窗口大小(不包括bordersmargins、滚动条)。

在移动端,布局视口被赋予一个默认值,大部分为980px,这保证PC的网页可以在手机浏览器上呈现,但是非常小,用户可以手动对网页进行放大。

我们可以通过调用document.documentElement.clientWidth / clientHeight来获取布局视口大小。

4.2 视觉视口

视觉视口(visual viewport):用户通过屏幕真实看到的区域。

视觉视口默认等于当前浏览器的窗口大小(包括滚动条宽度)。

当用户对浏览器进行缩放时,不会改变布局视口的大小,所以页面布局是不变的,但是缩放会改变视觉视口的大小。

例如:用户将浏览器窗口放大了200%,这时浏览器窗口中的CSS像素会随着视觉视口的放大而放大,这时一个CSS像素会跨越更多的物理像素。

所以,布局视口会限制你的CSS布局而视觉视口决定用户具体能看到什么。

我们可以通过调用window.innerWidth / innerHeight来获取视觉视口大小。

4.3 理想视口

布局视口在移动端展示的效果并不是一个理想的效果,所以理想视口(ideal viewport)就诞生了:网站页面在移动端展示的理想大小。

如上图,我们在描述设备独立像素时曾使用过这张图,在浏览器调试移动端时页面上给定的像素大小就是理想视口大小,它的单位正是设备独立像素。

上面在介绍CSS像素时曾经提到页面的缩放系数 = CSS像素 / 设备独立像素,实际上说页面的缩放系数 = 理想视口宽度 / 视觉视口宽度更为准确。

所以,当页面缩放比例为100%时,CSS像素 = 设备独立像素理想视口 = 视觉视口

我们可以通过调用screen.width / height来获取理想视口大小。

4.4 Meta viewport

<meta> 元素表示那些不能由其它HTML元相关元素之一表示的任何元数据信息,它可以告诉浏览器如何解析页面。

我们可以借助<meta>元素的viewport来帮助我们设置视口、缩放等,从而让移动端得到更好的展示效果。

<meta
  name="viewport"
  content="width=device-width; initial-scale=1; maximum-scale=1; minimum-scale=1; user-scalable=no;"
/>

上面是viewport的一个配置,我们来看看它们的具体含义:

Value 可能值 描述
width 正整数或device-width pixels(像素)为单位, 定义布局视口的宽度。
height 正整数或device-height pixels(像素)为单位, 定义布局视口的高度。
initial-scale 0.0 - 10.0 定义页面初始缩放比率。
minimum-scale 0.0 - 10.0 定义缩放的最小值;必须小于或等于maximum-scale的值。
maximum-scale 0.0 - 10.0 定义缩放的最大值;必须大于或等于minimum-scale的值。
user-scalable 一个布尔值(yes或者no 如果设置为 no,用户将不能放大或缩小网页。默认值为 yes。

4.5 移动端适配

为了在移动端让页面获得更好的显示效果,我们必须让布局视口、视觉视口都尽可能等于理想视口。

device-width就等于理想视口的宽度,所以设置width=device-width就相当于让布局视口等于理想视口。

由于initial-scale = 理想视口宽度 / 视觉视口宽度,所以我们设置initial-scale=1;就相当于让视觉视口等于理想视口。

这时,1 个CSS像素就等于 1 个设备独立像素,而且我们也是基于理想视口来进行布局的,所以呈现出来的页面布局在各种设备上都能大致相似。

4.6 缩放

上面提到width可以决定布局视口的宽度,实际上它并不是布局视口的唯一决定性因素,设置initial-scale也有肯能影响到布局视口,因为布局视口宽度取的是width和视觉视口宽度的最大值。

例如:若手机的理想视口宽度为400px,设置width=device-widthinitial-scale=2,此时视觉视口宽度 = 理想视口宽度 / initial-scale200px,布局视口取两者最大值即device-width 400px

若设置width=device-widthinitial-scale=0.5,此时视觉视口宽度 = 理想视口宽度 / initial-scale800px,布局视口取两者最大值即800px

4.7 获取浏览器大小

浏览器为我们提供的获取窗口大小的API有很多,下面我们再来对比一下:

  • window.innerHeight:获取浏览器视觉视口高度(包括垂直滚动条)。
  • window.outerHeight:获取浏览器窗口外部的高度。表示整个浏览器窗口的高度,包括侧边栏、窗口镶边和调正窗口大小的边框。
  • window.screen.Height:获取获屏幕取理想视口高度,这个数值是固定的,设备的分辨率/设备像素比
  • window.screen.availHeight:浏览器窗口可用的高度。
  • document.documentElement.clientHeight:获取浏览器布局视口高度,包括内边距,但不包括垂直滚动条、边框和外边距。
  • document.documentElement.offsetHeight:包括内边距、滚动条、边框和外边距。
  • document.documentElement.scrollHeight:在不使用滚动条的情况下适合视口中的所有内容所需的最小宽度。测量方式与clientHeight相同:它包含元素的内边距,但不包括边框,外边距或垂直滚动条。

欢迎大家到公众号: you的日常 阅读,体验更好哦。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342