1. 程式人生 > >移動端開發基礎之適配

移動端開發基礎之適配

說到移動端適配,首先我們需要先搞清楚一些基礎知識,所以本文路線是先了解畫素,dpr,視口等基礎知識,然後再整理出移動端適配方案。

畫素

畫素其實分為兩種,分別是物理畫素和CSS畫素

1.物理畫素(裝置畫素)

物理畫素,顧名思義,顯示屏是由一個個物理畫素點組成的,通過控制每個畫素點的顏色,使螢幕顯示出不同的影象,螢幕從工廠出來那天起,它上面的物理畫素點就固定不變了,單位pt。
通常我們看一些電子裝置的引數,比如解析度用的就是物理畫素。它配合螢幕尺寸(注意:螢幕尺寸通常是說螢幕的對角線長度),可以計算出PPI(每英寸畫素取值),即每一英寸物理畫素數量。PPI越高,說明螢幕能提供更多細節。
這裡寫圖片描述

2.CSS畫素

CSS和js使用的抽象單位,瀏覽器內的一切長度都是以CSS畫素為單位的,CSS畫素的單位是px。

3.物理畫素和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百分比寬度推算的根源。
在桌面瀏覽器,視口的寬度等同於瀏覽器視窗的寬度,高度即為瀏覽器視窗的高度。但移動端就有點複雜了,移動端的視口分為佈局視口和視覺視口。

1.佈局視口(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寬。
#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問題和圖片的高清問題了。

適配方案

1.固定高度,寬度自適應

即垂直方向使用固定大小,水平方向使用百分比,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/