熟悉又陌生的移動端適配
什么是移動端適配
在不同尺寸的移動設(shè)備上,讓頁面達(dá)到合理的展示或者說等比縮放展示。
分辨率
- 邏輯分辨率:它是軟件和硬件(物理分辨率)之間的一個轉(zhuǎn)換層,可通過
window.screen.width獲取寬度,視口大小就是參照該值進(jìn)行縮放。 - 物理分辨率:它是設(shè)備硬件固有的分辨率,出廠后就固定,可通過查看設(shè)備屏幕參數(shù)獲取,也可通過
window.screen.width * window.devicePixelRatio獲取。
歷代 iphone 分辨率:
| 設(shè)備 | 邏輯分辨率 | 物理分辨率 | dpr = 物理分辨率 / 邏輯分辨率 | PPI |
|---|---|---|---|---|
| iphone 3G | 320 x 480 | 320 x 480 | @1x | 163 |
| iphone 4/4s | 320 x 480 | 640 x 960 | @2x | 163 |
| iphone 5/5s | 320 x 568 | 640 x 1136 | @2x | 326 |
| iphone 6/6s | 375 × 667 | 750 × 1334 | @2x | 326 |
| iphone X/Xs | 375 × 812 | 1125 × 2436 | @3x | 458 |
像素密度 PPI
像素密度 PPI (Pixel Per Inch) 是指每英寸的長度(2.54 cm)中容納的像素個數(shù)。

問:一像素到底有多大?
答:1 像素長度 = 2.54 / PPI
例:1 像素長度(iphone 6) = 2.54 / 326 = 0.0779 mm = 頭發(fā)直徑(0.06 ~ 0.09 mm)
viewport 相關(guān)
概念
移動設(shè)備上的 viewport 就是設(shè)備屏幕上能用來顯示網(wǎng)頁的那一塊區(qū)域,又叫「視口」,代碼就是根據(jù)視口寬度進(jìn)行排列布局的。但 viewport 又不僅僅是瀏覽器可視區(qū)域的大小,它可能比瀏覽器可視區(qū)域大,也可能比它小。默認(rèn)情況下,移動設(shè)備的 viewport 要大于瀏覽器可視區(qū)域,這主要是用來兼容顯示 PC 端的網(wǎng)站(iPhone 默認(rèn)寬度 980px)。
控制 viewport
通過 meta 標(biāo)簽控制 viewport 大小,下面代碼是將 viewport 的寬度等于邏輯分辨率
<meta?name="viewport"?content="width=device-width,?initial-scale=1.0,?maximum-scale=1.0,?user-scalable=0"?/>
width 和 initial-scale 的取值有沖突,width=device-width 與 initial-scale=1.0 是等效的,這兩個值都可以設(shè)置初始視口大小,如果同時設(shè)置時在 Safari 和部分 Android 瀏覽器上會選擇使用較大值。各個瀏覽器表現(xiàn)可能不一致,為了統(tǒng)一應(yīng)該只使用其中一個值,使用 initial-scale=1.0 更方便計算。
視口寬度?=?邏輯分辨率?/?initial-scale
可以通過 document.documentElement.clientWidth 來獲取 viewport 的寬度(不包括滾動條)。
移動端適配的方式
所謂的移動端適配,就是說頁面內(nèi)的元素寬高,是根據(jù)不同邏輯分辨率自動改變的。那什么樣的長度單位能夠滿足呢?答案是通過 vw 或 rem(需要 js 根據(jù)設(shè)備寬度計算得出值) 單位來達(dá)到適配的目的。
<meta?name="viewport"?content="width=device-width,?initial-scale=1.0,?maximum-scale=1.0,?user-scalable=0"?/>
rem
兼容 IOS 4.1+, Android 2.1+ 可看這里[1]
通過 meta 標(biāo)簽設(shè)置視口的寬度等于設(shè)備寬度,如下:
<meta?name="viewport"?content="initial-scale=1.0,?maximum-scale=1.0,?user-scalable=0"?/>
rem 這個單位代表根元素(通常為 元素)的 font-size 大小,通過當(dāng)前設(shè)備寬度來設(shè)置跟元素的字體大小,來達(dá)到移動端適配的目的。
一般是參照設(shè)計稿來進(jìn)行開發(fā),假如設(shè)計稿的寬度為 375px,那怎么通過 rem 來進(jìn)行適配呢?
在不同尺寸的設(shè)備上,通過 js 按照設(shè)計稿的寬度等比例計算根元素的字體大小,這樣才不會失真。
比如,設(shè)計稿的寬度為 375px 時的根字體大小為 100px 也就是 1rem,那當(dāng)前設(shè)備下的根字體為多少?因為是等比縮放,所以通過如下等式可計算得出當(dāng)前根字體(currentRootFontSize)值。
設(shè)計稿根字體大小(隨意設(shè)定)?/?設(shè)計稿寬度?=?當(dāng)前根字體大小?/?視口寬度
因為:100px / designWidth = currentRootFontSize / viewportWidth
所以:currentRootFontSize = viewportWidth * 100px / designWidth
用代碼可表示為:
const?designWidth?=?375?//?設(shè)計稿寬度
const?viewportWidth?=?document.documentElement.clientWidth?//?當(dāng)前視口寬度
document.documentElement.style.fontSize?=?(viewportWidth?/?designWidth)?*?100?+?'px'?//?根字體大小
上面是設(shè)備像素比(dpr)為 1 的計算方式。但是移動適配往往存在 1 像素問題,因為 px 是邏輯像素,跟屏幕上的物理像素有可能不是 1:1 的關(guān)系,所以就有可能出現(xiàn),1px 對應(yīng) 2 個或多個物理像素的情況。
當(dāng) dpr > 1 時,同時想保證 1px 始終對應(yīng) 1 個物理像素,那就需要讓根字體擴(kuò)大 dpr 的倍數(shù),同時通過 meta 標(biāo)簽讓視口縮小 1/dpr 倍即可。
例如:當(dāng) dpr = 2 時
<meta?name="viewport"?content="initial-scale=0.5,?maximum-scale=0.5,?user-scalable=0"?/>
優(yōu)點(diǎn):
- 使用 meta initial-scale 來縮放,解決了 1px 問題
缺點(diǎn):
- 需要額外的 js 邏輯來控制根字體大小
- 邏輯不直觀,需要根字體大小進(jìn)行轉(zhuǎn)換
- initial-scale 進(jìn)行視口縮放后,第三方 UI 庫組件顯示有影響,需要額外處理
采用 rem 進(jìn)行移動端適配,是個歷史過程,因為早期 rem 兼容性比 vw 好,但現(xiàn)在而言,項目沒有要求太高兼容性的情況下,完全可以使用 vw 進(jìn)行移動端適配。
vw
1vw 等于視口寬度的 1%,它是天生的移動端適配單位,無需多余轉(zhuǎn)換。
兼容 IOS 8+, Android 4.4+ 可看這里[2]
優(yōu)點(diǎn):
- 無需引入多余 js 邏輯
- 語義化,vw 邏輯清晰,不需要像 rem 那樣需要換算一下
缺點(diǎn):
- 1px 問題需要額外處理
react 項目應(yīng)用
postcss-px-to-viewport[3] 是將 px 轉(zhuǎn)換成視口單位(vw、vh)的 PostCss[4] 插件
//?項目中配置
addPostcssPlugins([
??require('postcss-px-to-viewport')({
????viewportWidth:?375,?//?視窗的寬度,對應(yīng)的是我們設(shè)計稿的寬度
??}),
]),
參考
- Window.devicePixelRatio[5]
- 移動前端開發(fā)之 viewport 的深入理解[6]
- vw 相比 rem,在實(shí)際開發(fā)中究竟有多大區(qū)別?[7]
- iPhone 屏幕分辨率和適配規(guī)則(基礎(chǔ)篇)[8]
- 邏輯分辨率和物理分辨率到底是什么呀?[9]
參考資料
[1]可看這里: https://caniuse.com/#feat=rem
[2]可看這里: https://caniuse.com/#feat=viewport-units
[3]postcss-px-to-viewport: https://github.com/evrone/postcss-px-to-viewport/blob/master/README_CN.md
[4]PostCss: https://github.com/postcss/postcss
[5]Window.devicePixelRatio: https://developer.mozilla.org/zh-CN/docs/Web/API/Window/devicePixelRatio
[6]移動前端開發(fā)之 viewport 的深入理解: https://www.cnblogs.com/2050/p/3877280.html
[7]vw 相比 rem,在實(shí)際開發(fā)中究竟有多大區(qū)別?: https://www.zhihu.com/question/37179916/answer/101810379
[8]iPhone 屏幕分辨率和適配規(guī)則(基礎(chǔ)篇): https://www.jianshu.com/p/41a8ccdf91ed
[9]邏輯分辨率和物理分辨率到底是什么呀?: https://www.zhihu.com/question/40506180
