前端移動端適配總結

meta標籤到底作了什麼事情

作過移動端適配的小夥伴必定有遇到過這行代碼:javascript

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

可是,不少小夥伴只是感性的認識:噢,我加了這行代碼,而後頁面的寬度就會跟個人設備寬度一致。然而,這種理解是很片面的。那麼,這句話的本質究竟是什麼呢?css

不急,咱們先往下面看,這裏先留個懸念。html

幾個專有名詞和單位

這裏,咱們先來辨析一下在適配的時候常常會遇到的一些名詞、數值單位。前端

首先,先來看一下物理像素html5

以iphone6爲例,可知道:java

分辨率:1334pt x 750pt
指的是屏幕上垂直有1334個物理像素,水平有750個物理像素。ios

屏幕尺寸:4.7in
注意英寸是長度單位,不是面積單位。4.7英寸指的是屏幕對角線的長度,1英寸等於2.54cm。web

屏幕像素密度:326ppi
指的是每英寸屏幕所擁有的像素數,在顯示器中,dpi=ppi。dpi強調的是每英寸多少點。同時,屏幕像素密度=分辨率/屏幕尺寸算法

接着,咱們來看一下其餘的單位。segmentfault

設備獨立像素:設備獨立像素,不一樣於設備像素(物理像素),它是虛擬化的。好比說css像素,咱們常說的10px其實指的就是它。須要注意的是,物理像素開發者是沒法獲取的,它是天然存在的一種東西,該是多少就是多少。

設備像素比:縮寫簡稱dpr,也就是咱們常常在谷歌控制檯移動端調試頂端會看到的一個值。設備像素比 = 設備像素 / css像素(垂直方向或水平方向)。能夠經過JS來獲取:window.devicePixelRatio

PC和移動端不一樣的視口

注:如下涉及的像素均爲CSS像素。而且默認不考慮縮放。

佈局視口

寫過css的小夥伴應該知道,咱們在htmlbody設置width:100%;height:100%;的時候,它並非無效的。咱們都知道100%這種百分數應該是繼承父元素而來的。那在這裏是繼承哪裏的呢?

PC瀏覽器中,有一個用來約束CSS佈局視口的東西,又叫作初始包含塊。這也就是全部寬高繼承的由來。除去marginpadding,佈局視口和瀏覽器可視窗口寬度是一致的,同時也和瀏覽器自己的寬度一致。

可是在移動端,就大不同了。

如下的例子是在不加meta標籤的前提下進行演示的。

假如咱們如今作一個二八分的左右佈局,那麼若是在PC端上面的話,顯示的效果很是完美,這沒什麼好說的。

那若是是在手機端呢,這裏以iphone6爲例子來說解:

圖例以下:

image

代碼以下:

* {
    margin: 0;
    padding: 0;
}

html,
body {
    height: 100%;
    width: 100%;
}

.left {
    float: left;
    width: 20%;
    height: 100%;
    background: red;
}

.right {
    float: right;
    width: 80%;
    height: 100%;
    background: green;
}
----
<body>
    <div class="left"></div>
    <div class="right"></div>
</body>

這裏咱們會看到,爲何body的寬度是980px,而瀏覽器的寬度只有375px,那麼這個980px究竟是從哪裏來的呢?

其實,這裏的980px就是移動端所謂的佈局視口了。

在移動端,默認的狀況下,佈局視口的寬度是要遠遠大於瀏覽器的寬度的。這兩個視口不一樣於PC端,是相互獨立存在的。爲何呢?試想一下,若是一個網頁不對移動端進行適配,用戶進行閱讀的時候,若是默認狀況下佈局視口的寬度等於瀏覽器寬度,那是否是展現起來更加的不友好。也就是說,若是一個div的寬度爲20%,那麼它在佈局視口寬度爲980px的時候,展現給用戶的像素還有196px,而若是寬度只有375px的狀況下,寬度只有75px,展現的大小相差特別大。

因此,瀏覽器廠商爲了讓用戶在小屏幕下網頁也可以顯示地很好,因此把佈局視口寬度設置地很大,通常在768px ~ 1024px之間,最多見的寬度是980px。這個寬度能夠經過document.documentElement.clientWidth獲得。

視覺視口

對於視覺視口來講,這個東西是呈現給用戶的,它是用戶看到網頁區域內CSS像素的數量。因爲用戶能夠自行進行縮放控制,因此這個視口並非開發者須要重點關注的。

值得注意的是,在移動端縮放不會改變佈局視口的寬度,當縮小的時候,屏幕覆蓋的css像素變多,視覺視口變大,反之亦然。

而在PC端,縮放對應佈局寬度和視覺窗口寬度都是聯動的。而瀏覽器寬度自己是固定的,不管怎麼縮放都不受影響。

若是對上面的寬度仍是很亂,那麼這裏有一個表格能夠幫助你理清思路。

如下表格橫向都以瀏覽器窗口的寬度做爲基準:

對於PC端來講:
image

對於移動端來講:
image

理想視口

以上,佈局視口很明顯對用戶十分的不友好,徹底忽略了手機原本的尺寸。

因此蘋果引入了理想視口的概念,它是對設備來講最理想的佈局視口尺寸。理想視口中的網頁用戶最理想的寬度,用戶進入頁面的時候不須要縮放。

那麼很明顯,所謂的理想寬度就是瀏覽器(屏幕)的寬度了。

因此就有了下面的這段代碼:

<meta name="viewport" content="width=device-width">

然而,這段代碼其實也並不完美,在IE瀏覽器中,因爲橫屏豎屏的切換會對其形成影響,爲了解決這個兼容性的問題,最後再加上一句,就有了如今的:

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

width=device-width 這句代碼能夠把佈局視口設置成爲瀏覽器(屏幕)的寬度。

initial-scale=1 的意思是初始縮放的比例是1,使用它的時候,同時也會將佈局視口的尺寸設置爲縮放後的尺寸。而縮放的尺寸就是基於屏幕的寬度來的,也就起到了和width=device-width一樣的效果。

另外,值得一提的是,咱們在進行媒體查詢的時候,查詢的寬度值其實也是佈局視口的寬度值。

Retina屏幕&普通屏幕,模糊的由來

dpr的具體表現

有時候咱們會發現,當咱們在適某一機型的時候,顯示上沒什麼問題。可是一旦我換到另一部手機,發現出現了模糊的狀況,尤爲以圖片更爲顯著。

其實這個問題,就是涉及到了上面講到的一個屬性:設備像素比,即咱們常常說的dpr。下面先來看dpr的表現:

假設如今有一臺iphone6,那麼它的設備獨立像素是375x667,dpr爲2,尺寸是4.7in,那麼物理像素就是750x1334。
一樣的咱們也有一臺不知名的設備,它的設備獨立像素恰好也是375x667,尺寸也是4.7in,可是dpr爲1,此時的物理像素就是375x667。

因而,它們的屏幕表現以下:

image

在不一樣的屏幕上,不管是普通屏幕仍是retina屏幕,css像素所呈現的大小是一致的。(若是不理解這句話,能夠寫一個2px的正方形使用谷歌控制檯移動設備調試,在不一樣的設備之間來回切換,你會發現大小實際上是同樣的。一開始我總覺得這個css像素的實際寬高由於受到dpr的影響而在不一樣設備上的長寬是不一致的。)

不一樣的是,1個css像素對應(覆蓋)的物理像素個數。

因此,若是咱們想要在這兩個屏幕顯示這麼一個css樣式:

width: 2px;
heigth: 2px;

在普通屏幕下,也就是dpr爲1的屏幕中,1個css像素對應(覆蓋)的是一個物理像素。在retina屏幕下,1個css像素對應(覆蓋)的是4個物理像素。換句話說,就是dpr爲2的設備。看下面這張圖:

image

淺顯的理解就是能夠看做是2cmx2cm的正方形被切割成四塊,而後遇到dpr爲2的時候,被切割的四塊又被分別切割成四塊,可是總面積不變。

模糊的產生

知道了1個css像素覆蓋的物理像素可能不一樣,就好理解爲何會出現模糊的狀況了。

這裏又講到一個名詞:位圖像素

位圖像素是柵格圖像(如:png,jpg,gif等)最小的數據單元。每個位圖像素都包含着一些自身的顯示信息。(如:顯示位置,顏色值,透明度等)

理論上來講,1個位圖像素對應1個物理像素,圖片才能達到完美清晰的展現

可是上面說過,在retina屏幕上,會出現1個位圖像素對應多個物理像素。

仍是以iphone6爲例,1個位圖像素對應4個物理像素。因爲單個位圖像素已是最小的數據單位了,它不能再被進行切割。因而爲了可以顯示出來,就只能就近取色,從而致使所謂的圖片模糊問題。以下:

image

如何解決

很明顯,因爲位圖像素不夠分而產生模糊的狀況,解決的辦法十分簡單,就是使用跟dpr同個倍數大小的圖片。好比iphone6,一個200x300的img標籤,原圖就要提供400x600的大小。

那麼當加載到img標籤中,瀏覽器會自動對每1px的css像素減半,能夠理解爲此時仍是維持着1:1的css像素:物理像素,不產生模糊。

這個作法其實就是手淘團隊在作retina適配的一個重要的原理之一,後面會講到,這裏先放着不說。

其餘

反向思考一下,若是普通屏幕,也就是dpr爲1的屏幕,也使用了兩倍的圖片,會發生什麼樣的狀況呢?

很明顯,在普通屏幕下,200×300的img標籤,所對應的物理像素個數就是200×300個,而兩倍圖片的位圖像素個數則是200x300x4,因而就出現一個物理像素點對應4個位圖像素點,因此它的取色也只能經過必定的算法進行縮減,顯示結果就是一張只有原圖像素總數四分之一,肉眼看上去雖然圖片不會模糊,可是會以爲有點色差。(其實就是模糊的逆向過程)

用圖片來表示就是:

image

這裏摘取了網上一篇博文的demo來闡述上面所說的問題。

image

以上是一張100x100的圖片,分別放在了100x100,50x50,25x25的容器中,在retina屏幕下面的顯示效果。

經過取色器放大鏡能夠看出邊界像素點的差異:

在圖一中,邊界像素點就近取色,色值介於紅白之間,偏淡,圖片看上去會模糊(能夠理解爲圖片拉伸)。

在圖二中,圖片正常,很清晰。

在圖三中,邊界像素點就近取色,色值介於紅白之間,偏濃,圖片看上去有色差。

手淘團隊flexible.js佈局

現今,適配手機端的傳統rem佈局已經逐步被手淘團隊的一套flexible佈局代替。

具體的實現方式以及細節這裏也不鋪開來講,具體參考w3cplus的一篇文章,很容易讀懂和理解。

這裏我更想分析一下flexible.js作法的意義和緣由。

讀過文章以後,相信你們應該對整個開發適配的流程比較熟悉了。

假設如今要適配一個iphone6的設備。上面已經說過了iphone6的各個參數,這裏再也不贅述,須要的自行上移查看。

因而:

  1. 設計師給了一個750px寬度的設計稿(注意這裏是750px而不是375px)
  2. 前端工程師用750px的這個比例開始還原
  3. 把寬高是px的轉換成rem
  4. 字體使用px而不使用rem
  5. flexible.js會自動判斷dpr進行整個佈局視口的放縮

rem佈局和字體的處理

從flexible.js中可見,在寬高中使用的是rem,這是爲了保證在不一樣寬度尺寸的設備中可以保證佈局的等比例縮放。

而爲何字體不使用rem而是採用px呢?

首先,用過rem單位的小夥伴都會發現,使用rem後因爲不一樣的尺寸,換算以後出現各類奇奇怪怪的數值,最爲明顯的就是更多的小數位,好比13.755px之類的數值。在瀏覽器中,各瀏覽器中對小數點的計算存在誤差,並且有些帶小數的font-size值在特定的瀏覽器顯示並不夠清晰。

其次,咱們但願在小屏幕下面顯示跟大屏幕同等量的字體。而且若是使用rem的話,那麼因爲等比例的存在,在小屏幕下就會存在小屏幕字體更小的狀況,不利於咱們更好的去閱讀,違背了適配的初衷。因此,對於字體的適配,更好的作法就是使用px和媒體查詢來進行適配。

因此,也就不難解釋爲何要對font-size進行放大的處理了,以下的sass代碼:

@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;
    }
}

因爲retina屏幕下dpr的不一樣,咱們又想顯示的字體同樣大,因而就給字體再增大dpr的倍數,這樣當縮小dpr倍的時候,那麼字體也就和設計稿所示的大小同樣大了,在不一樣的手機中顯示的大小也是一致的。

Retina屏幕下的處理與安卓手機的適配

從flexible.js的代碼中能夠知道,flexible佈局僅僅只是針對iPhone進行適配,而默認全部的安卓設備都強制性設置dpr爲1。

因而,由於這個緣故,不少小夥伴可能就會產生這樣的問題:爲何安卓不用retina屏幕,安卓下面是否是就不會有模糊的問題?

其實否則,模糊的本質是由於dpr,而安卓手機不一樣的設備的dpr也是不盡相同的。也就是說,安卓手機下也存在模糊的狀況。只不過它的屏幕不叫retina屏幕,沒有這個叫法,因此不少小夥伴都誤認爲安卓手機沒有這個毛病。

那麼問題又來了?既然也有模糊的毛病,那麼爲何安卓手機不進行適配呢?

問題就在這裏了,有興趣的小夥伴能夠去看一下大中華的安卓手機,dpr參數五花八門,從1到4,連1.7五、2.75這種奇葩的數字也有,因此我的以爲權衡之下,直接簡單「粗暴」把安卓手機所有設置爲1,是效率和收益更高的作法。

固然,也有人進行了flexible.js的改進,就是對dpr比較正常的安卓手機進行適配,也就是說只適配dpr爲整數的安卓設備。對於那些奇葩的dpr爲1.75的設備直接忽略。實現這個並不難,有興趣的小夥伴們能夠試下。

響應式與自適應的選擇

最後,對於響應式和自適應的區別,網上有各類各樣的解釋。

我的認爲,其實不必把它講得那麼複雜,知乎上有個小夥伴講我以爲就很白話文:

響應式針對的是不一樣分辨率設備而進行的適配式設計,以利用@media規則爲主要手段,而自適應則忽略@media以比例佈局爲主,目的是適應不一樣的瀏覽器窗口大小。

因而咱們會發現,現今大型網站,例如說淘寶網,已經沒有作響應式了。什麼意思呢?

咱們會發現,淘寶網手機端和網頁端使用的是兩個域名,也就是說,不一樣的客戶端已經再也不共用一套dom結構了。而是區分開來作自適應。而後每次用戶訪問的時候它就根據客戶端的類型重定向。

爲何呢?

試想一下淘寶這種大型網站,一個分頁下的商品條目特別多,而且每一個商品條目的dom結構又十分複雜,並且pc端每每顯示的信息是要比手機端更多的。若是不分開作兩套,而是直接用響應式的話,那麼pc端上顯示的不少dom就要在手機端上隱藏,結果這些dom都沒有被用到,可是卻加載了。在這個流量和速度至上的時代,代碼冗餘先不說,多加載的這些無用的代碼而消耗的流量,從某種意義上來講就已經損失了不少的效益。

最後

考慮到兼容性的問題,原先咱們在文章頭部說到的那段代碼:

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

Chrome32+版本開始是會默認禁用用戶縮放的,可是考慮到兼容大部分設備,仍是要加上其餘設置,讓meta標籤可以有更好的容錯性。也就是下面這段代碼:

<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, 
 user-scalable=no;">

須要注意的是,在ios10+以上,儘管開發者設置了user-scalable=noSafari仍是容許用戶經過手勢來縮放。(安卓手機各大廠商的內置瀏覽器也逐漸開放用戶縮放,即便使用meta標籤進行設置)

解決的方法也很簡單,只須要檢測touch相關事件來阻止事件的觸發便可。

window.onload = function() {
    // 同時按下兩個手指
    document.addEventListener('touchstart', function(event) {
        if(event.touches.length > 1) {
            event.preventDefault()
        }
    })
    var lastTouchEnd = 0;
    // 特別注意300ms時差的設置
    document.addEventListener('touchend', function(event) {
        var now = (new Date()).getTime();
        if(now-lastTouchEnd <= 300) {
            event.preventDefault();
        }
        lastTouchEnd = now;
    })
}

以上,就是本文的所有啦。

文章有借鑑,借鑑的連接都會在這裏放出來。

前輩們的經驗和知識很寶貴,咱們須要作的,是站在巨人的肩膀上,去提煉這些東西,有本身更好的理解、思考和開拓新知識面。

相關連接:

移動端適配方案(主要講解的是移動端視口方面的知識):
https://segmentfault.com/a/11...
https://segmentfault.com/a/11...

Retina屏幕下模糊的由來:
http://mobile.51cto.com/web-4...

手淘flexible.js佈局:
http://www.w3cplus.com/mobile...

相關文章
相關標籤/搜索