移動端常見適配方案
做移動端頁面有一段時間了,總結下工作中常用的幾種移動端適配方案。
基礎
網上已經有非常多的基礎知識總結,其中容易搞混的概念是視口。
meta 標簽中的 viewport 屬性,就是 視圖 的含義
視口分為
- 布局視口
- 視覺視口
- 理想視口
布局視口
也就是 <meta name="viewport" content="width=device-width"> 中 width 屬性的含義
我們在css中寫的所有樣式,就是相對于 布局視口 進行布局的
默認情況下,移動端的布局視口并不是屏幕寬度,而是一般在768px ~ 1024px間(大部分情況下980px)
可以通過 document.documentElement.clientWidth 獲取 (根據 width 和 initial-scale 來確定)
視覺視口
視覺視口是指用戶通過設備屏幕看到的區域,默認等于當前瀏覽器的窗口大小(當 initial-scale 為1)
當用戶對瀏覽器進行縮放時,不會改變布局視口的大小,所以頁面布局是不變的,但是 縮放會改變覺視口的大小
可以通過 window.innerWidth 獲取 (會隨著縮放進行改變)
放大頁面,此時 window.innerWidth 反而減小 (頁面放大,你看到的東西也變少了)
理想視口
理想視口是指網站在移動設備中的理想大小,這個大小就是設備的屏幕大小
也就是 <meta name="viewport" content="width=device-width"> 中 device-width 的含義
可以通過 screen.width 獲取 (常量,不會改變)
initial-scale
<meta name="viewport" content="width=device-width, initial-scale=0.5">
根據公式 initial-scale = 理想視口寬度 / 視覺視口寬度
假設理想視口寬度為 414px (device-width),此時設置 initial-scale 為0.5,那么視覺視口寬度就是 414 / 0.5 = 818
如果這時你獲取 document.documentElement.clientWidth (布局視口)的值,會發現不是 414px 而是 818px
結論: 布局視口寬度取的是width和視覺視口寬度的最大值
思考題:
<meta name="viewport" content="width=600, initial-scale=2">
假設理想視口寬度為 414px (device-width),此時 document.documentElement.clientWidth (布局視口)的值是多少?
總結
- document.documentElement.clientWidth : 布局視口,css中一般寫成 width=device-width
- window.innerWidth : 視覺視口,頁面縮放都會實時改變該值
- screen.width : 理想視口,頁面屏幕大小(設備獨立像素),也就是css中的 device-width
常見適配方案
簡單一句話概括:移動端適配就是在進行 屏幕寬度 的 等比例縮放 :
平時我們開發中,拿到的移動端設計稿一般是 750 * 1334 尺寸大?。?iPhone6 的設備像素為標準的設計圖)。那如果在 750px 設計稿上量出的元素寬度為 100px ,那么在 375px 寬度的屏幕下,這個元素寬度就應該等比例縮放成 50px 。
所以適配的難點是:如果實現頁面的等比例縮放?
Rem 方案
該方案的核心就是:所有需要動態布局的元素,不再使用 px 固定尺寸,而是采用 rem 相對尺寸
rem 的大小是相對于根元素 html 的字體大?。喝绻?span> html 的 font-size 為100px,那么 1rem 就等于100px
現在我們假定:
750px 屏幕下 html 的 font-size 為100px,也就是 1rem 為100px,那么 200px 寬度的 .box 元素,就應該寫成 2rem
那么現在:
375px 屏幕下,我們需要 .box 元素渲染成 100px
由于 .box 的寬度仍然是 2rem ,因此,這時候我們就需要 1rem 為50px,也就是說,此時 html 的 font-size 為50px
于是此時,我們可以得出一個公式:
(750) / (100) = (當前屏幕尺寸) / (當前屏幕1rem)
把這個公式進行一次數學轉換就能得到:
(當前屏幕1rem) = 100 * (當前屏幕尺寸) / 750
翻譯成js語言就是
將代碼優化一下
考慮到Andorid端字體渲染的問題以及頁面大小變化的監聽,最終的代碼如下:
注意的是:我們取 100px 作為設計稿的1rem,是因為方便計算,比如設計稿上量出 250px ,我們就可以很容易的計算出為 2.5rem 。
我們當然也可以把 50px 作為設計稿的1rem,這時設計稿上的 250px ,就要寫成 5rem 。
其實我們也可以借助于postcss-pxtorem或者 SCSS 函數來幫我們自動轉換單位
通過Rem方案,需要動態縮放的元素,我們使用 rem 相對單位,不需要縮放的元素,我們仍然可以使用 px 固定單位。
不過在大屏設備下(例如ipad或者pc端),由于我們的頁面是等比例縮放,這時候頁面的元素會被放大很多(屏幕寬度大,導致根元素字體1rem也變大)。但是在大屏下,我們真正希望的是用戶看到更多的內容,這時候我們可以使用媒體查詢的方式來限制根元素的字體,從而防止在大屏下元素過大的問題。
或者修改js腳本的邏輯
VW 方案
vw 是相對單位,1vw 表示屏幕寬度的 1%
其實我們的 REM方案 就是 VW方案 的模擬,之前我們有一個公式:
(750) / (100) = (當前屏幕尺寸) / (當前屏幕1rem)
換一個轉換方式:
(當前屏幕1rem) = (當前屏幕尺寸) / 7.5
而 vw 單位其實就是:
(當前屏幕1vw) = (當前屏幕尺寸) / 100
因此, REM方案 就是用 JS 把屏幕寬度分成了7.5份,而 CSS3 中新增的 vw 單位,原生實現了把屏幕寬度分成了100份
所以,在 VW方案 中,我們不再需要使用JS腳本了!
750px 設計稿中, 1vw 等于 7.5px (750 / 100),因此,在設計稿中,量出 200px 的寬度,就因為寫成 26.667vw (200 / 7.5)
不過使用 vw 換算,并不像 rem 那么方便,這時候我們可以借助 postcss-px-to-viewport 或者 SCSS 函數來幫我們自動轉換單位
同樣,在大屏設備下,由于屏幕寬度大,所以頁面的元素同樣會放大很多(屏幕寬度大,1vw也很大)。但是由于 vw 是相對屏幕寬度的,所以我們不能像 REM方案 中一樣,手動控制 html 的根字體大小,這也是使用 VW方案 的一個缺點。
REM + VW 方案
REM方案 的優勢是可以手動控制 rem 的大小,防止屏幕太大時,頁面元素也縮放很大,但是缺點就是需要使用 JS 。 VW方案 剛好相反,無需使用 JS 但是無法手動控制 vw 的大小。
其實我們可以把兩者結合:
對于布局元素,我們仍然使用 rem 單位。但是對于根元素的字體大小,我們不需要使用JS來動態計算了
這段js可以直接使用css來實現
對于大屏設備,我們使用媒體查詢
更詳細的 vw+rem布局方案 可以見 《基于vw等viewport視區單位配合rem響應式排版和布局》
viewport 縮放方案
還有一種更簡單粗暴的方法,就是我們設置 initial-scale
我們的布局完全基于設計稿 750px ,布局元素單位也使用 px 固定單位 (布局視口寫死750px)
對于 375px 寬度,我們就將整個頁面縮放 0.5 :
<meta name="viewport" content="width=750, initial-scale=0.5, minimum-scale=0.5, maximum-scale=0.5, user-scalable=0">
此方案的缺點: 整個頁面都被縮放了,對于不想縮放的元素無法控制。
市面上一些營銷H5頁面,由于是通過后臺可視化拖拽搭建出來的,為了適配各種尺寸的屏幕,該方案是成本最低的實現(易企秀就是使用這種方案)
實戰
我們拿線上B站的會員購作為示例
- rem方案
- vw方案
- rem+vw方案
- viewport方案
請使用chrome開發者工具模擬移動端設備查看
源碼直接右鍵查看即可,代碼沒有經過壓縮,可以很直觀的看到各種方案的css適配寫法