[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就是設備獨立像素。

在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 是可以通過 分辨率 和 屏幕尺寸 計算得到的:

[這個網站](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中以移動開發模式看到。

瀏覽器廠商為了讓用戶在小屏幕下網頁也能夠顯示地很好,所以把視口寬度設置地很大,一般在 `768px ~ 1024px` 之間,最常見的寬度是 `980px`。
所以,在手機上,視口與移動端瀏覽器,屏幕寬度不再相關聯,是完全獨立的,這個瀏覽器廠商定的視口被稱為**布局視口**。

布局視口我們是看不見的,只知道網頁的最大寬度是 `980px` ,并且被縮放在了屏幕內。
可以這樣設置布局視口的寬度:
```
<meta name="viewport" content="width=640">
```
**媒體查詢與布局視口**
700px 指的是布局視口的寬度
```css
@media (min-width: 700px){
...
}
```
**獲取布局視口的尺寸**:
```js
document.documentElement.clientWidth/Height //返回布局視口的尺寸
```
## 視覺視口(visual viewport)

視覺視口是用戶正在看到的網頁的區域,大小是屏幕中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不再更新。

而下載的瀏覽器都返回的是理想視口尺寸。
`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像素數量增多就行了。這個道理應該是不難想的。
**用戶縮放不會影響布局視口**
>注意,這是『用戶縮放』,后面會說開發者設置縮放的情況
### 縮放比例
我們在開發者工具中可以在這里查看縮放比例:

這里的 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,看圖說話。

## 總結
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