移动开发屏幕适配分析

摘要:
当我开发前端时,我有几个疑问:1)我得到的设计宽度是640px或750px,当页面不同大小的屏幕布局不同时,如何转换它。在分析自适应问题之前,我们应该首先了解一些基本概念,例如视网膜和HI-DPI、DPI和PPI、DP和PT、视口等。例如,在iPhone4/4S中,1个CSS像素=4个物理像素:作为一个简单的示例,通过更改视口手动放大屏幕。然而,移动设备的屏幕比桌面屏幕小得多。为了使网页在小屏幕上正确显示,我们需要在视口上进行一些处理。1) 视觉视口视觉视口是当前显示在屏幕上的页面的一部分。

我在开发前端的时候曾经会有几个疑惑:

1)拿到的设计搞的宽度是640px或750px的,在页面不同尺寸屏幕布局的时候怎么换算。

2)移动端布局是用%、px、rem伸缩盒 Flexible Box Layout还是多列Multi-column

3)用px设置了字体大小,但是在ipad上面显示的却很小,字体大小是用rem比较好还是px+媒体查询比较好。

4)媒体查询@media分几个尺寸的范围能覆盖比较多的尺寸情况。

在分析适配问题之前,要先了解一些基础概念,Retina与HI-DPI、DPI与PPI、DP与PT、viewport等。

一、Retina显示屏

Retina Display是苹果注册的命名方式,其他厂商只能使用HI-DPI或者其他的命名方式,不过意思都是一样的,就是屏幕的PPI非常高。

移动开发屏幕适配分析第1张移动开发屏幕适配分析第2张
iPhone 3GS(非Retina屏幕)iPhone 4(Retina屏幕)

右边的图片明显要比左边的清晰,这是因为PPI要高,何为PPI。

1)PPI与DPI

PPI和DPI这两个是密度单位,不是度量单位。

1. PPI(pixels per inch):图像分辨率 (在图像中,每英寸所包含的像素数目)【1英寸(in)=2.54厘米(cm)】

2. DPI(dots per inch): 打印分辨率 (每英寸所能打印的点数,即打印精度)

PPI的计算方式如下,其中长度像素数和宽度像素数取的是物理像素(Physical Pixel)即设备像素

PPI = √(长度像素数² + 宽度像素数²) / 屏幕对角线英寸数

这个物理像素不是说屏幕的大小,例如iPhone4的屏幕是320*480(这是DP/PT设备独立像素),但物理像素是640*960,屏幕是3.5寸。自己按下计算机算出来≈326。

移动开发屏幕适配分析第3张

知乎上看到,还有一种比较复杂的PPI计算方法,考虑了观察者的距离,下图是一个极简版本:

移动开发屏幕适配分析第4张

上面又出现了几个概念,物理像素、DP、PT又是什么鬼?

2)物理像素(Physical Pixel)

物理像素又被称为设备像素,他是显示设备中一个最微小的物理部件。下图摘自《The Ultimate Guide To iPhone Resolutions》,原文是洋文写的,对应的中文可以看这里

移动开发屏幕适配分析第5张

上图中iPhone 3GS的物理元素就是320*480,而4/4S是640*960。其中4/4S的320*480其实就是PT,而render中的2x就是dpr(设备像素比)

3)DP、PT与SP

DP或PT是测量单位,DP(Density-independent pixels,也会叫DiP)表示独立于设备的像素点,PT(Point)表示点。

DP用在Android上,PT用在Apple上,但是他们本质上是相同的。

SP(Scale-independent pixel)和DP、PT从用途上来讲是不同的,但是工作方式相同。SP表示与比例无关的像素,通常用来定义字体大小。

这三个都是抽象像素,不会因为PPI的变化而变化,在相同物理尺寸和不同PPI下,他们呈现的高度大小是相同。也就是说更接近你手机看上去的大小,而px则不行。

移动开发屏幕适配分析第6张

上图摘自google的《Device metrics》,要浏览google的页面你懂得。表格中Density(密度)的基础单位,在Android上是mdpi,而在IOS中是1x。

4)CSS像素

CSS像素是Web编程的概念,指的是CSS样式代码中使用的逻辑像素。例如300px跟font-size:14px,这类css语句中的px。

CSS像素和物理像素(Physical Pixel)是容纳的关系。

例如iPhone4/4S中,1个CSS像素=4个物理像素(也叫设备像素):

移动开发屏幕适配分析第7张

做个简单的例子,通过改变viewport,来手动放大屏幕。简单设置一个ul的CSS。

ul{
    padding-left:20px;
    font-size: .5rem;
}

1. 页面宽度等于屏幕DP或PT值

移动开发屏幕适配分析第8张

2. 页面宽度等于物理像素(Physical Pixel)

移动开发屏幕适配分析第9张

从两张图片中可以看到,同样是20px,看起来却有大小差别,上图比下图宽了一倍,与第一张图正好对应起来。

5)设备像素比(device pixel ratio)

设备像素比简称为dpr,其定义了物理像素(Physical Pixel)和DP/DiP(设备独立像素)【IOS中是PT(点)】的对应关系。

设备像素比 = 物理像素(Physical Pixel) / 设备独立像素(DP/DiP或PT)

DP/DiP或PT与屏幕密度有关(屏幕密度需通过PPI计算出来)。

在JavaScript中,可以通过 window.devicePixelRatio 获取到当前设备的dpr。

而在CSS中,可以通过 -webkit-device-pixel-ratio , -webkit-min-device-pixel-ratio 和 -webkit-max-device-pixel-ratio 进行媒体查询。

二、viewport

在桌面浏览器中,viewport就是浏览器窗口的宽度高度。

但移动设备的屏幕比桌面屏幕要小得多,为了要让网页在小尺寸的屏幕上显示正确,就需要对viewport做些处理。

需要把viewport分成两部分:visual viewport和layout viewport。

George Cummins在Stack Overflow上对这两个概念做了分析。大致意思如下:

把layout viewport想像成为一张大图。现在用一个比较小的框,通过它来看这张大图。在框内看到的部分就是visual viewport。框中的度量单位是CSS像素。
可以把这个框靠近一些(放大看局部)或靠远一些(缩小看整体)。也可以改变框的方向,但是大图layout viewport的大小和形状永远不会变。

1)visual viewport

visual viewport是页面当前显示在屏幕上的部分。用户可以通过滚动来改变他所看到的页面的部分,或者通过缩放来改变visual viewport的大小。

移动开发屏幕适配分析第10张

结合上面的比喻,再想象一下大图和框的操作。

图片来自于文章《A tale of two viewports》,是用洋文写的,对应的中文可以看这里

2)layout viewport

layout viewport就是页面原来的大小。

移动开发屏幕适配分析第11张

但是我们用在手机用浏览器打开PC的网页的时候,会看到网页被浏览器自动缩小了,变的太小会导致无法浏览内容。

3)viewport meta标签

为了不让浏览器自动缩小,引入了viewport元标签。通过这个元标签控制layout viewport的宽度。

<meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">

上面这段HTML经常会在移动端页面看到。

1. width:设置 layout viewport 的宽。

2. initial-scale:初始缩放比例,也即是当页面第一次 load 的时候缩放比例,上面是变成设备的宽度,也就是layout viewport= DP或PT。

3. maximum-scale:允许用户缩放到的最大比例。

4. minimum-scale:允许用户缩放到的最小比例。

5. user-scalable:用户是否可以手动缩放。

移动开发屏幕适配分析第12张 移动开发屏幕适配分析第13张
浏览器自动缩放添加了viewport

注意下图片中的红色框,第二张图片内容超了出来,应该是给文字设置了宽度,并且这个宽度大于layout viewport导致了出来。但其实在Android和IOS中会有不一样的表现。

在文章《A tale of two viewports》中指出:

1. 通过 document.documentElement.clientWidth 获取 layout viewport 的宽度

2. 通过 window.innerWidth 获取 visual viewport 的宽度

我用iPhone6和红米 note在自己本地做了个获取这两个值的测试,设置了个宽度,撑开页面。

<div style=" 700px">
  //文字内容
</div>

1. 红米中的UC、微信浏览器、自带的浏览器还有移动端的Chrome,显示两个值都一样,是360px,只是UC和微信浏览器不会出现左右滚动条。

2. iPhone6中的UC浏览器和Safari显示两个值不一样,layout viewport 的宽度是375px,而visual viewport 的宽度是708px,不会出现滚动条。

4)ideal viewport

这个viewport的尺寸,我理解相当于DP/DiP(设备独立像素)或PT的尺寸。

在文章《Meta viewport》有详细的说明,如果洋文看起来吃力,可以选择浏览中文版的。

visual viewport宽度 = ideal viewport宽度/ zoom factor

zoom factor其实就是meta中的“initial-scale”。

设置initial-scale后,就开始相对于ideal viewport进行计算产生visual viewport,用visual viewport的宽度值去设置layout viewport的宽度值。

注意,刚刚用iPhone6测试获取到layout viewport与visual viewport不同,一开始应该是相同的,只是后面渲染的时候style=" 700px"把visual viewport给撑大了。

如果你拿到的设计稿是640px宽度的,在你用iPhone5布局的时候,其实你可以initial-scale=0.5,将layout viewport放大到640px,接下布局可以通过rem来设置。

但是手机屏幕的尺寸各不相同,那么initial-scale的值也应该是不同的,这个值是可以通过JavaScript计算出来的,可以参考flexible.js(移动自适应方案)中的代码。

三、flexible.js

打开手机淘宝页面,可以看到里面内联了这个库的代码,这个flexible.js库就是用来解决H5页面终端适配的,具体的使用方法可以参考这里

1)rem

rem 就是相对于根元素 <html> 的 font-size 来做计算。flexible.js会给html赋font-size的值。下面的代码是在iPhone5中模拟。

移动开发屏幕适配分析第14张

使用方法中并不推荐字体用rem单位,但我觉得可以因地制宜,具体情况具体分析,比如要在iPad中适配,这个时候用rem体验会比较好。

2)计算initial-scale

注意下面的Android设备一律dpr=1,我后面做了个简单的测试。

 1 var devicePixelRatio = win.devicePixelRatio;
 2 if (isIPhone) {
 3   // iOS下,对于2和3的屏,用2倍的方案,其余的用1倍方案
 4   if (devicePixelRatio >= 3 && (!dpr || dpr >= 3)) {
 5     dpr = 3;
 6   } else if (devicePixelRatio >= 2 && (!dpr || dpr >= 2)) {
 7     dpr = 2;
 8   } else {
 9     dpr = 1;
10   }
11 } else {
12   // 其他设备下,仍旧使用1倍的方案
13   dpr = 1;
14 }
15 scale = 1 / dpr;

将第2行修改下代码,通过正则匹配来判断dpr的值。

var isRegularDpr = devicePixelRatio.toString().match(/^[1-9]d*$/g);
if (isRegularDpr) {
 //...逻辑与上面相同   
}

在IOS中表现正常,可是在Android中的UC、Chrome浏览器中会放大,微信浏览器显示正常。顺便说下,我的手机是红米note,物理像素是720px,DP(设备独立像素)是360px。

移动开发屏幕适配分析第15张移动开发屏幕适配分析第16张移动开发屏幕适配分析第17张
修改前修改后 添加target-densitydpi=device-dpi 
CSS像素是360pxCSS像素是720pxCSS像素是720px

第二和第三都是720px,宽度都变大了,只是一个是通过“initial-scale=0.5”,另一个是通过“target-densitydpi=device-dpi”。但字体一张变大一张变小了。

造成两种展现不一样的原因,我的理解是,第一种1个CSS像素=4个物理像素,而第二种是1个CSS像素=1个物理像素。

Android的viewport中属性“target-densitydpi”,这个“DPI”就相当于“PPI”,我的红米note的“PPI”是267。

可以的取值如下:

device-dpi: 使用设备原本的 dpi 作为目标 dp。 不会发生默认缩放。
high-dpi: 使用hdpi 作为目标 dpi。 中等像素密度和低像素密度设备相应缩小。
medium-dpi: 使用mdpi作为目标 dpi。 高像素密度设备相应放大, 像素密度设备相应缩小。 这是默认的target density.
low-dpi: 使用mdpi作为目标 dpi。中等像素密度和高像素密度设备相应放大。
<value>: 指定一个具体的dpi 值作为target dpi. 这个值的范围必须在70–400之间。

有一篇文章《Viewport target-densitydpi support》曾经讲到,这个属性将被废弃。

3)viewport元标签

如果在页面中已经设置了元标签,那么就读取元标签中的内容,如果没有设置,就动态添加meta标签到head中。

4)refreshRem函数

此函数就是在设置html中font-size,以及flexible.rem与win.rem的值,flexible.rem会在后面的两个辅助方法refreshRem和px2rem内用到。

docEl.getBoundingClientRect().width是为了获得 visual viewport 的宽度。

这个540其实是个经验值,参考Issues#12,目前主流手机最大的css像素尺寸,是540(比如devicePixelRatio为2,分辨率是1080x1920的手机)。

width / dpr 是因为前面添加了viewport后,width会根据dpr(设备像素比)改变,而540定义的是一个DP/DiP(设备独立像素)或PT值。

width / 10 主要为了以后能更好的兼容 vh 和 vw。

function refreshRem() {
  var width = docEl.getBoundingClientRect().width;
  if (width / dpr > 540) {
    width = 540 * dpr;
  }
  var rem = width / 10;
  docEl.style.fontSize = rem + 'px';
  flexible.rem = win.rem = rem;
}

5)pageshow与DOMContentLoaded

pageshow事件类似于 onload 事件,onload 事件在页面第一次加载时触发, onpageshow 事件在每次加载页面时触发,即 onload 事件在页面从浏览器缓存中读取时不触发。

win.addEventListener('pageshow', function(e) {
  if (e.persisted) {
    clearTimeout(tid);
    tid = setTimeout(refreshRem, 300);
  }
}, false);

页面文档完全加载并解析完毕之后,会触发DOMContentLoaded事件,HTML文档不会等待样式文件,图片文件,子框架页面的加载。

jQuery中的ready方法就是基于这个事件编写的,查看ready源码

现在我自己来解答一下自己的疑惑:

1)通过修改initial-scale的值,然后配合rem来做换算,可以参考flexible.js中计算方式。

2)这几种布局应该是配合使用,因地制宜,我前面的一篇文章中就记录使用了多列与伸缩盒的使用场景。

3)还是一样,要结合媒体查询与rem或px,因地制宜。

4)这个范围,目前我还不能确定,还没有做过详细的实验。

免责声明:文章转载自《移动开发屏幕适配分析》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇asterisk AGI编程收藏http2协议翻译(转)下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

验证码识别,发票编号识别

update:排版 这个demo的初衷不是去识别验证码,是把验证的图像处理方式用到其他方面,车票,票据等。 这里最后做了一个发票编号识别的的案例: 地址:http://v.youku.com/v_show/id_XMTI1MzUxNDY3Ng==.html 源代码:https://github.com/ccccccmd/ReCapcha demo中包含一个...

Unity面试题汇总(第一部分)

一、什么是渲染管道? 答:就是告诉GPU一些数据,经过一系列的操作,得到最终要显示的数据。渲染管道中的很多步骤,总的来说是将几何物体从一个坐标系变换到另一个坐标系中去。 主要步骤如下: 本地坐标系 -- 经过世界变换矩阵 --> 世界坐标系 -- 经过视图变换矩阵 --> 视图坐标系 -- 经过投影变换矩阵 --> 投影坐标系 -- 经过...

像素 分辨率 和 点的关系(终极解决)

像素是组成图象的最基本单元要素:点。 分辨率是指在长和宽的两个方向上各拥有的像素个数。 一个像素有多大呢?主要取决于显示器的分辨率,相同面积不同分辨率的显示屏,其像素点大小就不相同。 大家都知道线是由无数个点组成的,而面是由无数条线组成,即一个平面是由无数个点所组成。但无论技术多先进发达,人类总是不可能做到一幅图象由无数个点来构成的境界,只能在长和宽的方向...

Android屏幕适配总结

一、首先需要明白的几个概念  1、屏幕尺寸:也就是我们平常所说的某某手机几寸屏。比如苹果的4.7寸, 荣耀6的5.5寸。这里说的寸是英寸(1 英寸 = 2.54 厘米)。       计算方法:屏幕尺寸=对角先尺寸(cm) / 2.54(cm)              一般用英寸表示。1英寸=2.54cm           2、分辨率:屏幕上显示的像...

libflexible源码阅读

前言 最近需要一款移动端的产品,当时需要赶工期,在参考了天猫的flexbox布局和手淘的rem布局方案后,决定选用libflexible。做完项目之后,稍有空闲时间,决定看看libflexible如何实现动态设置根元素的字体,从而通过rem的方式改变其他元素大小 正文 首先我们看一下flexible需要哪些属性 ;(function(win, lib) {...

Halcon斑点分析BlobAnalysis解析

斑点分析的算法非常简单:在图像中,相关对象的像素(也称为前景)通过其灰度值来识别。例如,图中示例显示了液体中的组织颗粒。这些粒子是明亮的,液体(背景)是暗的。通过选择明亮的像素(阈值),可以很容易检测到颗粒。在许多应用中,暗像素和亮像素的简单条件不再成立,但结果相同可以通过额外的预处理或像素选择/分组的替代方法来实现。 在这种情况下,斑点分析的优点是HAL...