導讀
移動端適配,是我們在開發中經常會遇到的,這里面可能會遇到非常多的問題:
1px
問題UI
圖完美適配方案iPhoneX
適配方案- 橫屏適配
- 高清屏圖片模糊問題
- ...
上面這些問題可能我們在開發中已經知道如何解決,但是問題產生的原理,以及解決方案的原理可能會模糊不清。在解決這些問題的過程中,我們往往會遇到非常多的概念:像素、分辨率、PPI
、DPI
、DP
、DIP
、DPR
、視口等等,你真的能分清這些概念的意義嗎?
本文將從移動端適配的基礎概念出發,探究移動端適配各種問題的解決方案和實現原理。
一、英寸
一般用英寸描述屏幕的物理大小,如電腦顯示器的17
、22
,手機顯示器的4.8
、5.7
等使用的單位都是英寸。
需要注意,上面的尺寸都是屏幕對角線的長度:
二、分辨率
2.1 像素
像素即一個小方塊,它具有特定的位置和顏色。
圖片、電子屏幕(手機、電腦)就是由無數個具有特定顏色和特定位置的小方塊拼接而成。
像素可以作為圖片或電子屏幕的最小組成單位。
下面我們使用sketch
打開一張圖片,將這些圖片放大即可看到這些像素點:
通常我們所說的分辨率有兩種,屏幕分辨率和圖像分辨率。
2.2 屏幕分辨率
屏幕分辨率指一個屏幕具體由多少個像素點組成。
iPhone XS Max
和 iPhone SE
的分辨率分別為2688 x 1242
和1136 x 640
。這表示手機分別在垂直和水平上所具有的像素點數。
當然分辨率高不代表屏幕就清晰,屏幕的清晰程度還與尺寸有關。
2.3 圖像分辨率
我們通常說的圖片分辨率
其實是指圖片含有的像素數
,比如一張圖片的分辨率為800 x 400
。這表示圖片分別在垂直和水平上所具有的像素點數為800
和400
。
同一尺寸的圖片,分辨率越高,圖片越清晰。
2.4 PPI
PPI(Pixel Per Inch)
:每英寸包括的像素數。
PPI
可以用於描述屏幕的清晰度以及一張圖片的質量。
使用PPI
描述圖片時,PPI
越高,圖片質量越高,使用PPI
描述屏幕時,PPI
越高,屏幕越清晰。
在上面描述手機分辨率的圖片中,我們可以看到:iPhone XS Max
和 iPhone SE
的PPI
分別為458
和326
,這足以證明前者的屏幕更清晰。
2.5 DPI
DPI(Dot Per Inch)
:即每英寸包括的點數。
這里的點是一個抽象的單位,它可以是屏幕像素點、圖片像素點也可以是打印機的墨點。
平時你可能會看到使用DPI
來描述圖片和屏幕,這時的DPI
應該和PPI
是等價的,DPI
最常用的是用於描述打印機,表示打印機每英寸可以打印的點數。
一張圖片在屏幕上顯示時,它的像素點數是規則排列的,每個像素點都有特定的位置和顏色。
當使用打印機進行打印時,打印機可能不會規則的將這些點打印出來,而是使用一個個打印點來呈現這張圖像,這些打印點之間會有一定的空隙,這就是DPI
所描述的:打印點的密度。
在上面的圖像中我們可以清晰的看到,打印機是如何使用墨點來打印一張圖像。
所以,打印機的DPI
越高,打印圖像的精細程度就越高,同時這也會消耗更多的墨點和時間。
三、設備獨立像素
實際上,上面我們描述的像素都是物理像素
,即設備上真實的物理單元。
下面我們來看看設備獨立像素
究竟是如何產生的:
智能手機發展非常之快,在幾年之前,我們還用着分辨率非常低的手機,比如分辨率是320x480
,我們可以在上面瀏覽正常的文字、圖片等等。
但是,隨着科技的發展,低分辨率的手機已經不能滿足我們的需求了。很快,更高分辨率的屏幕誕生了,比如640x940
,正好是兩倍。
理論上來講,在白色手機上相同大小的圖片和文字,在黑色手機上會被縮放一倍,因為它的分辨率提高了一倍。這樣,豈不是后面出現更高分辨率的手機,頁面元素會變得越來越小嗎?
然而,事實並不是這樣的,我們現在使用的智能手機,不管分辨率多高,他們所展示的界面比例都是基本類似的。喬布斯在iPhone4
的發布會上首次提出了Retina Display
(視網膜屏幕)的概念,它正是解決了上面的問題,這也使它成為一款跨時代的手機。
在iPhone4
使用的視網膜屏幕中,把2x2
個像素當1
個像素使用,這樣讓屏幕看起來更精致,但是元素的大小卻不會改變。
如果手機使用了視網膜屏幕的技術,那么顯示結果應該是下面的情況,比如列表的寬度為300
個像素,那么在一條水平線上,老手機會用300
個物理像素去渲染它,而新手機實際上會用600
個物理像素去渲染它。
我們必須用一種單位來同時告訴不同分辨率的手機,它們在界面上顯示元素的大小是多少,這個單位就是設備獨立像素(Device Independent Pixels
)簡稱DIP
或DP
。上面我們說,列表的寬度為300
個像素,實際上我們可以說:列表的寬度為300
個設備獨立像素。
打開chrome
的開發者工具,我們可以模擬各個手機型號的顯示情況,每種型號上面會顯示一個尺寸。
比如iPhone X
顯示的尺寸是375x812
,實際iPhone X
的分辨率會比這高很多,這里顯示的就是設備獨立像素。
3.1 設備像素比
設備像素比device pixel ratio
簡稱dpr
,即物理像素和設備獨立像素的比值。
在web
中,瀏覽器為我們提供了window.devicePixelRatio
來幫助我們獲取dpr
。
在css
中,可以使用媒體查詢min-device-pixel-ratio
,區分dpr
:
@media (-webkit-min-device-pixel-ratio: 2),(min-device-pixel-ratio: 2){ }
在React Native
中,我們也可以使用PixelRatio.get()
來獲取DPR
。
當然,上面的規則也有例外,iPhone 6、7、8 Plus
的實際物理像素是1080 x 1920
,在開發者工具中我們可以看到:它的設備獨立像素是414 x 736
,設備像素比為3
,設備獨立像素和設備像素比的乘積並不等於1080 x 1920
,而是等於1242 x 2208
。
實際上,手機會自動把1242 x 2208
個像素點塞進1080 * 1920
個物理像素點來渲染,我們不用關心這個過程,而1242 x 2208
被稱為屏幕的設計像素
。我們開發過程中也是以這個設計像素
為准。
實際上,從蘋果提出視網膜屏幕開始,才出現設備像素比這個概念,因為在這之前,移動設備都是直接使用物理像素來進行展示。
緊接着,Android
同樣使用了其他的技術方案來實現DPR
大於1
的屏幕,不過原理是類似的。由於Android
屏幕尺寸非常多、分辨率高低跨度非常大,不像蘋果只有它自己的幾款固定設備、尺寸。所以,為了保證各種設備的顯示效果,Android
按照設備的像素密度將設備分成了幾個區間:
Android
設備不一定嚴格按照上面的分辨率,每個類型可能對應幾種不同分辨率,所以,每個
Android
手機都能根據給定的區間范圍,確定自己的
DPR
,從而擁有類似的顯示。
Android
設備仍然不能做到在展示上完全相等。
3.3 WEB端開發
在寫CSS
時,我們用到最多的單位是px
,即CSS像素
,當頁面縮放比例為100%
時,一個CSS像素
等於一個設備獨立像素。
但是CSS像素
是很容易被改變的,當用戶對瀏覽器進行了放大,CSS像素
會被放大,這時一個CSS像素
會跨越更多的物理像素。
頁面的縮放系數 = CSS像素 / 設備獨立像素
。
四、視口
視口(viewport
)代表當前可見的計算機圖形區域。在Web
瀏覽器術語中,通常與瀏覽器窗口相同,但不包括瀏覽器的UI
, 菜單欄等——即指你正在瀏覽的文檔的那一部分。
一般我們所說的視口共包括三種:布局視口、視覺視口和理想視口,它們在屏幕適配中起着非常重要的作用。
4.1 布局視口
4.2 視覺視口
4.3 理想視口
布局視口在移動端展示的效果並不是一個理想的效果,所以理想視口(ideal viewport
)就誕生了:網站頁面在移動端展示的理想大小。
如上圖,我們在描述設備獨立像素時曾使用過這張圖,在瀏覽器調試移動端時頁面上給定的像素大小就是理想視口大小,它的單位正是設備獨立像素。
4.4 Meta viewport
<meta>
元素表示那些不能由其它HTML
元相關元素之一表示的任何元數據信息,它可以告訴瀏覽器如何解析頁面。
我們可以借助<meta>
元素的viewport
來幫助我們設置視口、縮放等,從而讓移動端得到更好的展示效果。
<meta name="viewport" content="width=device-width; initial-scale=1; maximum-scale=1; minimum-scale=1; user-scalable=no;">
上面是viewport
的一個配置,我們來看看它們的具體含義:
Value |
可能值 | 描述 |
---|---|---|
width |
正整數或device-width |
以pixels (像素)為單位, 定義布局視口的寬度。 |
height |
正整數或device-height |
以pixels (像素)為單位, 定義布局視口的高度。 |
initial-scale |
0.0 - 10.0 |
定義頁面初始縮放比率。 |
minimum-scale |
0.0 - 10.0 |
定義縮放的最小值;必須小於或等於maximum-scale 的值。 |
maximum-scale |
0.0 - 10.0 |
定義縮放的最大值;必須大於或等於minimum-scale 的值。 |
user-scalable |
一個布爾值(yes 或者no ) |
如果設置為 no ,用戶將不能放大或縮小網頁。默認值為 yes。 |
4.5 移動端適配
為了在移動端讓頁面獲得更好的顯示效果,我們必須讓布局視口、視覺視口都盡可能等於理想視口。
device-width
就等於理想視口的寬度,所以設置width=device-width
就相當於讓布局視口等於理想視口。
由於initial-scale = 理想視口寬度 / 視覺視口寬度
,所以我們設置initial-scale=1;
就相當於讓視覺視口等於理想視口。
這時,1個CSS
像素就等於1個設備獨立像素,而且我們也是基於理想視口來進行布局的,所以呈現出來的頁面布局在各種設備上都能大致相似。
4.7 獲取瀏覽器大小
瀏覽器為我們提供的獲取窗口大小的API
有很多,下面我們再來對比一下
window.innerHeight
:獲取瀏覽器視覺視口高度(包括垂直滾動條)。window.outerHeight
:獲取瀏覽器窗口外部的高度。表示整個瀏覽器窗口的高度,包括側邊欄、窗口鑲邊和調正窗口大小的邊框。window.screen.Height
:獲取獲屏幕取理想視口高度,這個數值是固定的,設備的分辨率/設備像素比
window.screen.availHeight
:瀏覽器窗口可用的高度。document.documentElement.clientHeight
:獲取瀏覽器布局視口高度,包括內邊距,但不包括垂直滾動條、邊框和外邊距。document.documentElement.offsetHeight
:包括內邊距、滾動條、邊框和外邊距。document.documentElement.scrollHeight
:在不使用滾動條的情況下適合視口中的所有內容所需的最小寬度。測量方式與clientHeight
相同:它包含元素的內邊距,但不包括邊框,外邊距或垂直滾動條。
六、移動端適配方案
盡管我們可以使用設備獨立像素來保證各個設備在不同手機上顯示的效果類似,但這並不能保證它們顯示完全一致,我們需要一種方案來讓設計稿得到更完美的適配。
6.1 flexible方案
lib-flexible這個過渡方案已經可以放棄使用,不管是現在的版本還是以前的版本,都存有一定的問題。建議大家開始使用viewport來替代此方案。
下面我們來看看現在最流行的vh、vw
方案。
6.2 vh、vw方案
vh、vw
方案即將視覺視口寬度 window.innerWidth
和視覺視口高度 window.innerHeight
等分為 100 份。
上面的flexible
方案就是模仿這種方案,因為早些時候vw
還沒有得到很好的兼容。
vw(Viewport's width)
:1vw
等於視覺視口的1%
vh(Viewport's height)
:1vh
為視覺視口高度的1%
vmin
:vw
和vh
中的較小值vmax
: 選取vw
和vh
中的較大值
如果視覺視口為375px
,那么1vw = 3.75px
,這時UI
給定一個元素的寬為75px
(設備獨立像素),我們只需要將它設置為75 / 3.75 = 20vw
。
這里的比例關系我們也不用自己換算,我們可以使用PostCSS
的 postcss-px-to-viewport
插件幫我們完成這個過程。寫代碼時,我們只需要根據UI
給的設計圖寫px
單位即可。
當然,沒有一種方案是十全十美的,vw
同樣有一定的缺陷:
px
轉換成vw
不一定能完全整除,因此有一定的像素差。- 比如當容器使用
vw
,margin
采用px
時,很容易造成整體寬度超過100vw
,從而影響布局效果。當然我們也是可以避免的,例如使用padding
代替margin
,結合calc()
函數使用等等...