茴字的四種寫法—移動適配方案的進化

話說我剛工做的時候,就開始用rem了,過了沒多久,接觸到了flexible,系統化且支持iOS的retina屏迅速征服了我,最近又看到了大漠大神的vw。因此本文想完成一篇一站式的文章,能夠系統的瞭解前端適配的演進。閒話少敘,立刻開始。

1. 什麼是前端適配

從UI展示層面上:
咱們指望不一樣尺寸的設備,頁面能夠自適應的展現或者進行等比縮放,從而在不一樣的尺寸的設備下看起來協調或者差很少css

從代碼實現層面上:
咱們但願前端適配能夠用用儘量簡潔的代碼來實現。最好一套代碼實現兼容全部設備,而不是對每一個或每種設備都寫一套方案,不是次次都選用最無奈的方案(Android和iOS分開編寫)。html

2. 關鍵字

若是你瞭解這些關鍵字,那麼這段大能夠跳過,若是後面遇到了問題,感受有些疑惑,也能夠再回來查閱。前端

2.1 Viewport/視口

通俗的講,移動設備上的viewport就是設備的屏幕上能用來顯示咱們的網頁的那一塊區域[1],但不必定是咱們可見的區域。具體來講,分爲如下三種。vue

2.1.1 Visual Viewport

Visual Viewport: 可見視口。就是移動設備上能夠被咱們看見的部分。寬度在移動端經過window.innerWidth得到(僅限移動端,PC上哪怕是chrome模擬也會有不一樣的結果)。

圖片描述

2.2.2 Layout Viewport

Layout Viewport: 佈局視口。

圖片描述

若是把PC上的頁面放到移動端,以iphone8爲例,若是隻展現爲可見視口的寬度(375px),那麼頁面會被壓縮的特別窄而顯示錯亂,因此移動瀏覽器就決定默認狀況下把viewport設爲一個較寬的值,好比980px,這樣的話即便是那些爲桌面設計的網站也能在移動瀏覽器上正常顯示了。[1]android

而事實上,咱們通常看不到如上圖這樣出現橫向滾動條的界面;在手機上訪問頁面時,每每是下圖的樣子:git

圖片描述

這是因爲頁面body寬度設置了100%而沒有指定一個具體的寬度致使的,從而使頁面被等比縮放了。因爲用戶能夠縮放,因此還算能正常瀏覽。github

2.2.3 Ideal Viewport

Ideal Viewport:理想視口,其實就是設備的可見區域,和可見視口一致。

設置Ideal Viewport的好處是,只要按照Ideal Viewport來設計樣式稿,用戶就不用能最完美的查看網站的內容——既不用左右滑動,也不用放大縮小。web

設置理想視口:chrome

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

這段代碼的意思是將佈局視口的寬度設置爲設備寬度,初始縮放比例爲1,最大縮放比例爲1,用戶不能縮放。瀏覽器

2.2 像素

2.2.1 物理像素

物理像素:一個物理像素是顯示器(手機屏幕)上最小的物理顯示單元,在操做系統的調度下,每個設備像素都有本身的顏色值和亮度值。[2]

2.2.2 設備獨立像素

設備獨立像素:又稱爲CSS像素,就是咱們平常代碼中使用的像素。瀏覽器內的一切長度都是以CSS像素爲單位的,CSS像素的單位是px。

2.2.3 設備像素比

設備像素比(簡稱dpr)定義了物理像素和設備獨立像素的對應關係。好比說對於iOS的retina屏,一個設備獨立像素就對應着4個物理像素。這樣的設計可使畫面更加清晰銳利,以下圖:
圖片描述

3. 業界的解決方案

OK,LongLongAgo的前綴以後,終於到了正題。回到咱們最開始的初心:咱們只是想要經過一套代碼,實現一個能夠在不一樣頁面尺寸上展現差很少的頁面。在這一塊,如今主要有三種方案。

3.1 Rem的解決方案

DPR一致時,px在不一樣的屏幕尺寸上會展現爲定寬,這就致使咱們的頁面可能會出現滾動條或者佔不滿的狀況。而經過rem來設置div的寬高,能夠保證頁面能夠經過調整html的font-size來總體放大或者縮小,從而達到無論屏幕寬度是多少,頁面都能完美展現的效果。

例如,針對750*1334的設計稿:

<meta name="viewport" content="initial-scale=1,maximum-scale=1, minimum-scale=1">
<script>
    document.documentElement.style.fontSize = window.innerWidth / 7.5 + 'px';
</script>

這樣,全部的設備的寬度都是7.5rem,只須要把設計稿上的px值統一除以100,就能夠獲得相應的rem值了。

網易也採用的這種方法。

3.2 Flexible.js

Flexible是阿里團隊開發的前端適配方案,也是用的rem的方法。那麼第一種方法其實已經能解決前端適配問題了,爲何阿里還要開發一個Flexible呢?

在第一種方法中,dpr=1時沒有任何問題,可是在dpr=2或者更高的手機屏幕上,由於物理像素的增長,存在小於1px的顯示空間。若是採用第一種方法,由於它統一對scale設置爲1,那麼咱們假如想要實現0.5px, 就只能經過transform的方式。若是有多個這樣的樣式,代碼就會變得很麻煩。

.scale{
    position: relative;
}
.scale:after{
    content:"";
    position: absolute;
    bottom:0px;
    left:0px;
    right:0px;
    border-bottom:1px solid #ddd;
    -webkit-transform:scaleY(.5);
    -webkit-transform-origin:0 0;
}

所以,阿里的flexible方案充分考慮了這種狀況,動態的設置了fontsize和scale, 從而使得CSS中的1px等於物理像素中的1px,在IOS下獲得最清晰的體驗。

if (!dpr && !scale) {
    var isAndroid = win.navigator.appVersion.match(/android/gi);
    var isIPhone = win.navigator.appVersion.match(/iphone/gi);
    var devicePixelRatio = win.devicePixelRatio;
    if (isIPhone) {
        // iOS下,對於2和3的屏,用2倍的方案,其他的用1倍方案
        if (devicePixelRatio >= 3 && (!dpr || dpr >= 3)) {                
            dpr = 3;
        } else if (devicePixelRatio >= 2 && (!dpr || dpr >= 2)){
            dpr = 2;
        } else {
            dpr = 1;
        }
    } else {
        // 其餘設備下,仍舊使用1倍的方案
        dpr = 1;
    }
    scale = 1 / dpr;
}

最終在iphone8下頁面的header被設置爲:
<meta name="viewport" content="initial-scale=0.5,maximum-scale=0.5,minimum-scale=0.5,user-scalable=no">

具體的你們能夠看《使用Flexible實現手淘H5頁面的終端適配

另外須要指出的一點是:Flexible將頁面分紅了100份,頁面的寬度是10rem,對於750的設計稿,咱們須要用相應的px數除以75來獲得。手動計算是愚蠢的,不一樣的編譯器均可如下載pix2rem插件(能夠直接寫px而後自動轉換爲相應的rem值),直接使用sass或者postcss打包也能達到一樣的功能。

總結一下上面兩種rem方法,主要思想爲:

  • 根據dpr的值來修改html的font-size,從而使用rem實現等比縮放
  • 根據dpr的值來修改viewport實現1px的線

可是Flexible也有它的侷限性,具體表現爲:

  • 不能與響應式佈局兼容
  • 對Android沒有作處理,致使1px和backgroudImage還要額外作處理的問題[4]

因此咱們有了第三種解決方案——vw。

3.3 vw

vw是基於Viewport視窗的長度單位,在CSS3中和Viewport相關的單位有四個,分別爲vw、vh、vmin和vmax。

  • vw: 是Viewport's width的簡寫,1vw等於window.innerWidth的1%
  • vh:和vw相似,是Viewport's height的簡寫,1vh等於window.innerHeihgt的1%
  • vmin: vmin的值是當前vw和vh中較小的值
  • vmax: vmax的值是當前vw和vh中較大的值

其實vw的方案的寫法和flexible方案的寫法一致——由於flexible其實就是用hack的手段模擬了vw的實現而已。

具體寫法:針對750px的設計稿,將相應的px值除以7.5就是vw的值。

由於此方法不會改變可見視口的寬度,因此能夠和media query通用了,另外,也支持了Android上高分辨率屏的展現。

儘管在某些Android機型上還存在兼容問題,咱們也可使用Viewport Units Buggyfill,具體見《如何在Vue項目中使用vw實現移動端適配

總結

正如大漠所說,flexible模擬vw的時代已通過去,真正的酋長vw已經歸來

參考文檔:

  1. 移動前端開發之viewport的深刻理解
  2. 移動端高清、多屏適配方案
  3. 再聊移動端頁面的適配
  4. 基於淘寶彈性佈局方案lib-flexible的問題研究
  5. 如何在Vue項目中使用vw實現移動端適配
相關文章
相關標籤/搜索