移動端適配方案

  • 移動端適配方案:
    1)viewport(scale=1/dpr)
    2)rem
    3)flex
    4)vm/vh
    1、什麼是移動端適配
  • 移動端Web頁面,即常說的H5頁面、手機頁面、webview頁面;
  • 手機設備屏幕尺寸不一,作移動端的Web頁面,須要考慮安卓/IOS的各類尺寸設備上的兼容,針對移動端設備的頁面,設計與前端實現怎樣作能更好地適配不一樣屏幕寬度的移動設備;
  • 目的:在不一樣尺寸的手機設備上,頁面「相對性的達到合理的展現(自適應)」或者「保持統一效果的等比縮放(看起來差很少)」;

2、viewport視口css

瀏覽器可見視口和佈局視口html

 

  • visual viewport:可見視口,即屏幕寬度;
  • layout viewport:佈局視口,即DOM寬度;
  • idea viewport:理想適口,使佈局視口就是可見視口;
  • 設備寬度(visual viewport)與DOM寬度(layout viewport),scale的關係是:
    (visual viewport)= (layout viewport)* scale
  • 獲取屏幕寬度(visual viewport)的尺寸:window. innerWidth/Height
  • 獲取DOM寬度(layout viewport)的尺寸:document. documentElement. clientWidth/Height
  • 完美適配:首先不須要用戶縮放和橫向滾動條就能正常的查看網站的全部內容;第二,顯示的文字的大小合適,好比一段14px大小的文字,不會由於在一個高密度像素的屏幕裏顯示得過小而沒法看清,理想的狀況是這段14px的文字不管是在何種密度屏幕,何種分辨率下,顯示出來的大小都是差很少的。固然,不僅是文字,其餘元素像圖片什麼的也是這個道理,ppk把這個viewport叫作 ideal viewport,也就是第三個viewport——移動設備的理想viewport;
  • 移動設備默認的viewport是layout viewport,也就是那個比屏幕要寬的viewport,但在進行移動設備網站的開發時,咱們須要的是ideal viewport。那麼怎麼才能獲得ideal viewport呢?這就該輪到meta標籤出場了;meta標籤的做用是讓當前viewport的寬度等於設備的寬度,同時不容許用戶手動縮放。也許允不容許用戶縮放不一樣的網站有不一樣的要求,但讓viewport的寬度等於設備的寬度,這個應該是你們都想要的效果,若是你不這樣的設定的話,那就會使用那個比屏幕寬的默認viewport,也就是說會出現橫向滾動條;
    這個name爲viewport的meta標籤到底有哪些東西呢,又都有什麼做用呢?

meta viewport 標籤首先是由蘋果公司在其safari瀏覽器中引入的,目的就是解決移動設備的viewport問題。後來安卓以及各大瀏覽器廠商也都紛紛效仿,引入對meta viewport的支持,事實也證實這個東西仍是很是有用的。前端

在蘋果的規範中,meta viewport 有6個屬性(暫且把content中的那些東西稱爲一個個屬性和值),以下:
width:設置layout viewport 的寬度,爲一個正整數,或字符串"width-device";
initial-scale:設置頁面的初始縮放值,爲一個數字,能夠帶小數;
minimum-scale:容許用戶的最小縮放值,爲一個數字,能夠帶小數;
maximum-scale:容許用戶的最大縮放值,爲一個數字,能夠帶小數;
height:設置layout viewport 的高度,這個屬性對咱們並不重要,不多使用;
user-scalable:是否容許用戶進行縮放,值爲"no"或"yes", no 表明不容許,yes表明容許;android

  • 設置理想視口:把默認的layout viewport的寬度設爲移動設備的屏幕寬度,獲得理想視口(ideal viewport):

 

<meta name="viewport" content="width=device-width,initial-scale=1">

參考:移動前端開發之viewport的深刻理解git

3、物理像素(physical pixel)github

  • 物理像素又被稱爲設備像素,他是顯示設備中一個最微小的物理部件,每一個像素能夠根據操做系統設置本身的顏色和亮度,所謂的一倍屏、二倍屏(Retina)、三倍屏,指的是設備以多少物理像素來顯示一個CSS像素,也就是說,多倍屏以更多更精細的物理像素點來顯示一個CSS像素點,在普通屏幕下1個CSS像素對應1個物理像素,而在Retina屏幕下,1個CSS像素對應的倒是4個物理像素,關於這個概念,看一張「田」字示意圖就會清晰了;

4、CSS像素web

  • CSS像素是一個抽象的單位,主要使用在瀏覽器上,用來精確度量WEB頁面上的內容,通常狀況下,CSS像素稱爲與設備無關的像素(device-independent pixel),簡稱DIPs,CSS像素顧名思義就是咱們寫CSS時所用的像素;

做爲Web開發者,咱們接觸的更多的是用於控制元素樣式的樣式單位像素。這裏的像素咱們稱之爲CSS像素
參考:CSS像素、物理像素、邏輯像素、設備像素比、PPI、Viewport #21
5、設備像素比segmentfault

  • 設備像素比dpr(device pixel ratio):設備像素比簡稱dpr,其定義了物理像素和設備獨立像素的對應關係,它的值能夠按下面的公式計算獲得:
  • 設備像素比=物理像素/設備獨立像素
  • Retina屏的iphone上,devicePixelRatio的值爲2,也就是說1個css像素至關於2個物理像素,一般所說的二倍屏(Retina)的dpr是2,三倍屏是3;
  • viewport中的scale和dpr是倒數關係:

 

// 獲取當前設備的dpr:
JavaScript: window.devicePixelRatio。
CSS: -webkit-device-pixel-ratio, -webkit-min-device-pixel-ratio, -webkit-max-device-pixel-ratio。不一樣dpr的設備,可根據此作一些樣式適配(這裏只針對webkit內核的瀏覽器和webview)。

6、設備獨立像素dip與設備像素dp瀏覽器

  • 設備獨立像素dip(device independent pixels):與屏幕密度有關,dip能夠用來輔助區分視網膜設備仍是非視網膜設備;
  • 設備像素dp(device pixels)
  • 安卓設備根據屏幕像素密度可分爲ldpi、mdpi、hdpi、xhdpi等不一樣的等級,規定以160dpi爲基準,1dp=1px;若是密度爲320dpi,則1dp=2px,以此類推;
  • IOS設備:從iphone4開始Retina屏;
  • CSS像素與設備獨立像素之間的關係依賴於當前的縮放等級;

7、屏幕像素密度PPI(pixel per inch)sass

  • 屏幕像素密度是指一個設備表面上存在的像素數量,它一般以每英寸有多少像素來計算(PPI),屏幕像素密度與屏幕尺寸和屏幕分辨率有關,在單一變化條件下,屏幕尺寸越小、分辨率越高,像素密度越大,反之越小;
  • 屏幕密度=對角線分辨率/屏幕尺寸

8、概念關係圖

 

屏幕尺寸、屏幕分辨率-->對角線分辨率/屏幕尺寸-->屏幕像素密度PPI
                                             |
              設備像素比dpr = 物理像素 / 設備獨立像素dip(dp)
                                             |
                                       viewport: scale
                                             |
                                          CSS像素px

CSS像素與設備像素「田字圖解」

9、前端實現相關方式

  • viewport 設置理想視口

 

// 設置理想視口,使得DOM寬度(layout viewport)與屏幕寬度(visual viewport)同樣大,DOM文檔主寬度即爲屏幕寬度,1個CSS像素(1px)由多少設備像素顯示由具體設備而不一樣;
<meta name="viewport" content="width=device-width,user-scalable=no,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0">

10、動態設置視口縮放爲1/dpr

  • 對於安卓,全部設備縮放設爲1,對於IOS,根據dpr不一樣,設置其縮放爲dpr倒數,設置頁面縮放可使得1個CSS像素(1px)由1個設備像素來顯示,從而提升顯示精度;所以,設置1/dpr的縮放視口,能夠畫出1px的邊框;
  • 無論頁面中有沒有設置viewport,若無,則設置;如有,則改寫,設置其scale爲1/dpr;

 

(function (doc, win) {
  var docEl = win.document.documentElement;
  var resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize';
  var metaEl = doc.querySelector('meta[name="viewport"]');
  var dpr = 0;
  var scale = 0;

  // 對iOS設備進行dpr的判斷,對於Android系列,始終認爲其dpr爲1
  if (!dpr && !scale) {
    var isAndroid = win.navigator.appVersion.match(/android/gi);
    var isIPhone = win.navigator.appVersion.match(/[iphone|ipad]/gi);
    var devicePixelRatio = win.devicePixelRatio;

    if(isIPhone) {
      dpr = devicePixelRatio;
    } else {
      drp = 1;
    }
    
    scale = 1 / dpr;
  }

  /**
    * ================================================
    *   設置data-dpr和viewport
    × ================================================
    */

  docEl.setAttribute('data-dpr', dpr);
  // 動態改寫meta:viewport標籤
  if (!metaEl) {
    metaEl = doc.createElement('meta');
    metaEl.setAttribute('name', 'viewport');
    metaEl.setAttribute('content', 'width=device-width, initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
    document.documentElement.firstElementChild.appendChild(metaEl);
  } else {
    metaEl.setAttribute('content', 'width=device-width, initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
  }

})(document, window);

11、px單位的適配
設置動態縮放視口後,在iphone6上,縮放爲0.5,即CSS像素2px最終顯示效果爲1px,而在scale=1的設備,CSS像素1px顯示效果爲1px,爲了達到顯示效果一致,以px爲單位的元素(好比字體大小),其樣式應有適配不一樣dpr的版本,所以,在動態設置viewport:scale的時候,同時在根元素上加上data-dpr=[dpr],可是這種方式仍是不夠,若是dpr爲2,3以外的其餘數值,px就沒辦法適配到,所以會選擇用rem爲單位進行適配;

 

.p {
    font-size: 14px;

  [data-dpr="2"] & {
    font-size: 14px * 2;
  }

  [data-dpr="3"] & {
    font-size: 14px * 3;
  }
}

 

// 爲寫樣式方便,能夠藉助sass的mixin寫代碼片斷:
// // 適配dpr的字體大小
@mixin font-dpr($font-size){
  font-size: $font-size;

  [data-dpr="2"] & {
      font-size: $font-size * 2;
  }

  [data-dpr="3"] & {
      font-size: $font-size * 3;
  }
}
@mixin px-dpr($property, $px) {
  #{$property}: $px;

  [data-dpr="2"] & {
    #{$property}: $px * 2;
  }

  [data-dpr="3"] & {
    #{$property}: $px * 3;
  }
}

// 使用
@include font-dpr(14px);
@include px-dpr(width, 40px); @include px-dpr(height, 40px);

12、設置縮放視口與設置理想視口有什麼不一樣

  • 問題:viewport設爲理想視口(scale=1),基本上已經知足適配,爲何要動態設置viewport縮放?
  • 緣由:iphone6爲例,dpr=2,縮放設爲0.5,則DOM寬度爲750,縮放後顯示恰好屏幕寬度爲375,而總的CSS像素實際上是750,與設備像素一致,這樣1px的CSS像素,佔用的物理也是1;而viewport設置縮放爲1的理想視口狀況下,DOM寬度爲375,顯示也恰好是屏幕寬度,然而1px的CSS像素,佔用的物理像素是2,這樣說來,這樣設置能夠實現1px的線條在二倍屏的顯示,由於:CSS像素與設備像素的關係依賴於屏幕縮放;
  • 驗證:設備:iphone6
    在scale=0.5時,1px邊框顯示效果;
    在scale=1.0時,1px邊框顯示效果;
    在scale=0.5時,2px邊框顯示效果;
    經過對比後發現,在scale=0.5時,1px的線比scale=1.0要細,這也就解決了1px線條的顯示問題;

十3、rem(一個css單位)

  • 定義:font size of the root element,這個單位的定義和em相似,不一樣的是em是相對於父元素,而rem是相對於根元素,rem定義是根元素的font-size,以rem爲單位,其數值與px的關係,需相對於根元素<html>的font-size計算,好比設置根元素font-size=16px,則表示1rem=16px;
  • 根據這個特色,能夠根據設備寬度動態設置根元素的font-size,使得以rem爲單位的元素在不一樣終端上以相對一致的視覺效果呈現;
  • 選取一個設備寬度做爲基準,設置其根元素大小,其餘設備根據此比例計算其根元素大小,好比使得iphone6根元素font-size=16px;
設備 設備大小 根元素font-size/px 寬度/rem
iPhone5/SE 320*568 js計算所得 --
iPhone6/7/8 375*667 16 23.4375
i6/7/8 Plus 414*736 js計算所得 --
iPhone x 375*812 js計算所得 --
iPad 768*1024 js計算所得 --
- 360 js計算所得 --
  • 根元素fontSize公式:width/fontSize = baseWidth/baseFontSize;
    其中,baseWidth, baseFontSize是選爲基準的設備寬度及其根元素大小,width, fontSize爲所求設備的寬度及其根元素大小;

十4、動態設置根元素fontSize

 

// 以320px的屏幕爲基準
const oHtml = document.getElementsByTagName('html')[0]
const width = oHtml.clientWidth;
// 320px的屏幕基準像素爲12px
oHtml.style.fontSize = 12 * (width / 320) + "px";

 

/**
  * 如下這段代碼是用於根據移動端設備的屏幕分辨率計算出合適的根元素的大小
  * 當設備寬度爲375(iPhone6)時,根元素font-size=16px; 依次增大;
  * 限制當爲設備寬度大於768(iPad)以後,font-size再也不繼續增大
  * scale 爲meta viewport中的縮放大小
  */
(function (doc, win) {
  var docEl = win.document.documentElement;
  var resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize';
  /**
    * ================================================
    *   設置根元素font-size
    * 當設備寬度爲375(iPhone6)時,根元素font-size=16px; 
    × ================================================
    */
  var refreshRem = function () {
    var clientWidth = win.innerWidth
                      || doc.documentElement.clientWidth
                      || doc.body.clientWidth;

    console.log(clientWidth)
    if (!clientWidth) return;
    var fz;
    var width = clientWidth;
    fz = 16 * width / 375;
    docEl.style.fontSize = fz + 'px';
  };

  if (!doc.addEventListener) return;
  win.addEventListener(resizeEvt, refreshRem, false);
  doc.addEventListener('DOMContentLoaded', refreshRem, false);
  refreshRem();

})(document, window);

十5、rem計算(px2rem)

  • 對於須要使用rem來適配不一樣屏幕的元素,使用rem來做爲CSS單位,爲了方便,能夠藉助sass寫一個函數計算px轉化爲rem,寫樣式時沒必要一直手動計算;

 

/* 
 * 此處 $base-font-size 具體數值根據設計圖尺寸而定
 * flexible中設置的標準是【fontSize=16px, when 屏幕寬度=375】,所以,按此標準進行計算,
 * 若設計圖爲iPhone6(375*667)的二倍稿,則$base-font-size=32px
 *
 */ 
@function px2rem($px, $base-font-size: 32px) {
  @if (unitless($px)) {
    @warn "Assuming #{$px} to be in pixels, attempting to convert it into pixels for you";
    @return px2rem($px + 0px); // That may fail.
  } @else if (unit($px) == rem) {
    @return $px;
  }
  @return ($px / $base-font-size) * 1rem;
}

// 使用,eg:
font-size: px2rem(18px);

十6、問題思考

  • 我以前一直在想一個問題,選取哪一個設備來作基準、屏幕寬度等分爲多少比較合適,設計圖給多大寬度的版本?被選取做爲基準的設備,應當就是前端須要設計提供的設計圖版本,這樣能夠避免一些尺寸上的糾纏,而等分爲多少等分,除了考慮方便設計,是否須要考慮其餘問題?對於根元素font-size沒有手動設置的狀況,1rem究竟等於多少?

  • 瞭解到的一些事實:某些Android設備會丟掉 rem 小數部分(具體是哪些設備,搜到的文章中沒有說),那麼1rem對應的px少些,在這些安卓設備上顯示偏差就會較小,固然若是不存在會丟掉小數這個問題,這一說也就沒必要考慮了。
    未設置font-size狀況下,1rem的大小具體看瀏覽器的實現,默認的根元素大小是font-size=16px

  • 目前通常會選取iPhone6做爲基準,設計圖便要iPhone6的二倍圖
    當動態縮放視口爲1/dpr, 計算所得的根元素fontSize也會跟着縮放,即若理想視口(scale=1), iPhone6根元素fontSize=16px; 若scale=0.5, iPhone6根元素fontSize=32px; 所以設置視口縮放應放於設置根元素fontSize以前;

十7、vm/vh:CSS單位

  • vw(view-width), vh(view-height) 這兩個單位是CSS新增的單位,表示視區寬度/高度,視區總寬度爲100vw, 總高度爲100vh;
  • 視區指瀏覽器內部的可視區域大小:window.innerWidth/Height;

十8、一些問題

  • upsampling/downsampling
    DownSampling: 大圖放入比圖片尺寸小的容器中時,出現像素分割成就近色

  • 不一樣scale顯示同一圖片基本無問題;同一sacle,不一樣倍數圖,存在色差(Downsampling)

十9、手淘的實現方案

經過一段JS代碼根據設備的屏幕寬度、dpr設置根元素的data-dpr和font-size, 這段JS代碼要在全部資源加載以前執行,建議作內聯處理。

各類元素(文本、圖片)處理方案參考:
圖:怎樣讓你的網站適應視網膜分辨率

二10、px轉rem的mixin

 

// 使用sass的混合宏
// 淘寶手淘的方案裏,i6(375pt)屏幕寬度爲10rem,即font-size=75px, scale=0.5 因設計圖爲二倍圖,$base-font-size=75px
@mixin px2rem($property,$px-values,$baseline-px:16px,$support-for-ie:false){
    //Conver the baseline into rems
    $baseline-rem: $baseline-px / 1rem * 1;
    //Print the first line in pixel values
    @if $support-for-ie {
        #{$property}: $px-values;
    }
    //if there is only one (numeric) value, return the property/value line for it.
    @if type-of($px-values) == "number"{
        #{$property}: $px-values / $baseline-rem;
    }
    @else {
        //Create an empty list that we can dump values into
        $rem-values:();
        @each $value in $px-values{
            // If the value is zero or not a number, return it
            @if $value == 0 or type-of($value) != "number"{
                $rem-values: append($rem-values, $value / $baseline-rem);
            }
        }
        // Return the property and its list of converted values
        #{$property}: $rem-values;
    }
}

二11、小結

  • 適配不一樣屏幕寬度以及不一樣dpr,經過動態設置viewport(scale=1/dpr) + 根元素fontSize + rem, 輔助使用vw/vh等來達到適合的顯示;
  • 若無需適配可顯示1px線條,也能夠不動態設置scale,只使用動態設置根元素fontSize + rem + 理想視口;
  • 當視口縮放,計算所得的根元素fontSize也會跟着縮放,即若理想視口(scale=1), iPhone6根元素fontSize=16px; 若scale=0.5, iPhone6根元素fontSize=32px; 所以沒必要擔憂rem的計算;
  • !!css單位:之前我認爲這樣比較好:適配元素rem爲單位,正文字體及邊距宜用px爲單位;如今認爲所有用rem便可,包括字體大小,不用px;
  • px爲單位的元素,需根據dpr有不一樣的大小,如大小12px, dpr=2則採用24px, 使用sass mixin簡化寫法;
  • 配合scss函數,簡化px2rem轉換,且易於維護(若需修改$base-font-size, 無需手動從新計算全部rem單位);
  • px2rem函數的base-font-size=32px,參數傳值直接爲設計圖標註尺寸;
  • 使用iPhone6(375pt)二倍設計圖:寬度750px;
  • 切圖使用三倍精度圖,以適應三倍屏(這個目前我尚未實際應用過)

二12、Retina視網膜屏
       Retina視網膜屏,是指人眼在正常觀察距離(iPhone定義爲10英寸,iPad定義爲15英寸)下,視網膜已經沒法分辨單個像素,再也不出現像素顆粒感,僅能觀察到如絲般細膩的畫面,能夠理解爲超高分辨率屏幕;

  • 這是一種顯示技術,能夠將把更多的像素點壓縮至一塊屏幕裏,從而達到更高的分辨率並提升屏幕顯示的細膩程度,這種分辨率在正常觀看距離下足以令人肉眼沒法分辨其中的單獨像素。

最早使用retina屏幕是iphone 4,屏幕分辨率爲960 * 640(326ppi)。

對好比下兩幅圖,能夠清晰地看出是否 Retina 屏的顯示差別:

 

屏幕快照 2018-08-20 下午3.00.23.png

 

兩代iPhone 的物理尺寸(屏幕寬高有多少英寸)是同樣的,從上圖能夠看出,iphone 4的顯示效果要明顯好於iphone 3GS,雖然 iPhone 4 分辨率提升了,但它不一樣於普通的電腦顯示器那樣爲了顯示更多的內容,而是提高顯示相同內容時的畫面精細程度。這種提高方式是靠提高單位面積屏幕的像素數量,即像素密度來提高分辨率,這樣作的主要目的是爲了提升屏幕顯示畫面的精細程度。以第三代 MacBook Pro with Retina Display爲例, 工做時顯卡渲染出的2880x1880個像素每四個一組,輸出原來屏幕的一個像素顯示的大小區域內的圖像。這樣一來,用戶所看到的圖標與文字的大小與原來的1440x900分辨率顯示屏相同,但精細度是原來的4倍。

注意:在桌面顯示器中,咱們調整了顯示分辨率,好比從 800 * 600 調整到 1024 * 768 時,屏幕的文字圖標會變小,顯示的內容更多了。但 Retina 顯示方式不會產生這樣的問題,或者說, Retina 顯示技術解決的是顯示畫面精細程度的問題,而不是解決顯示內容容量的問題。

二十3、相關連接

做者:puxiaotaoc 連接:https://www.jianshu.com/p/b13d811a6a76 來源:簡書 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。

相關文章
相關標籤/搜索