本文主要針對初學移動端web開發的讀者,筆者也是初學者,文中有衆多用詞不當之處望讀者指正。css
從開始作web app開發到如今,一直對移動端的尺寸適配有一種模糊的概念。能說得上來‘媒體查詢’,‘柵格化佈局’,‘流式佈局’等若干技術名詞和實現方式,可是每次本身作web app開發的時候,作出來的產物老是不盡人意,好比在iPhone5
上出現文字溢出,調整好的佈局位置在一些小尺寸手機上發現位置很是不對,或是遮蓋了其餘元素,或是換行了。html
若是是以前,我是這樣的作法:前端
不斷寫媒體查詢作兼容,直到PM或者QA滿意爲止。html5
這樣的方法,存在如下幾個問題:git
我想了想,爲何會出現治標不治本的狀況:github
一勞永逸
,全尺寸支持
,不用動腦子算
的移動端尺寸適配方案呢?**答案固然是有的。**筆者結合了本身所看的幾篇熱門的博客,總結了其中比較有用的幾個知識點,但願能讓讀者更快的掌握並使用這個'一勞永逸的方法'。能偷懶的事情絕對要偷懶。🐷(熱門博客題目如圖,含flexible的github repo) web
230px
, 經過函數將其轉爲rem
而不用人工計算。一句話歸納: 假如<html>
標籤上設置了樣式font-size:16px
,那麼 1rem = 16px
。 因此:bash
首先,須要和UI小姐姐說一句話:前端工程師
"標註元素的時候請按照
750px * 1334px
爲準。"app
那麼,你將會拿到一張以下的標註圖:
到這一步,咱們仍然沒有解決核心問題:
接下來,就是最爲核心的環節了,筆者經過步驟圖向你們還原計算的過程。
第一步:假設有三款不一樣長寬的手機。
第二步:把手機的寬分爲10份,那麼上述三款手機的每份寬度是35px/36px/37px。而且將<html>
標籤添加不一樣的font-size
設置。
即:一份分別爲35px/36px/37px
第三步:根據UI的px標註圖計算出相應的rem:
第四步:rem將轉化成不一樣的px尺寸在不一樣的手機上呈現:(ps:圖中的除法結果算錯了)
經過這樣的方式,便可以在不一樣尺寸的手機上有相同的展現效果。而最cool的地方,是上述整個過程時自動適配的。開發者只需根據UI標註圖無腦寫就好了,不再用擠眉弄眼地對着Chrome Devtools 瘋狂調試了。
把手機的寬分爲10份,那麼上述三款手機的每份寬度是35px/36px/37px。而且將
<html>
標籤添加不一樣的font-size
設置。
經過JavaScript動態計算出當前的屏幕寬度,切割爲10份並將<html>
的fontSize
設置爲1份單位寬度
。
document.addEventListener('DOMContentLoaded', function(e) {
document.getElementsByTagName('html')[0].style.fontSize = window.innerWidth / 10 + 'px';
}, false);
複製代碼
當初始的 HTML 文檔被徹底加載和解析完成以後,DOMContentLoaded 事件被觸發,而無需等待樣式表、圖像和子框架的完成加載。另外一個不一樣的事件 load 應該僅用於檢測一個徹底加載的頁面。 MDN::DOMContentLoaded
根據UI的px標註圖計算出相應的rem
這一步須要使用Sass
來定義一個px2rem
的工具函數:
// utils.scss
@function px2rem($px){
$rem : 75px; // '750/10':分紅10份
@return ($px/$rem) + rem;
}
// foo.scss
.box1 {
width: px2rem(320px); // '(320/750) * 10 = 4.266rem'
}
複製代碼
這樣,咱們在styleSheet
中實際生效的是height: 4.266rem
,而1rem
對應多少px
是上述JavaScript代碼根據不一樣的window.innerWidth
提早計算好的。這樣就實現了自動適配。
若是你嫌寫
px2rem()
也麻煩,那麼能夠把函數名定義簡單一些。
在寫這篇博客的開始,我曾試圖繞開闡述viewport
和dpr
這個抽象的概念,由於上述的內容已經能夠從一個維度解決大多數問題了。可是,若是想作得更完美,就必須從另外一個維度出發,而這個維度,就是dpr。
首先,要區分兩個概念:
有這樣一個場景
一位前端工程師敲出了
.box {
width: 100px;
height:100px;
}
複製代碼
那麼此時,他的意思是box
在咱們的屏幕中佔的實際長寬是100px
,在他腦中是這樣的畫面:
項目上線以後,有一個用戶'不懷好意'地使用了放大鏡功能將長寬放大了兩倍,如今就變成了:
你會發現,設備花了200px的長寬來渲染CSS裏面定義的100px的長寬,而設備pixels和樣式pixels的比值,就是dpr,即Device Pixel Ratio,若是對這個概念仍有問題,請查看viewport剖析。
咱們你們都知道Retina屏(視網膜屏),之因此看起來這麼高清,就是由於蘋果設備花兩個像素來渲染一個像素的物體,那麼看起來確定更爲精緻。
因此,若是咱們針對dpr=1
的書寫了rem2px(100px)
,那麼在dpr=2
的設備看起來將會是被放大了2倍的元素。
那麼,若是咱們可以查詢出當前設備的dpr,而且作相應的縮放就能夠解決這個問題。
舉個例子:某些安卓機的dpr=1
,可是UI作標註圖的時候是根據dpr=2
來作的,就像咱們上文的750px * 1334px
。直接按照750px * 1334px
寫出來的元素將會被放大兩倍,那麼咱們就使頁面縮小兩倍,如何控制呢?
用viewport
簡言之,在這裏咱們使用viewport
是爲了控制屏幕的縮放。
var dpr = window.devicePixelRatio;
meta.setAttribute('content', 'initial-scale=' + 1/dpr + ', maximum-scale=' + 1/dpr + ', minimum-scale=' + 1/dpr + ', user-scalable=no');
// 幫助理解 若是dpr=2,說明寫的100px渲染成了200px,因此須要縮小至1/2,即1/dpr
複製代碼
另外值得一提的是,UI通常會以750px * 1334px
的標準進行設計,由於這樣使得設計稿更加精細。 好比咱們寫了rem2px(375px)
,那麼會通過下列的過程換算到設備pixels寬度爲390px且dpr=3的手機。
rem2px(375px)
----> 5rem
5rem
-----> 195px (樣式pixels)
樣式195px
------> 此時看起來(指的就是設備pixels)有195*3 = 585px
的長度設置dpr=1/3
------->此時看起來只有195px
這樣,咱們完成了從dpr
維度的適配。
<script>
var dpr = window.devicePixelRatio;
var meta = document.createElement('meta');
// dpr
meta.setAttribute('content', 'initial-scale=' + 1/dpr + ', maximum-scale=' + 1/dpr + ', minimum-scale=' + 1/dpr + ', user-scalable=no');
document.getElementsByTagName('head')[0].appendChild(meta);
// rem
document.addEventListener('DOMContentLoaded', function (e) {
document.getElementsByTagName('html')[0].style.fontSize = window.innerWidth / 10 + 'px';
}, false);
</script>
複製代碼
爲了防止全局變量污染或者覆蓋他人的變量,請封裝成模塊再使用。
在寫這篇博客的過程當中,曾糾結過這樣的問題:rem佈局和百分比佈局感受差距不大啊,由於在寫rem的時候是基於把寬度切爲10份後再寫的,就像是1rem = 10% = 10vw
同樣。這讓我一度以爲能夠用百分比佈局。後來發現,若是出現盒子嵌套(這種場景太多了),那麼百分比佈局就出現問題了,由於其百分比的參考系選擇的是父元素,因此咱們若是在子盒子裏面定義10%
的寬度,指的是針對父盒子
的而不是咱們想要的針對整個window.innerWidth
的10%
。而vw
的代碼可維護性不如上述的這套方案,且兼容性也沒有rem
好(這一點差距不是太大)。
若是想了解更多關於PC端or移動端佈局,請看參考資源&鳴謝
板塊。