flexible.js 移動端自適應(yīng)方案

一,flexible.js 的使用方式:

github地址:https://github.com/amfe/lib-flexible
官方文檔地址:https://github.com/amfe/article/issues/17
本文中有部分內(nèi)容引至上面這個文檔。

(一),引用方式

1,引用cdn地址
<script src="http://g.tbcdn.cn/mtb/lib-flexible/0.3.2/??flexible_css.js,flexible.js"
></script>

當前最新的版本是0.3.2。

2,下載flexible.js 等文件到項目指定目錄下,然后在head中引入。建議對于js做內(nèi)聯(lián)處理,在所有資源加載之前執(zhí)行這個js。

下面是淘寶的寫法:

<!DOCTYPE HTML>
<html>
<head>
    <meta charset="utf-8" />
    <meta content="yes" name="apple-mobile-web-app-capable" />
    <meta content="yes" name="apple-touch-fullscreen" />
    <meta content="telephone=no,email=no" name="format-detection" />
    <meta content="maximum-dpr=2" name="flexible" />
    <script src="build/flexible_css.js"></script>
    <script src="build/flexible.js"></script>
    <title>lib.flexible</title>
</head>

(二),flexible.js原理

在頁面中引入flexible.js后,flexible會在<html>標簽上增加一個data-dpr屬性和font-size樣式(如下圖)。

flexible.png

js首先會獲取設(shè)備型號,然后根據(jù)不同設(shè)備添加不同的data-dpr值,比如說1、2或者3,從源碼中我們可以看到。

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 {
        // 其他設(shè)備下,仍舊使用1倍的方案
        dpr = 1;
    }
    scale = 1 / dpr;
}

另外,頁面中的元素用rem單位來設(shè)置,rem就是相對于根元素<html>的font-size來計算的,flexible.js能根據(jù)<html>的font-size計算出元素的盒模型大小。這樣就意味著我們只需要在根元素確定一個px字號,因此來算出各元素的寬高,從而實現(xiàn)屏幕的適配效果。

(三),把視覺稿中的px轉(zhuǎn)換成rem

工作中我們常見的視覺稿大小大至可為640、750、1125三種。不過flexible.js并沒有限制只能用這三種,所以你還可以根據(jù)自身情況來調(diào)整,具體如何轉(zhuǎn)換,我們以視覺稿為640px的寬來舉例子,把640px分為100份,每一份稱為一個單位a,那么每個a就是6.4px,而1rem單位被認定為10a,此時,1rem=1(a)X10X6.4(px)即64px。

640px/100=6.4px                              1個單位a為6.4px
1rem = 10a                                   1rem單位被認定為10a
1rem = 1(a)*10*6.4(px) = 64px

因此,對于視覺稿上的元素的尺寸換算,只需要原始px值除以rem基準px值(此例子中為64px)即可。例如240px * 120px的元素,最后轉(zhuǎn)換為3.75rem * 1.875rem。

在開發(fā)過程中那我們?nèi)绾慰焖俚陌裵x轉(zhuǎn)換成rem呢?

1,如果你用的是Sublime Text3,你可以直接在這個編輯器上安裝CSSREM插件。

github地址:https://github.com/flashlizi/cssrem

2,如果你用的是其他編輯器或者IDE,就可以用CSS的處理器來處理,比如說Sass、LESS以及PostCSS這樣的處理器。我們簡單來看兩個示例。
@function px2em($px, $base-font-size: 75px) {
  @if (unitless($px)) {
    @warn "Assuming #{$px} to be in pixels, attempting to convert it into pixels for you";
    @return px2em($px + 0px); // That may fail.
  } @else if (unit($px) == em) {
    @return $px;
  }
  @return ($px / $base-font-size) * 1em;
}

除了使用Sass函數(shù)外,還可以使用Sass的混合宏:

@mixin px2rem($property,$px-values,$baseline-px:75px,$support-for-ie:false){
  //Conver the baseline into rems
  $baseline-rem: $baseline-px / 1rem * 1;
  //打印出第一行的像素值
  @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;
  }
}

(四),字體不使用rem的方法

工作中做完一個觸屏版的頁面后,我們會拿iPhone5s、iPhone6、iPhone6s等手機進行測試,他們都是Retina屏,我們當然希望在這些手機型號上看到的文本字號是相同的。也就是說,我們不希望文本在Retina屏幕下變小,另外,我們希望在大屏手機上看到更多文本(例如iPhone7、iPhone7Plus)。另外,現(xiàn)在絕大多數(shù)的字體文件都自帶一些點陣尺寸,通常是16px和24px,都是偶數(shù),所以我們不希望出現(xiàn)13px和15px這樣的奇葩尺寸。
如此一來,就決定了在制作H5的頁面中,rem并不適合用到段落文本上。所以在Flexible整個適配方案中,考慮文本還是使用px作為單位。只不過使用[data-dpr]屬性來區(qū)分不同dpr下的文本字號大小。

div {
    width: 1rem;
    height: 0.4rem;
    font-size: 12px; // 默認寫上dpr為1的fontSize
}
[data-dpr="2"] div {
    font-size: 24px;
}
[data-dpr="3"] div {
    font-size: 36px;
}

為了能更好的利于開發(fā),在實際開發(fā)中,我們可以定制一個font-dpr()這樣的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;
    }
}

有了這樣的混合宏之后,在開發(fā)中可以直接這樣使用:

@include font-dpr(16px);

當然這只是針對于描述性的文本,比如說段落文本。但有的時候文本的字號也需要分場景的,比如在項目中有一個slogan,業(yè)務(wù)方希望這個slogan能根據(jù)不同的終端適配。針對這樣的場景,完全可以使用rem給slogan做計量單位。

(五),viewport的meta標簽。

該標簽主要用來告訴瀏覽器如何規(guī)范的渲染W(wǎng)eb頁面,而你則需要告訴它視窗有多大。在開發(fā)移動端頁面,我們需要設(shè)置meta標簽如下:

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

代碼以顯示網(wǎng)頁的屏幕寬度定義了視窗寬度。網(wǎng)頁的比例和最大比例被設(shè)置為100%。

而我們在使用flexible.js時候就只需要像下面這樣寫<meta>標簽,或者干脆省略下面的標簽:
<meta name="viewport" content="width=device-width, user-scalable=no">

或者我們也可以像flexible的github例子中那樣寫:

<meta content="maximum-dpr=2" name="flexible" />

原理:flexible.js會先去獲取頁面上[name="viewport"]或[name="flexible"]的meta標簽,如果有就直接根據(jù)獲取到的值來判斷,如果沒有,會自己創(chuàng)建一個meta標簽,我們看一下源碼就知道了。

var metaEl = doc.querySelector('meta[name="viewport"]');
var flexibleEl = doc.querySelector('meta[name="flexible"]');
...
if (!metaEl) {
    metaEl = doc.createElement('meta');
    metaEl.setAttribute('name', 'viewport');
    metaEl.setAttribute('content', 'initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
    if (docEl.firstElementChild) {
        docEl.firstElementChild.appendChild(metaEl);
    } else {
        var wrap = doc.createElement('div');
        wrap.appendChild(metaEl);
        doc.write(wrap.innerHTML);
    }
}

有了<meta>標簽之后,就可以動態(tài)改寫data-dpr和font-size兩個屬性的值,因此也就達到了適配的效果。

二,手動設(shè)置的相關(guān)問題:

(一)手動配置dpr

引入執(zhí)行js之前,可以通過輸出meta標簽方式來手動設(shè)置dpr。語法如下:

<meta name="flexible" content="initial-dpr=2, maximum-dpr=3" />

注意:initial-dpr=2, maximum-dpr=3這兩個參數(shù)只能選其一。

(二),當我們手動設(shè)置maximum-dpr=x時

在flexible的github例子中,添加maximum-dpr這個屬性,content="maximum-dpr=2",這個屬性是給出一個最大的dpr限制,然后比較系統(tǒng)的dpr和給定的dpr,取最小值。

(三),當我們手動設(shè)置initial-dpr=x時

如果要使用flexible.js來做布局的話,建議不要添加這個屬性,因為這個屬性會把dpr強制設(shè)置為給定的值,如果手動設(shè)置initial-dpr=1之后,不管設(shè)備是多少dpr都會強制認為其dpr是你設(shè)備的值。
另外,在flexible中,只對IOS設(shè)備進行dpr判斷,對于Android系列始終認為其dpr為1,手機淘寶并沒有對安卓的dpr進行一個適配。咱們可以通過flexible.js的源碼來看:

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 {
        // 其他設(shè)備下,仍舊使用1倍的方案
        dpr = 1;
    }
    scale = 1 / dpr;
}

作者:Scaukk 在這篇http://www.lxweimin.com/p/221bebfae266文章中是這么理解的:
android手機屏幕大小,寬高比是花開滿地,要做的調(diào)整真的是太多了。如果根元素的font-size尺寸不對,頁面效果不用多說。
就算把當前的設(shè)備信息都考慮進去了,那以后呢。
所以,考慮開發(fā),維護,兼容性...淘寶這么做還是有道理的。

(四),手動設(shè)置rem基準值的方法:

html{ font-size: 60px !important; }

三,需要注意的幾個地方:

(一),遇到下面兩種情況的時候,我們在切頁面的時候需要切兩套圖片,即@2x和@3x:

1,當圖標被放大時會模糊。

2,當產(chǎn)品對頁面上的圖片清晰度要求很高時。

@2x為750X1334的設(shè)計稿(高度會隨著內(nèi)容多少而改變)。@3x為1125X2001的設(shè)計稿(高度會隨著內(nèi)容多少而改變)。如果要放大設(shè)計稿來切圖的時候是等比放大1.5倍。

(二), 解決雪碧圖的問題,建議能用SVG的地方就盡量用SVG,或者有些常用的圖標用iconfont來替代,另外,有些小圖片在遇到dpr=2時,可能會模糊,這時建議用大圖來切圖。

五,幾個后期我們開發(fā)中可能會遇到的名詞:

Element.getBoundingClientRect().width 用來獲取元素自身的寬度。

Element.getBoundingClientRect()用來獲取頁面中某個元素的左、上、右、下分別相對于瀏覽器視窗的位置,是DOM元素到瀏覽器可視范圍的距離(不含頁面不可見部分)。

設(shè)備的px不會改變,css的px改變%(百分比)時,不會影響設(shè)備的px,只是原本設(shè)備的1個px中可能會顯示多個或不足一個css的px。當縮放級別100%時,1個單位的css px嚴格等于1個單位的設(shè)備px。

screen.width、screen.height用戶屏幕的完整寬度和高度。

window.innerWidth、window.innerHeight瀏覽器窗口內(nèi)部寬度和高度的尺寸,包含了滾動條的尺寸。

window.pageXOffset、window.pageYOffset用戶滾動了多少滾動條的距離。

視窗viewport 簡單的理解,viewport是嚴格等于瀏覽器的窗口。在桌面瀏覽器中,viewport就是瀏覽器窗口的寬度高度。但在移動端設(shè)備上就有點復(fù)雜。移動端的viewport太窄,為了能更好為CSS布局服務(wù),所以提供了兩個viewport:虛擬的viewportvisualviewport和布局的viewportlayoutviewport。

Retina 是視網(wǎng)膜的意思,指顯示屏的分辨率極高,使得肉眼無法分辨單個像素。

物理像素,也可以稱為設(shè)備像素,他是顯示設(shè)備中一個最微小的物理部件,每個像素可以根據(jù)操作系統(tǒng)設(shè)置自己的顏色和亮度。正是這些設(shè)備像素的微小距離欺騙了我們?nèi)庋劭吹降膱D像效果。

設(shè)備獨立像素也稱為密度無關(guān)像素,可以認為是計算機坐標系統(tǒng)中的一個點,這個點代表一個可以由程序使用的虛擬像素(比如說CSS像素),然后由相關(guān)系統(tǒng)轉(zhuǎn)換為物理像素。

CSS像素是一個抽像的單位,主要使用在瀏覽器上,用來精確度量Web頁面上的內(nèi)容。一般情況之下,CSS像素稱為與設(shè)備無關(guān)的像素(device-independent pixel),簡稱DIPs。

屏幕密度,即設(shè)備表面上存在的像素數(shù)量,通常以每英寸有多少像素來計算(PPI)。

設(shè)備像素比(device pixel ratio),簡稱dpr,定義了物理像素和設(shè)備獨立像素的對應(yīng)關(guān)系,它的值可以按下面的公式計算得到:

設(shè)備像素比 = 物理像素 / 設(shè)備獨立像素

眾所周知,iPhone6的設(shè)備寬度和高度為375pt * 667pt,可以理解為設(shè)備的獨立像素;而其dpr為2,根據(jù)上面公式,我們可以很輕松得知其物理像素為750pt * 1334pt。
在不同的屏幕上,CSS像素所呈現(xiàn)的物理尺寸是一致的,而不同的是CSS像素所對應(yīng)的物理像素具數(shù)是不一致的。
在普通屏幕下1個CSS像素對應(yīng)1個物理像素,而在Retina屏幕下,1個CSS像素對應(yīng)的卻是4個物理像素。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 剛開始做移動端web開發(fā)的同學(xué)應(yīng)該都碰到過頁面適配問題,為什么我在開發(fā)手機上調(diào)試好的頁面在其他手機會有這樣或那樣的...
    留七七閱讀 19,529評論 5 80
  • 在移動互聯(lián)網(wǎng)快速發(fā)展的今天,手機的種類和尺寸越來越多,作為前端的小伙伴們可能會越來越頭疼,但又不得不去適配一款又一...
    keenjaan閱讀 26,932評論 9 86
  • 一、meta標簽的效果 移動端頁面一般會在head頭部添加如下meta標簽。 該meta標簽是否添加對頁面渲染的影...
    nimw閱讀 3,638評論 0 5
  • 問答題47 /72 常見瀏覽器兼容性問題與解決方案? 參考答案 (1)瀏覽器兼容問題一:不同瀏覽器的標簽?zāi)J的外補...
    _Yfling閱讀 13,815評論 1 92
  • 娛樂性:7.5分 愛倫坡的經(jīng)典恐怖故事,配合每個故事風格迥異的畫風,使得故事的詭異氛圍變得更讓人深陷其中,深深的壓...
    會飛的清蒸魚閱讀 589評論 0 0