[toc]
# 移动适配方案(一)
要搞懂移动端的适配问题,就要先搞明白像素和视口。
## 像素
### 什么是像素?
像素是组成图象的最基本单元要素:点。
分辨率是指在长和宽的两个方向上各拥有的像素个数。一个像素有多大呢?主要取决于显示器的分辨率,相同面积不同分辨率的显示屏,其像素点大小就不相同。
**大家都知道线是由无数个点组成的,而面是由无数条线组成,即一个平面是由无数个点所组成。但无论技术多先进发达,人类总是不可能做到一幅图象由无数个点来构成的境界,只能在长和宽的方向上由有限个点组成而已。**
这些有限的点就叫做像素,每一个长度方向上的像素个数乖每一个宽度方向上的像素个数的形式表示,就叫做图片的分辨率。
如一张640X480的图片,表示这张图片在每一个长度的方向上都有640个像素点,而每一个宽度方向上都480个像素点,总数就是640X480=307200(个像素),**简称30万像素**。
在移动端给一个元素设置 `width:200px` 时发生了什么?这里的`px`到底是多长呢?像素是网页布局的基础,但是我们一直在用直觉使用它。
其实存在两种像素:
**1. 设备像素**
屏幕的物理像素,任何设备屏幕的物理像素的数量都是固定不变的,单位是`pt`。
**2. CSS像素**
在CSS、JS中使用的一个抽象的概念,单位是 `px`。
**然而,px是什么**
**像素是没有标准“大小”概念的单位(不像厘米、英寸、斤等单位)**
cm、pt之类的都是绝对长度,它们是物理长度——1cm是1/100米,而1米则大约是光在1秒钟内跑过距离的3亿分之一。(至于光速和1秒的精确值到底是多少,请查阅维基百科光速条目和秒条目。)1pt则是1/72英寸,而1英寸换算到公制是2.54cm。
而em、ex,以及百分比,则是相对长度。比如em相对于当前字体大小,百分比则根据属性定义不同有不同涵义,例如margin的值如果是百分比,表示是相对于父元素的width。
一般而言,pixel(像素)是图像的基本采样单位。**在前端开发中,它是 Web 编程的概念,本质上是为我们 Web 开发者创建的一个抽象概念,是相对的而不是绝对的。所以它不是一个确定的物理量,也不是一个具体的点或者小方块(尽管可以用点和小方块来呈现)。**
> 顺便说下,CSS像素也可以称为**设备独立像素(也可以称为设备无关的逻辑像素)**(device-independent pixels),简称为dips,单位是dp。
meta里面设置`width=device-width`,这个device-width就是设备独立像素。
![](https://box.kancloud.cn/eed8c1dcad203b9c54099d873636109b_341x37.png)
在chrome里 看到的`375*667 320*480`等等都是设备独立像素,它们在数值上与css数值是相等的,即我们平时经常写的`width:100px;height:100px;`都是与设备无关的。
**值得注意的是:** 不管你手机是否切换到横屏,这两个值还是一样的。所以不管是移动端还是PC端通过`screen.width/height`获取的这个值是设备独立像素(CSS 像素),而不是设备的屏幕分辨率,因为设备的屏幕分辨率对于WEB开发者来说是无法通过代码来获得的,是完全透明的。
那么,我们现在再来说说一个元素 `width:200px` 以后会怎么样。这个元素跨越了200个CSS元素,200个CSS元素相当于多少个设备像素取决于两个条件:
* 页面是否缩放
* 屏幕是否为高密度
这两方面后面再解释,先梳理一下手机硬件之间的关系,注意这里使用的都是物理像素。
*以 iPhone5 为例,我们已知的是:*
**分辨率**:1136pt x 640pt
泛指量测或显示系统对细节的分辨能力。以PC屏幕,手机屏幕为例,分辨率1920*1080 是指屏幕纵向能显示1136个物理像素,横向能显示640 个物理像素
**屏幕尺寸**: 4英寸
注意英寸是长度单位,不是面积单位。4英寸指的是屏幕对角线的长度。
**屏幕像素密度**:326dpi
屏幕像素密度(Pibel Per Inch)简称 ppi ,单位是 dpi(dot per inch)。这里指屏幕水平或垂直每英寸有326个物理像素。原则上来说,ppi越高越好,因为图像会更加细腻清晰。
ppi 是可以通过 分辨率 和 屏幕尺寸 计算得到的:
![ppi](https://box.kancloud.cn/7c417c726186fd85bf26f7c97f44f41f_300x83.png)
[这个网站](https://www.sven.de/dpi/)列出了很多设备的分辨率和屏幕尺寸,并且计算了ppi。
## 视口(viewport)
桌面浏览器中,浏览器窗口就是约束你的CSS布局视口(又称初始包含块)。它是所有CSS百分比宽度推算的根源,它的作用是给CSS布局限制了一个最大宽度,视口的宽度和浏览器窗口宽度一致。
但是在移动端,情况就很复杂了。
### 如何度量viewport
`document.documentElement.clientWidth/clientHeight`可以度量viewport的尺寸。
这里需要说明一下`window.innerWidth`和`window.innerHeight`的区别:`document.documentElement.clientWidth`计算的宽度不算滚动条的宽度,而`window.innerWidth`的宽度将滚动条计算在内。
### 如何度量html元素
```js
document.documentElement.offsetWidth/offsetHeight //可以获得html元素的
```
### 事件中的坐标
当一个鼠标事件发生时,event事件中提供了坐标的相关信息
`pageX/Y`提供了相对于html元素的以css像素度量的坐标
`clientX/Y`提供了相对于viewport的以css相对度量的坐标。
`screenX/Y`提供了相对于屏幕的以设备像素进行度量的坐标。
## 利用meta标签对viewport进行控制
移动设备默认的viewport是layout viewport,但在进行移动设备网站的开发时,需要的是ideal viewport。要得到ideal viewport,需要用到meta标签。
head标签中加入:
```html
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
```
该meta标签的作用是让当前viewport的宽度等于设备的宽度,同时不允许用户手动缩放。当然maximum-scale=1.0, user-scalable=0不是必需的,是否允许用户手动播放根据网站的需求来定,但把width设为width-device基本是必须的,这样能保证不会出现横向滚动条。
meta viewport 的6个属性:
* width 设置**layout viewport** 的宽度,为一个正整数,或字符串"width-device"
* initial-scale 设置页面的初始缩放值,为一个数字,可以带小数
* minimum-scale 允许用户的最小缩放值,为一个数字,可以带小数
* maximum-scale 允许用户的最大缩放值,为一个数字,可以带小数
* height 设置layout viewport 的高度,这个属性并不重要,很少使用
* user-scalable 是否允许用户进行缩放,值为"no"或"yes", no 代表不允许,yes代表允许
width能控制layout viewport的宽度,如果不指定该属性,layout viewport将默认为980px或1024px(也可能是其它值,由设备本身决定),如果把layout viewport的宽度设置为移动设备的宽度,那么layout viewport将成为ideal viewport。
其实,**要把当前的viewport宽度设为ideal viewport的宽度,可以设置width=device-width,或者设置initial-scale=1**,但有一个小缺陷,就是width=device-width会导致iphone、ipad横竖屏不分,initial-scale=1会导致IE横竖屏不分,都以竖屏的ideal viewport宽度为准。
## 布局视口(layout viewport)
ppk把这个浏览器默认的viewport叫做 layout viewport。
一个没有为移动端做优化(没有设置viewport等)的网页,例如[PPK](https://quirksmode.org/)等,会尽可能缩小网页让用户看到所有东西。这是我的手机查看博客园的样子(...现在博客园已经做了移动端优化),你也可以在Chrome中以移动开发模式看到。
![博客园截图](https://box.kancloud.cn/2e1fa69efae74896daab56c610256052_300x534.png)
浏览器厂商为了让用户在小屏幕下网页也能够显示地很好,所以把视口宽度设置地很大,一般在 `768px ~ 1024px` 之间,最常见的宽度是 `980px`。
所以,在手机上,视口与移动端浏览器,屏幕宽度不再相关联,是完全独立的,这个浏览器厂商定的视口被称为**布局视口**。
![布局视口](https://box.kancloud.cn/03ab6aae99ba434a9c4dcf29a68d0162_501x382.png)
布局视口我们是看不见的,只知道网页的最大宽度是 `980px` ,并且被缩放在了屏幕内。
可以这样设置布局视口的宽度:
```
<meta name="viewport" content="width=640">
```
**媒体查询与布局视口**
700px 指的是布局视口的宽度
```css
@media (min-width: 700px){
...
}
```
**获取布局视口的尺寸**:
```js
document.documentElement.clientWidth/Height //返回布局视口的尺寸
```
## 视觉视口(visual viewport)
![](https://box.kancloud.cn/ccffe4a91a819fb7b177786e61c84b64_504x381.png)
视觉视口是用户正在看到的网页的区域,大小是屏幕中CSS像素的数量。
**获取视觉视口的尺寸**:
```js
window.innerWidth/Height //返回视觉视口的尺寸
```
## 理想视口(ideal viewport)
布局视口明显对用户是不友好的,完全忽略了手机本身的尺寸。所以苹果引入了理想视口的概念,它是**对设备来说最理想的布局视口尺寸**。理想视口中的网页用户最理想的宽度,用户进入页面的时候不需要缩放。
现在讨论所谓的『最理想的宽度』到底是多少?其实,如果我们把布局视口的宽度改成屏幕的宽度不就不用缩放了么,**通常是我们说的屏幕分辨率**。可以这样设置告诉浏览器使用它的理想视口:
```html
<meta name="viewport" content="width=device-width">
```
定义理想视口是浏览器的事情,并不能简单地认为是开发者定义的,开发者只能使用。
**获取理想视口的尺寸**:
```js
window.screen.width/height //返回视觉视口的尺寸
/*
有严重的兼容性问题(*在某些安卓原生浏览器或webview中会出现视觉视口小于布局视口的情况。直观的显示就是页面会出现左右滑动。*)
---可能返回两种值:
理想视口的尺寸(下载的浏览器)
屏幕的设备像素尺寸(内置浏览器)
*/
```
关于不同的设备ideal viewport的宽度都为多少,可以到去查看一下,里面收集了众多设备的理想宽度。
http://viewportsizes.com
[screen sizes ](http://screensiz.es/phone)收集了很多手机的信息。
[Screen size tests](http://quirksmode.org/m/tests/widthtest.html)和[Understanding viewport](http://andreasbovens.github.io/understanding-viewport/)可以测试你的设备的`screen.width`值,同一设备的不同浏览器返回的值可能是不一样的。这一情况主要发生在默认浏览器和下载的浏览器(如UC、Chrome)之间。
默认浏览器是安卓系统内置的浏览器,长下面那个样子。而且它使用的是Webkit而不是Blink。只有在更新安卓系统的时候才能更新它。直到安卓4.3,Google不再更新。
![](https://box.kancloud.cn/9835ba69dd7504a1585cd25d619279ce_960x244.png)
而下载的浏览器都返回的是理想视口尺寸。
`ideal viewport` 的意义在于,无论在何种分辨率的屏幕下,那些针对ideal viewport 而设计的网站,不需要用户手动缩放,也不需要出现横向滚动条,都可以完美的呈现给用户。
## ppk大神看viewport
[ppk大神](http://www.quirksmode.org/)对于移动设备上的viewport有着非常多的研究([第一篇](http://www.quirksmode.org/mobile/viewports.html),[第二篇](http://www.quirksmode.org/mobile/viewports2.html),[第三篇](http://www.quirksmode.org/mobile/metaviewport/))。
首先,移动设备上的浏览器**认为自己必须能让所有的网站都正常显示,即使是那些不是为移动设备设计的网站**。但如果以浏览器的可视区域作为viewport的话,因为移动设备的屏幕都不是很宽,所以那些为桌面浏览器设计的网站放到移动设备上显示时,必然会因为移动设备的viewport太窄,而挤作一团,甚至布局什么的都会乱掉。
也许有人会问,现在不是有很多手机分辨率都非常大吗,比如`768x1024`,或者`1080x1920`这样,那这样的手机用来显示为桌面浏览器设计的网站是没问题的吧?
前面我们已经说了,css中的`1px`并不是代表屏幕上的`1px`,你分辨率越大,css中`1px`代表的物理像素就会越多,`devicePixelRatio`的值也越大,这很好理解,因为你分辨率增大了,但屏幕尺寸并没有变大多少,必须让css中的`1px`代表更多的物理像素,才能让1px的东西在屏幕上的大小与那些低分辨率的设备差不多,不然就会因为太小而看不清。所以在`1080x1920`这样的设备上,在默认情况下,也许你只要把一个div的宽度设为300多px(视`devicePixelRatio`的值而定),就是满屏的宽度了。
**如果把移动设备上浏览器的可视区域设为viewport的话,某些网站就会因为viewport太窄而显示错乱,所以这些浏览器就决定默认情况下把viewport设为一个较宽的值,比如`980px`,** 这样的话即使是那些为桌面设计的网站也能在移动浏览器上正常显示了。
然而,layout viewport 的宽度是大于浏览器可视区域的宽度的,所以我们还需要一个viewport来代表 浏览器可视区域的大小,ppk把这个viewport叫做 visual viewport。visual viewport的宽度可以通过`window.innerWidth` 来获取,但在Android 2, Oprea mini 和 UC 8中无法正确获取。
## 缩放
**缩放与设备像素、CSS像素的关系**
缩放是在放大或缩小CSS像素,比如一个宽度为 200px 的元素无论放大,还是200个CSS像素。但是因为这些像素被放大了,所以CSS像素也就跨越了更多的设备像素。缩小则相反。
### 缩放与视口
**缩放会影响视觉视口的尺寸**
页面被用户放大,视觉视口内CSS像素数量减少;被用户缩小,视觉视口内CSS像素数量增多就行了。这个道理应该是不难想的。
**用户缩放不会影响布局视口**
>注意,这是『用户缩放』,后面会说开发者设置缩放的情况
### 缩放比例
我们在开发者工具中可以在这里查看缩放比例:
![](https://box.kancloud.cn/a555615c97e7703aa0304c9b91ca5497_850x198.png)
这里的 0.3 是相对于**理想视口**的。
在下载浏览器中,可以这么算(理想视口与视觉视口的比):
```javascript
zoom level = screen.width / window.innerWidth
```
**禁止缩放**
```
<meta name="viewport" content="user-scalable=no">
```
**设置缩放**
```html
<meta name="viewport" content="initial-scale=2">
```
使用initial-scale有一个副作用:同时也会将布局视口的尺寸设置为缩放后的尺寸。所以`initial-scale=1`与`width=device-width`的效果是一样的。
## 完美视口
解决各种浏览器兼容问题的理想视口设置
```html
<meta name="viewport" content="width=device-width,initial-scale=1">
```
## 设备像素比(DPR)
在谈到像素的时候,讲到除了缩放,屏幕是否为高密度也会影响设备像素和CSS像素的关系。
因为安卓产品的参差不齐和厂商对屏幕制造标准的标准不一样。这个放大的比例并不是固定的。还好我们有一个叫设备像素比的东西来检测当前屏幕是不是属于高清屏幕。
在缩放程度为100%(这个条件很重要,因为缩放也会影响他们)时,他们的比例叫做设备像素比(device pixel ratio):
~~~
dpr = 设备像素 / CSS像素
当这个比率为2:1时,使用4个设备像素显示1个CSS像素,当这个比率为3:1时,使用9(3*3)个设备像素显示1个CSS像素。
~~~
在 JavaScript 中可以通过 `window.devicePixelRatio` 获取,在 CSS Media Query 中的名称是 `device-pixel-ratio`
设备像素比也和视口有关:
~~~
dpr = 屏幕横向设备像素 / 理想视口的宽
~~~
~~~
1倍:1pt=1dp=1px(iPhone 3GS)
2倍:1pt=1dp=2px(iPhone 4s/5/6)
3倍:1pt=1dp=3px(iPhone 6 plus)
~~~
这样在iphone4s上,屏幕的物理像素为640像素,独立像素还是320像素,因此window.devicePixelRatio=2。
由上面的举例相信已经对设备的物理像素和独立像素有了一个认识,那么总结如下:
1. 设备的物理像素(也叫设备像素),就是屏幕显示颜色的最小的物理单元,也就是我们经常看到的手机分辨率所描述的数字;
2. 设备独立像素(与密度无关的像素),就是我们手机的实际的视窗口的大小。具体来说**可以认为是计算机坐标系统中得一个点,这个点代表一个可以由程序使用的虚拟像素(比如: css像素),然后由相关系统转换为物理像素。**
## 高清屏(Retina)概念解析
**简单整理下高清显示屏原理如下:**
1、一种具备超高像素密度的液晶屏
2、同样大小的屏幕上显示的像素点由1个变为多个
**高清屏(Retina)和普通屏相比,相同区域的物理像素点数,高清屏是普通屏的4倍。**
在不同的屏幕上(普通屏幕 vs retina屏幕),css像素所呈现的大小(物理尺寸)是一致的,不同的是1个css像素所对应的物理像素个数是不一致的。
比如iPhone 5使用的是Retina视网膜屏幕,使用2px x 2px的 device pixel 代表 1px x 1px 的 css pixel,所以设备像素数为640 x 1136px,而CSS逻辑像素数为320 x 568px。
所以在Retina屏幕中,dpr=2,看图说话。
![](https://box.kancloud.cn/528422b22e510b4176c0c99b8635cc77_800x466.png)
## 总结
1个CSS像素相当于多少个物理像素,取决于:
* 页面缩放比
* 屏幕密度
所以设计师给的是`640px`宽的设计稿是根据设备像素(device pixel,物理像素)为单位制作的设计稿;而前端工程师参照相关的设备像素比来进行换算
比如根据iPhone5出稿的设计稿的中有一个·`width:100px,height:200px`的按钮
那么前端工程师在编码web页面时应该写`width:50px,height:100px`。
他们之间的换算比例是根据设备像素比来计算的。
这一篇介绍了移动端适配需要掌握的知识,先说明了移动端存在的两种像素,然后介绍了三种视口,由缩放对视口的影响引入理想视口,最后说明设备像素比。下一篇介绍现在流行的适配方案。
## 参考文章
[像素是什么意思?一个像素有多大?告诉你像素和分辨率的关系!](http://blog.sina.com.cn/s/blog_48744dcb0101bne9.html)
[ppk的移动端系列文章](http://quirksmode.org/mobile/)
[screen.width is useless](http://www.quirksmode.org/blog/archives/2013/11/screenwidth_is.html)
[devicePixelRatio](http://www.quirksmode.org/blog/archives/2012/06/devicepixelrati.html)
[More about devicePixelRatio](http://www.quirksmode.org/blog/archives/2012/07/more_about_devi.html)
下面这些文章可能也会对你有帮助:
[移动前端开发之viewport的深入理解](http://www.cnblogs.com/2050/p/3877280.html)
[深入了解viewport和px](http://tgideas.qq.com/webplat/info/news_version3/804/7104/7106/m5723/201509/376281.shtml)
>摘自:https://github.com/riskers/blog/issues/17
- 前言
- 中文字体
- 移动Web适配方案
- !移动Web基础!
- 详解适配相关概念
- 移动开发之设计稿
- 移动适配方案(一)
- 移动适配方案(二)
- vw+rem 实现移动端布局
- 移动端适配之雪碧图(sprite)背景图片定位
- 适配 iPhoneX
- 前端开发实战
- 打造自己的前端开发流程(Gulp)
- flexible.js案例讲解
- viewport 与 flexible.js解读
- 图片与字体
- 踩过的坑
- 浏览器默认样式
- 300ms点击延迟和点击穿透
- ios css
- CSS 常见问题
- Ionic v1混合开发
- Native App、Web App 、Hybrid App?
- ionic项目结构
- 混淆加密
- 解决问题
- cordova
- 环境配置
- 打包发布
- 问题
- 移动前端开发优化
- Web开发之抓包
- ===web移动开发资源===
- H5组件框架
- 调试集合
- 简单h5调试
- whistle
- devtools-pro