說到移動端適配,首先我們需要先搞清楚一些基礎知識,所以本文路線是先了解像素,dpr,視口等基礎知識,然后再整理出移動端適配方案。
基礎知識
像素
像素其實分為兩種,分別是物理像素和CSS像素
- 物理像素(設備像素)
物理像素,顧名思義,顯示屏是由一個個物理像素點組成的,通過控制每個像素點的顏色,使屏幕顯示出不同的圖像,屏幕從工廠出來那天起,它上面的物理像素點就固定不變了,單位pt。
通常我們看一些電子設備的參數,比如分辨率用的就是物理像素。它配合屏幕尺寸(注意:屏幕尺寸通常是說屏幕的對角線長度),可以計算出PPI(每英寸像素取值),即每一英寸物理像素數量。PPI越高,說明屏幕能提供更多細節。
- CSS像素
CSS和js使用的抽象單位,瀏覽器內的一切長度都是以CSS像素為單位的,CSS像素的單位是px。 - 物理像素和CSS像素之間的關系(dppx,DPR)
在非高清屏及未縮放的狀態下,一個CSS像素等於一個物理像素,而在一些PPI非常高的屏幕(例如蘋果的視網膜屏幕)上,如果還讓一個CSS像素等於一個物理像素,就會導致網頁上的元素變得非常小,因此高PPI屏下,通常一個CSS像素等於兩個甚至三個物理像素(由瀏覽器廠商決定,不同瀏覽器設定的值可能不同)。如果一個CSS像素占用n個物理像素,那么我們就說此時的dppx(dots per px)為n。
所以,我們可以用dppx描述物理像素和CSS像素之間的關系。dppx除了和PPI有關,也和當前縮放狀態有關,舉個例子,在非高清屏下,如果沒縮放,一個CSS像素占用一個物理像素,此時是1dppx,但如果將頁面放大到200%,此時一個CSS像素等於兩個物理像素,即2dppx。
DPR(設備像素比,devicePixelRatio)描述的是未縮放狀態下,物理像素和CSS像素的初始比例關系,計算方法如下圖。
DPR由瀏覽器廠商決定,我們無法修改,但可以通過window.devicePixelRatio讀取DPR。
可能有人疑問DPR和dppx到底啥關系,我們可以認為DPR描述的是未縮放狀態,而dppx可以描述任意時刻的狀態,未縮放狀態下的dppx和DPR相等,當有縮放操作時,此時的物理像素和CSS像素的比例關系就只能用dppx描述了。
視口(viewport)
視口也叫作初始包含塊,之所以叫這個名字,是因為它包含了元素,它的寬度是所有CSS百分比寬度推算的根源。
在桌面瀏覽器,視口的寬度等同於瀏覽器窗口的寬度,高度即為瀏覽器窗口的高度。但移動端就有點復雜了,移動端的視口分為布局視口和視覺視口。
- 布局視口(layout viewport)
說布局視口之前,我們先說一下桌面端的頁面放到移動端瀏覽器后出現的問題:由於早期的頁面很多采用固定寬度的布局,導致放在移動端的小窗口下出現橫向的滾動條,不便於用戶查看,所以瀏覽器廠商研究出了布局視口。布局視口的寬度一般在768px~1024px(由瀏覽器廠商設置),常見寬度980px,雖然設置了很大的寬度,但在沒有手動設置寬度的情況下,布局視口仍會容納在一屏里(說白了,就是把980px的東西裝在320px的屏幕里),這樣,瀏覽器會避免出現橫向滾動條。
設置布局視口寬度:<meta name="viewport" content="width=640">
獲取布局視口寬度:document.documentElement.clientWidth
布局視口除了和meta設置的width有關,還和scale有關,scale后面會說。
2. 視覺視口(visual viewport)
用戶正在看到的網頁的區域,大小是屏幕中CSS像素的數量。
獲取視口寬度:window.innerWidth/Height
初始狀態下,一般視覺視口會等於布局視口。
3. 理想視口
當布局視口的寬度達到理想狀態時,就是理想視口,理想視口中的頁面有最理想的寬度,用戶進入頁面不用縮放。
實現方法:<meta name="viewport" content="width=device-width">(即設置布局視口寬度為設備寬度)
縮放(scale)
原理:可以理解為修改dppx,舉個例子,假設一屏幕DPR為2,即1px對應2pt,如果這個時候,我們設置scale=0.5,那么dppx就會從2變成1,即1px對應1pt。所以,通過縮放我們可以調整自己所能控制的最小物理像素粒度。
縮放會影響布局視口的大小,因為瀏覽器會盡量讓布局視口鋪滿屏幕,所以當我們設置scale=0.5時,布局視口的寬度會自動變為原來的兩倍。
具體如下圖所示。
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<script>
console.log(document.documentElement.clientWidth); //結果為375
</script>
<meta name="viewport" content="width=device-width,initial-scale=0.5">
<script>
console.log(document.documentElement.clientWidth); //結果為750
</script>
可能有人會問,scale的初始值是多少?其實這取決於viewport的寬度,因為瀏覽器會盡量讓布局視口鋪滿全屏,所以瀏覽器會根據布局視口的大小動態調整scale的初始值大小,從而使布局視口鋪滿全屏。
使用縮放的好處:對於DPR為2或更高的屏幕,如果不做縮放操作,我們所能控制的最小物理像素粒度是2pt或更多物理像素,這樣帶來了兩個問題。
- 如果設計圖上某個邊框標識的大小是1px,那說明設計師想讓這個邊框在任何屏幕上都是1像素,在普通屏幕上,不會有問題,但在retina這種高清屏上,當我們用1px修飾border時,實際上會有2pt渲染為border,導致border看上去比普通屏的border寬。
<style>
#log {
border: 1px solid black;
}
</style>
<div id="log"></div>
- 對於高清屏,如果我們控制像素的粒度還是2pt或3pt,實際上我們並沒有充分利用高清屏展現細節的能力。
解決這兩個問題的方法就是縮放,我們把scale設置為1/dpr。
meta.setAttribute('content', 'initial-scale=' + 1/dpr + ', maximum-scale=' + 1/dpr + ', minimum-scale=' + 1/dpr + ', user-scalable=no');
這樣1px就對應1pt,我們就可以解決1px border問題和圖片的高清問題了。
適配方案
- 固定高度,寬度自適應
即垂直方向使用固定大小,水平方向使用百分比,flex。
<meta name="viewport" content="width=device-width,initial-scale=1">
適合場景:比較適合列表式的結構。
2. 固定寬度,viewport縮放
開發頁面時完全按照和設計圖1:1的比例開發,設計圖,頁面,視口寬度使用同一個寬度,單位使用px即可,由於瀏覽器會盡量將布局視口鋪滿全屏,所以瀏覽器會自動幫助我們縮放。舉個例子,假設設計圖是640px寬,那我們這樣設置。
<meta name="viewport" content="width=640">
這個時候會發現,瀏覽器會幫助我們將頁面鋪滿全屏,而且這是絕對的等比例縮放。圖片、文字等等所有元素都被縮放在手機屏幕中。
3. rem做寬度,viewport縮放
根據屏幕寬度設定rem值,需要適配的元素都使用rem為單位,不需要適配的元素還是使用 px 為單位。
總共分兩步:
- 動態設置font-size
document.getElementsByTagName('html')[0].style.fontSize = window.innerWidth / 10 + 'px';
- 縮放viewport
meta.setAttribute('content', 'initial-scale=' + 1/dpr + ', maximum-scale=' + 1/dpr + ', minimum-scale=' + 1/dpr + ', user-scalable=no');
第二步是對頁面適配的優化,修改scale是為了解決前面說到的1px border問題。
淘寶的Flexible使用的就是這種方案,而且它加了data-dpr屬性,這樣我們就可以根據不同的DPR設置不同的樣式。
方案總結
方案一比較適合列表這種比較固定的結構,方案二適合的場景比較多,而且實現簡單,但需要注意它會將頁面的所有元素都縮放,方案三適合的場景是頁面內有些元素需要適配,有些元素不需要適配。總體來講,方案二和方案三是比較常用的方式。
參考文獻:
https://www.zybuluo.com/gongzhen/note/170557
鄭航的知乎回答https://www.zhihu.com/question/35221839
https://github.com/amfe/article/issues/17
https://github.com/riskers/blog/issues/18
https://riskers.github.io/share/share/flexible.htm#1
https://github.com/riskers/blog/issues/17
https://github.com/amfe/lib-flexible
http://www.html-js.com/article/2402
http://www.alloyteam.com/2016/03/mobile-web-adaptation-tool-rem/
https://www.nihaoshijie.com.cn/index.php/archives/593
http://www.zhangxinxu.com/wordpress/2012/08/window-devicepixelratio/