天天看點

flexible.js 移動端自适應架構

;(function(win, lib) {
    var doc = win.document;
    var docEl = doc.documentElement;
    var metaEl = doc.querySelector('meta[name="viewport"]');
    var flexibleEl = doc.querySelector('meta[name="flexible"]');
    var dpr = 0;
    var scale = 0;
    var tid;
    var flexible = lib.flexible || (lib.flexible = {});

    if (metaEl) {
        console.warn('将根據已有的meta标簽來設定縮放比例');
        var match = metaEl.getAttribute('content').match(/initial\-scale=([\d\.]+)/);
        if (match) {
            scale = parseFloat(match[1]);
            dpr = parseInt(1 / scale);
        }
    } else if (flexibleEl) {
        var content = flexibleEl.getAttribute('content');
        if (content) {
            var initialDpr = content.match(/initial\-dpr=([\d\.]+)/);
            var maximumDpr = content.match(/maximum\-dpr=([\d\.]+)/);
            if (initialDpr) {
                dpr = parseFloat(initialDpr[1]);
                scale = parseFloat((1 / dpr).toFixed(2));    
            }
            if (maximumDpr) {
                dpr = parseFloat(maximumDpr[1]);
                scale = parseFloat((1 / dpr).toFixed(2));
            }
        }
    }

    if (!dpr && !scale) {
        var isAndroid = win.navigator.appVersion.match(/android/gi);
        var isIPhone = win.navigator.appVersion.match(/iphone/gi);
        var devicePixelRatio = win.devicePixelRatio;
        //var isRegularDpr = devicePixelRatio.toString().match(/^[1-9]\d*$/g);
        if (isIPhone) {
            // 對于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;
    }

    docEl.setAttribute('data-dpr', dpr);
    if (!metaEl) {
        metaEl = doc.createElement('meta');
        metaEl.setAttribute('name', 'viewport');
        //Android target-densitydpi=device-dpi
        var attribute = 'initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no';
        //判斷是否是WebView
        var app = getCookie('chelun_appName');
        if(app) {
            attribute = 'width=device-width,'+attribute;
        }
        metaEl.setAttribute('content', attribute);
        if (docEl.firstElementChild) {
            docEl.firstElementChild.appendChild(metaEl);
        } else {
            var wrap = doc.createElement('div');
            wrap.appendChild(metaEl);
            doc.write(wrap.innerHTML);
        }
    }

    function getCookie(name){
        var maps = {};
        var cookArr = document.cookie.split(';')
        for(var i in cookArr){
            var tmp = cookArr[i].replace(/^\s*/, '');
            if(tmp){
                var nv = tmp.split('=');
                maps[nv[0]] = nv[1] || '';
            }
        }
        return maps[name];
    }
    function refreshRem(){
        var width = docEl.getBoundingClientRect().width;
        if (width / dpr > 540) {
            width = 540 * dpr;
        }
        var rem = width / 10;
        docEl.style.fontSize = rem + 'px';
        flexible.rem = win.rem = rem;
    }

    win.addEventListener('resize', function() {
        clearTimeout(tid);
        tid = setTimeout(refreshRem, 300);
    }, false);
    win.addEventListener('pageshow', function(e) {
        if (e.persisted) {
            clearTimeout(tid);
            tid = setTimeout(refreshRem, 300);
        }
    }, false);

    if (doc.readyState === 'complete') {
        //doc.body.style.fontSize = 12 * dpr + 'px';
    } else {
        doc.addEventListener('DOMContentLoaded', function(e) {
            //doc.body.style.fontSize = 12 * dpr + 'px';
        }, false);
    }

    refreshRem();

    flexible.dpr = win.dpr = dpr;
    flexible.refreshRem = refreshRem;
    flexible.rem2px = function(d) {
        var val = parseFloat(d) * this.rem;
        if (typeof d === 'string' && d.match(/rem$/)) {
            val += 'px';
        }
        return val;
    }
    flexible.px2rem = function(d) {
        var val = parseFloat(d) / this.rem;
        if (typeof d === 'string' && d.match(/px$/)) {
            val += 'rem';
        }
        return val;
    }

})(window, window['lib'] || (window['lib'] = {}));
           

引入flexible.js

這一步其實非常簡單,隻要把flexible.js的内容複制出來,在本地建立一個flexible.js的檔案,打開粘貼進去就可以了,我把這個檔案放在了js/lib下面:

flexible.js 移動端自适應架構

接着在html頁面裡面,盡可能早的引入這個js檔案(為了讓适配的效果更快):

flexible.js 移動端自适應架構

注:使用lib-flexible,通常不要寫:

複制代碼代碼如下: <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no"/>

交給flexible.js自動處理。

然後在chrome的模拟器裡面,選擇iphone6,應該就能看到html的font-size已經被設定為font-size: 75px了:

flexible.js 移動端自适應架構

3. 編寫CSS

基本要求:

1)除font-size外,其它大小都根據750标注稿的尺寸,轉換成rem機關的值,轉換方法為:标注稿尺寸 / 标注稿基準字型大小;

2)标注稿基準字型大小 = 标注稿寬度 / 10,如标注稿寬為750,标注稿基準字型大小為75;标注稿寬為640,标注稿基準字型大小為64;(是以淘寶這個方案是可以在任意設計稿尺寸下使用的)

3)如果需要設定font-size,可跟據html的data-dpr屬性來處理,類似下面的寫法:

?

1 2 3 4 5 6 7

[data-dpr=

"2"

] p {

font-size

:

16px

;

}

[data-dpr=

"3"

] p {

font-size

:

24px

;

}

以安卓下載下傳按鈕的樣式為例,說明這種用法。android.png的尺寸為:414*80,是以css這麼寫:

?

1 2 3 4

.btn {

width

:

414

rem/@font-size-base;

height

:

80

rem/@font-size-base;

}

由于用了less,事先定義了一個變量來儲存标注稿基準字型大小:

@font-size-base: 75;

是以px2rem的轉換變得非常容易,如上所示。less編譯之後,會将正确的rem值計算出來:

?

1 2 3 4

.btn {

width

:

5.52

rem;

height

:

1.06666667

rem;

}

到此,lib-flexible的基本實踐就結束了,不過還有一個問題,就是retina屏的問題,到現在都還沒提到@3x下圖的那些切圖怎麼辦,其實很簡單,借助html元素的data-dpr屬性,可以輕松實作另一種媒介查詢,以便在devicePixelRatio>=2的時候啟用@3x下的圖檔,還是以安卓下載下傳按鈕的樣式為例,寫法是:

?

1 2 3 4 5 6

.btn-android {

background-image

:

url

(

"../img/@2x/[email protected]@version"

);

[data-dpr=

"3"

] & {

background-image

:

url

(

"../img/@3x/[email protected]@version"

);

}

}

這下就OK了,原先還不知道data-dpr有什麼作用,現在看看,作用還是挺大的。

注:

  • 1) 由于使用了grunt建構,是以需要先安裝node ,git,再通過npm安裝grunt 和bower
  • 2)考慮到将來可能要做全屏滾動的效果,是以這頁面一開始就是用fullpage.js來做的,通過bower引入了jquery跟fullpage.js的庫
  • 3) 子產品化用到了requirejs
  • 4)運作grunt default完成建構,再運作grunt server啟動靜态伺服器預覽。

以上就是實作淘寶彈性布局方案lib-flexible實踐的全部過程,希望對大家的學習有所啟發。

繼續閱讀