前言
原文 && 個人首頁
知乎&&知乎專欄
背景
随着移動端的普及,以及手機尺寸越來越多,這就衍生了衆多的适配方案,以下挑一些常見的适配方案進行探讨。
本文預設讀者已經對視口、實體像素、邏輯像素、css像素等移動端基本概念已經了解了。
px + viewport适配
這種适配方案原理比較簡單:實際上就是通過動态設定
meta
标簽的viewport讓css中的1px等于裝置的1px。
js僞代碼:
const scale = 1 / devicePixelRatio
head.appendChild(`<meta name='viewport' content='width=device-width, initial-scale=${scale}, maximum-scale=${scale}, minimum-scale=${scale}, user-scalable='no'>`)
body.setAttribute('data-dpr', devicePixelRatio)
css僞代碼
[data-dpr='3'] {
property: value * 3
...
}
[data-dpr='2'] {
property: value * 2
...
}
大家可以用不同手機進入這裡,或者用浏覽器模拟。
優點:簡單易于了解
缺點:不能涵蓋所有dpr的機型,即使能,也會造成代碼臃腫。
rem布局
首先我們需要知道
-
的大小是基于頁面根元素的rem
。font-size
-
的本質是等比縮放。rem
例如:根元素的
font-size: 16px
則,
1rem
就等于
16px
;根元素的
font-size:32px
則,
1rem
就等于
32px
可以到這裡來意會一下
rem
的本質是縮放
這麼一來,我們就可以利用這個特性動态設定根元素的
font-size
來達到我們想要的效果。
基于設計圖的rem布局
通常我們拿到的設計圖寬度的是750也就是基于iphone6/7/8的設計圖,我們如果要想讓1px像素等于設計圖的1px該怎麼做呢?
其實很簡單,直接讓根元素的
font-size: 0.5px
即可(因為是2倍圖,1px等于2實際像素,是以為
0.5px
)。
那麼問題來了:設計圖一定會是750的,可是市面上機型那麼多,螢幕不一定是750啊,怎麼辦?
前面我說過,
rem的本質是等比縮放,讓大于750或者小于750螢幕的手機等比縮放不就完事了?
js僞代碼:
html.fontSize = clientWidth / 750
你以為這就完事了?其實事情并沒有那麼簡單。
别忘了,平時我們開發都是在chrome下開發的。chrome并不支援
font-size
小于12的字型,那怎麼辦呢?
還能怎麼辦,為了友善計算,讓
font-size
大于12呗,在以上基礎上将結果放大100倍,然後寫樣式的時候再除以100:
js僞代碼:
html.fontSize = clientWidth / 750 * 100
樣式:
.element {
width: 0.1rem; /* 實際到6/7/8上就是10px */
}
基于螢幕百分比的rem布局
假設一個頁面有100份,每一份的寬度用
x
表示,則
x=螢幕寬度(即clientWidth)/100
,如果
font-size: x
,那我們不就實作了
1rem === 1%
螢幕寬度了嗎?
可是現實是殘酷的,平時我們開發的時候寫樣式可不是按照螢幕寬度的百分之幾來寫的,而是根據設計給的标注來寫的,而且設計通常給的設計圖寬度是
750
的,那麼如何轉換呢?
其實可以換種思維:如果得到這個元素的寬度占頁面寬度的百分之幾不就完事了嗎?
是以,
實際的rem = 元素标注長度 / 設計圖寬度(這裡是750) * 100
當然,這個轉換過程可以讓postcss去幫我們轉換。
rem布局的優缺點
優點:易于了解,且相容性好,能夠很好地解決适配問題。
缺點:- 需要用js額外設定字型大小,強制設定根元素的字型大小,剝奪了使用者的自由
- 在webview中,部分機型下使用者設定系統預設字型大小會造成布局錯亂
小程式的黑魔法rpx布局
做過小程式的都知道,小程式有個
wxss
,這玩意有個rpx的機關,官方文檔是這麼介紹他的:
可以根據螢幕寬度進行自适應。規定螢幕寬為750rpx。如在 iPhone6 上,螢幕寬度為375px,共有750個實體像素,則750rpx = 375px = 750實體像素,1rpx = 0.5px = 1實體像素
再看到下面那個轉換表時我瞬間就不淡定了:
既是根據螢幕寬度自适應的又和上一章講到的:基于設計圖的rem布局的換算結果一樣的,那它内部的實作原理其實和基于設計圖的rem布局的原理差不多。
隻不過小程式内部處理了一下,讓rpx直接能夠根據螢幕寬度自适應,而不是像rem那樣依賴于根元素的
font-size
vw布局
上一章基于螢幕百分比的rem布局小節中提到的
x
代表1份螢幕的寬度,在css中剛好有個機關代表這個
x
,即vw,至于vw是啥這裡就不多說了,都9012年了,竟然還不知道vw是什麼,傳送門。
那麼問題來了,平時我們拿到的設計圖都是基于
px
标記的,怎麼将
px
轉為
vw
呢?
emmm,這裡有個專門的插件專門做這件事postcss-px-to-viewport
其代碼中有這麼一句:
toFixed((pixels / viewportSize * 100), opts.unitPrecision)
是不是和我們上面所說的
實際的rem = 元素标注長度 / 設計圖寬度(這裡是750) * 100
很像?
這個插件會解析我們的css檔案,将
px
轉換為
vw
,至于為什麼要這麼轉化,上面已經說過了,這裡就不再贅述。
vw
完美地避開了
rem
的缺點,還繼承了
rem
的優點,然而
vw
并不是完美的,由于
px
轉
vw
是經過js計算的,多少都會優些精度損失,不過這是個小問題,不會丢失多少精度,除非你對精度要求特别高。
參考
Rem布局的原了解析
rem, vw, 還是…? 各憑本事的移動端适配方案