屏幕適配,一直是做爲一個前端開發始終逃不掉的問題,這個話題能夠追溯到最開始的PC端瀏覽器的不一樣分辨率,再到移動端不一樣的屏幕尺寸,一直伴隨着前端工程師的平常的頁面開發工做。所謂屏幕適配,能夠理解爲一個網頁元素或者網頁佈局,在不一樣尺寸,分辨率等場景下,如何呈現最佳的效果。 從最先的PC端屏幕來講,大部分的屏幕適配採起的是:javascript
隨着HTML5和CSS3的到來,逐漸出現了彈性佈局(flex佈局),媒體查詢Media Query,和響應式頁面概念,這些特性均可以應用在PC端以及移動端屏幕適配解決方案中。除了這些以外,還有rem和vw方案更加有針對性的解決移動web頁面的適配問題。css
在HTML代碼的<head>
標籤中,都有一行設置的代碼,以下:html
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
複製代碼
這行代碼的做用就是設置瀏覽器的視窗大小,具體的含義咱們後面在介紹,在講解視窗以前,咱們首先須要瞭解一下什麼是物理像素和CSS像素。前端
像素,也就是px,實際是pixel的縮寫,它是圖像顯示的基本單元,每一個像素能夠有色彩數值和位置,每一個圖像是由若干個像素組成,好比對一幅標有1024×768像素的圖像,就代表這幅圖像的長邊有1024個像素,寬邊有768個像素,共有1024×768=786432個像素組成。可是從概念上來講,像素既不是一個肯定的物理量,也不是一個點或者小方塊,而是一個抽象概念。因此像素所表明的具體含義要從其處於的上下文環境來具體分析。物理像素和CSS像素就是不一樣的上下文。java
window.devicePixelRatio
獲取到。其實對於早期PC端web頁面來講,在的CSS裏寫個1px,屏幕就給你渲染成1個實際的像素點,此時的設備像素比是1,這時物理像素和CSS像素是同樣的。可是對於一些高清屏,例如蘋果的retina屏幕,這種屏幕使用2個或者3個物理像素來渲染1個CSS像素,因此這些屏幕的顯示效果要清晰不少。例以下圖a表明物理像素,b表明CSS像素,它們之間的關係如圖下圖所示。 web
能夠想象一下,一個傳統的PC端web頁面,若是想要徹底放在手機端使用瀏覽(能夠想象成把PC端顯示器替換成手機屏幕),必定是放不下的,而這時就須要對頁面進行縮放,那麼對頁面進行放大和縮小,其實就是改變像素比,例以下圖,用4個CSS像素和4個物理像素來模擬放大和縮小。 在頁面處於正常狀態時,4個物理像素的區域須要4個CSS像素恰好展現完,當頁面縮小時,本來4個物理像素須要大於4個CSS像素才能顯示完這片區域,而當頁面放大時,本來4個物理像素須要小於4個CSS像素就能夠顯示完,或者說是4個CSS像素可以放下更多於4個物理像素的位置。這就實現了頁面的放大和縮小,而對於HTML而言,控制放大和縮小的就是視窗Viewport。在瞭解了物理像素和CSS像素的概念以後,而後就須要引入下一個概念,移動設備中的視窗,視窗就是瀏覽器顯示頁面內容的屏幕區域,有3種不一樣的類別,主要分爲:前端工程化
<meta>
標籤設置viewport來修改。每一個瀏覽器默認都會有一個設置,例如iOS,Android這些機型設置佈局視窗寬度爲980px,因此PC上的網頁基本能在手機上呈現,只不過元素看上去很小,通常能夠經過手指動雙擊縮放網頁。以下圖,能夠表示物理視窗和佈局視窗的關係,底部的網頁大小至關於佈局視窗,而半透明灰色區域表示物理視窗大小,看起來就像一個手機屏幕大小。 瀏覽器
因此若是想要在物理視窗裏面徹底展現佈局視窗裏的內容,確定要將頁面縮小。那麼縮小到多少合適呢,就須要有理想視窗,以下圖所示。對於移動端web頁面,能夠採用<meta>
標籤對視窗的大小,縮放等進行配置,也就是以前提到的在<head>
標籤內設置的<meta>
的代碼以下:bash
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
複製代碼
其中,能夠配置的屬性含義以下:前端工程師
在使用<meta>
標籤設置viewport時有幾點須要注意,首先viewport只對移動端瀏覽器有效,對PC端瀏覽器是無效的,其次對於移動端瀏覽器,某些屬性也並非徹底支持,例如對於iOS的Safari瀏覽器,從10.0版本開始將不在支持user-scalable=no,因此即便設置了user-scalable=no,用戶依然能夠對頁面進行手勢操做來縮放。若是依然須要禁用,能夠參考以下代碼:
window.onload = function () {
document.addEventListener('touchstart', function(event) {
// 當兩個手指操做
if (event.touches.length > 1) {
// 組織瀏覽器默認事件
event.preventDefault();
}
});
var lastTouchEnd = 0;
document.addEventListener('touchend', function(event) {
var now = (new Date()).getTime();
// 判斷是不是雙擊操做,即兩次點擊間隔小於300ms
if (now - lastTouchEnd <= 300) {
// 組織瀏覽器默認事件
event.preventDefault();
}
lastTouchEnd = now;
}, false);
}
複製代碼
經過手勢來進行縮放是屬於瀏覽器的默認功能,上面代碼的原理就是利用event.preventDefault()
方法,來禁用瀏覽器的默認事件,這樣就不能觸發這個默認的縮放功能。具體邏輯能夠將代碼運行以後看一下效果。 Viewport視窗的相關知識點是瞭解移動web適配的基礎,經過動態的設置viewport能夠實現不一樣屏幕下的頁面適配,例如對設備像素比不爲1的機型進行縮放,強制讓物理像素和CSS像素相等,代碼以下:
(function(){
var scale = 1/window.devicePixelRatio;
var meta = document.createElement("meta");
meta.name = "viewport";
meta.content = "width=device-width,initial-scale="+scale+",minimum-scale="+scale+",maximum-scale="+scale;
document.head.appendChild(meta);
})();
複製代碼
這種方法有時候不許確,好比devicePixelRatio不爲整數的時候,會出現除不盡的狀況,那縮放的倍數就會出現很長的小數,再去算物理像素的時候就會有偏差,因此如今大部分移動web頁面採用更加完善的rem或者vw加flex的方案來進行適配。
Rem適配方案是當下流行而且兼容性最好的移動端適配解決方案,它支持大部分的移動端系統和機型,Rem其實是一個字體單位,即rem(font size of the root element)是指相對於根元素的字體大小的單位,簡單的說它就是一個相對單位。看到rem你們必定會想起em單位,em(font size of the element)是指相對於父元素的字體大小的單位。它們之間其實很類似,只不過一個計算的規則是依賴根元素一個是依賴父元素計算。 因此Rem適配方案的適配原理就是:將咱們以前寫px的單位換成rem單位,而後根據屏幕大小動態設置根元素<html>
的font-size大小,那麼只要跟元素的font-size改變,對應的元素的大小就會改變,從而達到在不一樣屏幕下的適配的目的。
使用瀏覽器瀏覽網頁時,網頁中的字體大小由根元素<html>
來決定,而<html>
的字體大小由瀏覽器自己決定,在不修改瀏覽器默認字體狀況下是16px,即默認狀況下1rem = 16px,可是若是採用Rem的適配方案就須要動態設置<html>
的font-size。通常狀況下是根據屏幕的寬度來動態設置,即採用屏幕寬度來識別不一樣的機型,以達到對不一樣機型的適配,具體有兩種方案來設置,第一種是採用媒體查詢(Media Query),代碼以下:
@media screen and (min-width:461px){
html{
font-size:18px;
}
}
@media screen and (max-width:460px) and (min-width:401px){
html{
font-size:22px;
}
}
@media screen and (max-width:400px){
html{
font-size:30px;
}
}
複製代碼
上面代碼中,使用screen媒體特性,來定義了3組屏幕寬度區間,當小於400px,大於401px且小於460px,大於461px,當屏幕寬度位於不一樣的區間時,則會應用上對應的<html>
的font-size。 另一種則是使用Javascript動態設置<html>
的font-size,代碼以下:
// 獲取屏幕視窗寬度
let htmlWidth = document.documentElement.clientWidth || document.body.clientWidth;// 獲取寬度最好有個兼容的方案,避免某些狀況下第一種獲取不到能夠選擇第二種
//獲取html
let htmlDom = document.getElementsByTagName('html')[0];
htmlDom.style.fontSize = htmlWidth / 10 + 'px'; //求出font-size
複製代碼
上面代碼中,獲得屏幕寬度後,通常要除以一個係數,這裏使用的係數是10,這樣獲得的font-size值更加靈活,適配性更強,因此實際應用當中,大多數採用的JavaScript來動態設置。若是想要實時監聽屏幕大小的變化動態修改font-size,能夠引入resize事件,代碼以下:
window.addEventListener('resize',function(){
/*上面設置font-size的代碼*/
})
複製代碼
設置完font-size以後,就能夠直接利用rem單位來給咱們的div或者其餘元素設置寬高等等的屬性了,這裏就有一個問題,咱們通常拿到的UI稿都會提供標註,這些標註通常會標識出某個元素例如按鈕,圖片具體大小數值,單位是px,而且整個UI稿都會基於一個具體的移動設備,例如iPhone6s等,能夠參考下圖所示。
那麼,咱們如何根據視覺稿上的px單位值轉換成對應的rem單位值呢?這裏舉一個例子,一個按鈕在視覺搞上標註的大小是:寬200px,高400px,那麼咱們根據這個來進行以下計算:<html>
的font-size獲得是37.5px,這裏37.5px稱作rem的基準值,下面的計算會用。根據上面的方法,咱們就能夠給按鈕元素設置rem單位了,代碼以下:
.button {
width: 5.3rem;
height: 10.6rem;
font-size:0.53rem;
background-color: red;
}
複製代碼
咱們給一個元素採用了rem單位來設置了寬高,那麼這個元素在不一樣機型中顯示時,因爲設置的根元素<html>
的font-size大小不同,那麼rem所實際渲染出來的大小也就不同,能夠比較一下分別在Chrome開發者工具中的Device Mode中採用iPhone6s和iPhone6P運行的效果區別,如圖下圖所示。
@function px2rem($px){
$rem: 37.5;
@return ($px/$rem) + rem;// $px表示變量,+號表示拼接,rem爲字符串至關於'rem'
}
.button {
width: px2rem(200);
height: px2rem(400);
font-size: px2rem(20);
background-color: red;
}
複製代碼
固然,上面的代碼已經不是一個標準的CSS代碼了,而是一個Sass語言的CSS代碼,不過沒有學過Sass也沒有關係,咱們只會用到Sass的不多一部分知識點。 上面代碼中,定義了一個方法,方法名爲px2rem,這個方法接收一個參數就是將要轉換的px值,而後根據rem基準值來計算。當在給元素設置寬高時,調用這個方法即px2rem(200),將須要轉換的px值做爲參數傳遞進去,這樣通過編譯後,最終獲得的就是rem單位的值了即width: px2rem(200)轉換成了width:5.3rem。 總結下來,使用Rem適配方案主要有如下幾點須要注意:
<html>
的font-size,這段腳本通常放置在<head>
標籤裏面,讓font-size更早的設置,可讓適配更早的生效。<html>
的font-size設置不許確。另外就是一些小說網站,屏幕越小的移動設備若是用了rem單位就會致使文字就越小,就會致使看文章的時候特別費眼。vw其實也是一個CSS單位,相似的還有vh,vmin,vmax共四個單位,這些單位伴隨着CSS3的出現就已經有了,可是當時移動web的浪潮已經來臨,而且Rem出現的要早一些,因此不少開發人員對此並不熟悉。 和Rem適配方案相比,vw適配方案不須要使用JavaScript腳原本提早設置font-size,vw適配方案徹底基於CSS自身,這也是相對於Rem適配方案的優點所在,而且對於橫豎屏切換較爲頻繁的頁面時能夠採用vmin單位,更加靈活。咱們先來了解一下vw,vh,vmin,vmax這幾個單位,含義以下:
從上面的解釋能夠看出,vw和vh這些單位也並非一個固定的值,而是根據視口寬度或者高度而變。那麼什麼是視口呢?還記得以前講解的viewport嗎,經過標籤:
<meta name="viewport" content="width=device-width">
複製代碼
設置的這個寬度就是視口寬度,而且能夠經過JavaScript中的document.documentElement.clientWidth
或者document.body.clientWidth
獲取到這個值,這裏就和前面講解Rem適配方案時獲取屏幕寬度時的用法是同樣的。 有些同窗會遇到例如window.innerWidth
或者window.screen.width
來獲取屏幕或者視口的寬度,這種方法獲取到的通常是設備的物理寬度,例如真實的分辨率或者物理像素值,這個和視口寬度不必定相等,當<meta>
標籤設置viewport時,若是width=!device-width時,這種狀況下就是不相等的,因此各位在使用時仍是須要注意一下。
對於vw適配方案,也是須要計算vw值的,同理咱們仍是以iPhone6的UI稿爲例子,例如一個按鈕在視覺搞上標註的大小是寬200px,高400px,那麼咱們根據這個來作以下計算:
根據上面的方法,咱們就能夠給按鈕元素設置vw單位了,代碼以下:
.button {
width: 53vw;
height: 106vw;
background-color: red;
}
複製代碼
和計算rem值同理,也能夠利用Sass來聲明一個方法,作px到vw的轉換,代碼以下:
@function px2vw($px) {
$vw: 3.75;
@return ($px/$vw)+vw;// $px表示變量,+號表示拼接,vw爲字符串至關於'vw'
}
.button {
width: px2vw(53);
height: px2vw(106);
background-color: red;
}
複製代碼
不管是轉換成rem值仍是vw值,在後續的實戰項目中,均可以經過另外一種方式來更加方便的轉換。例如能夠經過構建的方式,在代碼中只須要寫px值,經過配置一些插件和工具,最終生成的項目中就是轉換好的代碼,這就是前端工程化帶來的便利。
從上面的對兩種相關的適配方案講解,能夠知道vw適配方案要優於Rem適配方案的,可是沒有Rem流行就在於vw的兼容性問題,咱們從caniuse[ caniuse是一個當下流行的前端技術兼容性查詢網站,地址是:www.caniuse.com/]網站中查詢到兼容性以下。
Rem適配方案在主流瀏覽器中總體支持性98.93%,而vw適配方案在主流瀏覽器中總體支持性94.44%,而且對於Android4.4以前的機型來講vw不支持是硬傷,畢竟這部分機型的市場佔有率仍是有一部分的。因此各位在選取適配方案時,要根據本身業務的場景來選擇合適的方案,避免出現兼容性問題。更多關於移動web相關的內容,歡迎關注課程: 《從0到1 實戰朋友圈移動Web App開發》 《移動Web App開發之實戰美團外賣》