HTML5移動端自適應解決方案


自接觸移動端H5頁面以來,從未停止對H5頁面適配不同屏幕的解決方案的探索。從最初的bootstrap響應式框架來做手機適配; 后來嘗試用百分比去做H5的適配;接着又去嘗試媒體查詢,但移動端的屏幕大小個各異,各種尺寸的機型都有,難以做到不同手機適配, 后來看到京東,網易,手淘等使用rem做手機適配,使用rem前端開發者可以很方便的在各種屏幕尺寸下,做出設計圖要求的效果。本文重在說明 手機端不同的自適應解決方案的優缺點以及rem作為主流的移動端自適應布局方案的原理以及使用方案。

1. 使用rem做手機適配

什么是rem

rem是css3新增的相對長度單位,是指相對於根元素html的font-size值的大小

擴展: em也是css的相對長度單位,em作為font-size的單位時,其代表父元素的字體大小,em作為其他屬性單位時,代表自身字體大小

rem適配原理

rem布局適配的本質是等比縮放,根據不同屏幕的寬度,以相同的比例動態修改html的font-size值,它跟設計師給出的設計圖的大小沒有關系,不管設計圖給出的 是480,720,750,1080,都是將設計稿等比縮放在設備上;那么根html的font-size的值是怎么計算的呢?

  • 網易的方案是根據屏幕寬度與設計圖寬度的比值作為font-size 大小,但此時計算出的font-size值小於12px會造成一些錯誤和奇怪的問題,為此我們把比例擴大100倍,即,根html的font-size值=屏幕寬度/設計圖寬度*100 為了使比例不變,相應的設計圖元素在變為rem的過程中要除以100;

  • 阿里的方案是根據設備像素比設置scale的值,保持視口device-width始終等於設備物理像素,接着根據屏幕寬度/10動態計算根html的font-size的大小, 設計稿的像素單位如何換成以rem為單位呢?可以用一個比例來計算,具體是將設計圖分成100份,每一份的寬度用a來表示,同時認定1rem單位為10a,即1rem=10a; 如設計稿寬度為750px,此時:

750px=100a --- 1a=7.5px

1rem=10a --- 1rem=75px

同理,如果設計稿總寬度是640px,則1rem=64px。

rem適配使用方案

(function(doc, win) {
  var docEl = doc.documentElement,
    resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize',
    recalc = function() {
      var clientWidth = docEl.clientWidth;
      if(!clientWidth) return;
      docEl.style.fontSize = (clientWidth / 7.5) + 'px';
    };
  if(!doc.addEventListener) return;
  win.addEventListener(resizeEvt, recalc, false);
  doc.addEventListener('DOMContentLoaded', recalc, false);
})(document, window);

小米移動端使用方案(基於720的設計圖)

!function(e){
    var t = e.document,
        n = t.documentElement,
        i = e.devicePixelRatio || 1,
        a = "orientationchange" in e ? "orientationchange" : "resize",
        d = function(){
            var e = n.getBoundingClientRect().width || 360; //  Element.getBoundingClientRect()方法返回元素的大小及其相對於視口的位置。
            (1 == i || e > 720) && (e = 720), n.style.fontSize = e/7.2 + "px" // 小米就是6啊 設計圖都是安卓 720*1280界面
        };

    n.setAttribute("data-dpr", i),
    t.addEventListener && (e.addEventListener(a, d, !1), "complete" === t.readyState ||
    t.addEventListener("DOMContentLoaded", 
        function() { 
            setTimeout(d)
        }, !1)
    )

} (window)

flexible移動端使用方案

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

    docEl.setAttribute('data-dpr', dpr);
    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);
        }
    }
    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'] = {}));

 

手機淘寶適配方案(基於750的設計圖)

!function(e, t) { 
    var n = t.documentElement,
        d = e.devicePixelRatio || 1; // 設備DPR
    function i() { 
        var e = n.clientWidth / 3.75; // iPhone 6 布局視口375
        n.style.fontSize = e + "px" 
    } 
    if (function e() { t.body ? t.body.style.fontSize = "16px" : t.addEventListener("DOMContentLoaded", e)}(),
        i(),
        e.addEventListener("resize", i), 
        e.addEventListener("pageshow", function(e) { e.persisted && i() }), d >= 2){
        var o = t.createElement("body"), a = t.createElement("div");
        a.style.border = ".5px solid transparent", o.appendChild(a), n.appendChild(o), 
        1 === a.offsetHeight && n.classList.add("hairlines"), n.removeChild(o) 
    } 

}(window, document)

 

2. vw/vh

vw/vh是基於Viewport視窗的長度單位,這里的視窗(Viewport)指的就是瀏覽器可視化的區域,視口單位包括以下4個

  • vw : 1vw 等於視口寬度的1%
  • vh : 1vh 等於視口高度的1%
  • vmin : 選取 vw 和 vh 中最小的那個
  • vmax : 選取 vw 和 vh 中最大的那個

假如你的設計圖是750px的寬度,從vw、vh的原理上看100vw=750px,即1vw=7.5px,我們可以根據設計圖上的px值轉換成對應的vw的值; 可以使用vw適配我們的頁面的地方:

  • 容器適配,可以使用vw
  • 文本的適配,可以使用vw
  • 大於1px的邊框、圓角、陰影都可以使用vw
  • 內距和外距,可以使用vw

兼容性問題(在移動端 iOS 8 以上以及 Android 4.4 以上獲得支持,並且在微信 x5 內核中也得到完美的全面支持),所以仍有多種機型會存在 兼容性的問題,如果不考慮這些兼容性的問題,可以大膽使用vw。

3. 百分比

百分比做手機適配的方法是子元素相對於父元素的百分之多少,做手機端的適配,整體布局可以實現不同屏幕的縮放,但 元素內的字體以及位置難以做到不同屏幕上的放大和縮小,百分比布局只適合布局簡單的頁面,定制化要求比較高復雜的頁面 實現很困難。

4 媒體查詢

媒體查詢是早期使用的手機適配方案,通過查詢設備的寬度執行不同的css代碼,展示設計圖的UI; 針對移動和PC維護同一套代碼時使用的較多,例如使用Bootstrap做響應式布局時,底層使用的就是媒體查詢。 該方案缺點是代碼量比較大,維護不方便,做定制化需求時不能很好的滿足效果,以及為了兼顧移動端和PC端 各自特有的交互方式不能單獨使用。

總結

隨着移動端的發展會出現更多的適配方案,目前主流的仍是rem做手機端的適配,隨着瀏覽器對vw的支持,使用vw做手機端的適配也是不錯的選擇, 如果你擔心你的移動端項目有更好的兼容性以及更好的適配不同的設備,請選擇rem作為你的適配方案,如果不用考慮兼容性的問題可以使用vw作為您的方案, 加入你的公司需要使用響應式布局移動和PC維護同一套代碼,可以考慮使用基於媒體查詢適配方案外加less和sass減少工作量。百分比做手機適配只能做簡單 的頁面。

參考資料


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM