移動端適配不會,千萬別點進來

夢想攻城獅發表於2018-10-08

鄙人也偶爾做過移動端的開發,對於移動端適配剛開始就被幾個專用的名詞給嚇的不知所措,現在來看看這幾個名詞的真面目

物理畫素、邏輯畫素

  • 物理畫素(裝置畫素):顯示裝置中一個最微小的物理部件。螢幕這塊物理材料上橫向和縱向共有多少個畫素點,任何裝置螢幕的物理畫素出廠時就確定了,且固定不變
  • 邏輯畫素(裝置獨立畫素):程式使用的虛擬畫素(比如說CSS畫素),程式認為橫向和縱向有多少個畫素點,然後由相關係統轉換為物理畫素
iPhone5 iPhone6 iPhone8Plus
邏輯畫素 568x320 667x375 736x414
物理畫素 1136x640 1334x750 2208x1242

高清屏和畫素比

畫素點

圖片是由很多的畫素點構成,每個畫素點顯示的色彩不一樣,構成了一副圖片。你可以想象把蒙娜麗莎放大很多倍,然後裁成大小相同的小塊,然後拼在一起,這個就是畫素點

高清屏

同樣大小的圓,裡面容納的正多邊形的邊數越多,其形狀越接近圓,面積越接近圓。類似相同尺寸的螢幕,一個電容點所容納的畫素點越多,一個點可以由更多的畫素點搭配來成像則效果當然更細膩,螢幕顯得更清晰。

畫素比

裝置畫素比簡稱為dpr,其定義了物理畫素和裝置獨立畫素的對應關係,它決定了一個點是由多少個畫素點來描繪。所以在開發的時候才會有@1、@2和@3圖片區別。

iPhone5 iPhone6 iPhone8Plus
邏輯畫素 568x320 667x375 736x414
物理畫素 1136x640 1334x750 2208x1242
螢幕倍率 @2 @2 @3
dpr 2 2 3

視口viewport

  • 佈局視口(layout):在html中一般在meta中的name為viewport欄位就是控制的佈局視口。佈局視口一般都是瀏覽器廠商給的一個值,移動端在不設定的情況下為980px。
document.documentElement.clientWidth(clientHeight) // 佈局視口的尺寸。
複製程式碼
  • 視覺視口(visiual):瀏覽器可視區域的大小,即使用者看到的網頁的區域。(其寬度繼承的layout寬度)
window.innerWidth(innerHeight)  // 視覺視口尺寸
複製程式碼
  • 理想視口(ideal):layout-viewport = width.screen.width(邏輯畫素橫向)。
<meta name="viewport" content="width=device-width">
複製程式碼

由於以前的PC頁面尺寸比較小,手機在沒有進行viewport設定時預設的時980px,可以相容大多數情況,部分頁面頁面顯示太小,部分頁面顯示不下而出現滾動條。所以為了達到理想視口,將layout-viewport設定和手機邏輯畫素一樣大小。但是由於高清屏,設計稿一般和手機物理畫素一致,如果設定layout-viewport為邏輯畫素,手機程式只會用邏輯尺寸長度去佈局,那麼橫向只能顯示正常設計稿的1/2或1/3,就會出現滾動條。

1px問題

在理想視口的情況下,layout-viewport的尺寸大小是邏輯尺寸大小,所以CSS中的1px就是邏輯尺寸的1px,在顯示的時候,手機會根據dpr對其進行轉化,導致CSS中的1px其實對應的兩倍屏2px,三倍屏的3px,所以導致1px的線在這種情況下會變粗。

  1. IOS解決方案
.border { border: 1px solid #999 }
@media screen and (-webkit-min-device-pixel-ratio: 2) {
    .border { border: 0.5px solid #999 }
}
@media screen and (-webkit-min-device-pixel-ratio: 3) {
    .border { border: 0.333333px solid #999 }
}
複製程式碼
  1. :before, :after與transform(相容)
.radius-border{
    position: relative;
}
@media screen and (-webkit-min-device-pixel-ratio: 2){
    .radius-border:before{
        content: "";
        pointer-events: none; /* 防止點選觸發 */
        box-sizing: border-box;
        position: absolute;
        width: 200%;
        height: 200%;
        left: 0;
        top: 0;
        border-radius: 8px;
        border:1px solid #999;
        -webkit-transform(scale(0.5));
        -webkit-transform-origin: 0 0;
        transform(scale(0.5));
        transform-origin: 0 0;
    }
}
複製程式碼
  1. viewport縮放
//dpr為2時,安卓下通過flexible.js動態設定無效,initial-scale=0.5始終預設為1
<meta name="viewport" content="initial-scale=0.5, maximum-scale=0.5, minimum-scale=0.5, user-scalable=no">
複製程式碼

適配方案

從1px的適配中,可以知道viewport縮放是為了消除高清屏倍率的影響。所以一般適配主要從兩方面著手:

  • 利用viewport進行縮放來消除高清屏倍率的問題
  • 利用rem來消除高清屏倍率的問題
  • 利用rem調整不同裝置之間的邏輯尺寸不一致的問題

固定viewport為理想視口

  1. 固定高度,寬度自適應:
<meta name="viewport" content="width=device-width,initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
複製程式碼

垂直方向使用固定的值,水平方向使用彈性佈局,元素採用定值、百分比、flex佈局等。這種方案相對簡單,還原設計稿程度較低,尺寸一般都採用百分比和固定值,不能跟隨裝置變化而調整,高清屏的設計稿尺寸要縮小1/2或1/3。

  1. 根據不同螢幕動態寫入font-size,以rem作為寬度單位。
var width = document.documentElement.clientWidth;   // 螢幕的佈局視口寬度
var rem = width / 7.5;  //750px設計稿將佈局視口分為7.5份 
rem = px * 0.01;    //1rem等於設計稿上的100px

在ipone6上: 
width = document.documentElement.clientWidth = 375px;
rem = 375px / 7.5 = 50px; 
0.75rem = 37.5px; (37.5/375=10%;佔螢幕10%) 
在任意其他機型上:
width = document.documentElement.clientWidth = 420px;
rem = (375px / 7.5)*(420/375) = 420/7.5 = 56px; 
0.75rem = 42px = (420/375)*37.5px 
複製程式碼

利用rem來消除高清屏倍率的問題,利用rem調整不同裝置之間的邏輯尺寸不一致的問題,利用百分比還原設計圖,但是無法消除1px的影響

動態設定viewport

  1. 根據不同螢幕動態寫入font-size和viewport,以rem作為寬度單位。
var width = document.documentElement.clientWidth;   // 螢幕的佈局視口寬度
var rem = width / 10;                               // 將佈局視口分為10份

var devicePixelRatio = window.devicePixelRatio;
var isIPhone = window.navigator.appVersion.match(/iphone/gi);
var dpr,scale; 
if (isIPhone) { 
    if (devicePixelRatio >=3) { 
    dpr = 3; 
    } else if (devicePixelRatio >=2) { 
    dpr = 2; 
    } else { 
    dpr = 1; 
    } 
} else { 
    dpr = 1; 
} 
scale = 1 / dpr; 

在ipone6上: 
width = document.documentElement.clientWidth = 750px;
rem = 750px / 10 = 75px; 
1rem = 75px; 
在任意其他機型上:
width = document.documentElement.clientWidth = 840px;
rem = (840px / 10)*(420/375) = 420/7.5 = 94.08px; 
1rem = 94.08px = (420/375)*75px   
複製程式碼

利用viewport進行縮放來消除高清屏倍率的問題,能消除1px的問題,利用rem調整不同裝置之間的邏輯尺寸不一致的問題,利用百分比還原設計圖,但是其1rem等於75px或者其他不固定尺寸不方便計算。

  1. vw和viewport放縮
var devicePixelRatio = window.devicePixelRatio;
var isIPhone = window.navigator.appVersion.match(/iphone/gi);
var dpr,scale; 
if (isIPhone) { 
    if (devicePixelRatio >=3) { 
    dpr = 3; 
    } else if (devicePixelRatio >=2) { 
    dpr = 2; 
    } else { 
    dpr = 1; 
    } 
} else { 
    dpr = 1; 
} 
scale = 1 / dpr; 

100vw = 100%width 
複製程式碼

這種方式類似於rem = 750px / 10 的方案,只不過用vw來替代了rem,但是它有一個好處就是將rem解放出來,所以rem用來設定字型的大小,在移動端可以相容使用者設定字型大小的影響。

  1. 根據不同螢幕動態寫入font-size和viewport,以rem作為寬度單位。為了消除這種不利的影響將上面進行修改:
var width = document.documentElement.clientWidth;   // 螢幕的佈局視口寬度
var rem = width / 7.5;  //750px設計稿將佈局視口分為7.5份 
rem = px * 0.01;    //1rem等於設計稿上的100px

var devicePixelRatio = window.devicePixelRatio;
var isIPhone = window.navigator.appVersion.match(/iphone/gi);
var dpr,scale; 
if (isIPhone) { 
    if (devicePixelRatio >=3) { 
    dpr = 3; 
    } else if (devicePixelRatio >=2) { 
    dpr = 2; 
    } else { 
    dpr = 1; 
    } 
} else { 
    dpr = 1; 
} 
scale = 1 / dpr; 

在ipone6上: 
width = document.documentElement.clientWidth = 750px;
rem = 750px / 7.5 = 100px; 
0.75rem = 75px; 
在任意其他機型上:
width = document.documentElement.clientWidth = 840px;
rem = (750px / 7.5)*(840/750) = 840/7.5 = 56px; 
0.75rem = 84px = (840/750)*75px 
複製程式碼

利用viewport進行縮放來消除高清屏倍率的問題,能消除1px的問題,利用rem調整不同裝置之間的邏輯尺寸不一致的問題,利用百分比還原設計圖,方便計算。

結語

終於揭開了移動端佈局的面紗,錯誤的地方望指正。

相關文章