手淘flexible.js框架使用和源代码讲解

手淘框架是一个用来适配移动端的js框架,下面咱们来说解一下如何使用手淘的这套框架。css

其实手淘框架的核心原理就是根据不一样的width给网页中html跟节点设置不一样的font-size,而后全部的距离大小都用rem来代替,这样就实现了不一样大小的屏幕都适应相同的样式了,首先咱们来讲一下经常使用的移动设备。html

iphone6:    375px*667px  实际像素:750px*1334pxjquery

iphone5:   320px*568px   实际像素:640px*1136pxandroid

iphone4:   320px*480px   实际像素:640px*960pxchrome

nexus5X(安卓): 411px *731px 实际像素:411px*731px浏览器

以上数据都来自于chrome浏览器- -!!!sass

其实咱们的iphone手机都是视网膜屏幕,因此咱们的实际像素因该是无力像素*视网膜屏的倍数。app

然而咱们在实际的开发中ui给出的图通常都是750X1334的,其实iphone6的像素和ui设计的像素是同样大小的,可是咱们的开发若是都是按照6的px来设计,那么咱们的其它比6小尺寸屏幕的全部设备都会面临width不够的问题。flexible就完美的解决了这个问题。框架

应用中咱们只要设置好他的公共比的像素就ok了,好比说若是ui图的像素是750,那咱们须要的就是750/10,咱们须要的就是75,咱们全部的width的固定px就均可以变换成用rem像素代替实现样式统一例如咱们width须要200px那么咱们就能够这样写:less

width=200rem/75;从而实现样式的兼容(特别注意:由于css不支持样式的计算,咱们须要用less活着sass相似的css编译执行就能够获得最终的rem的值了)

另外,咱们根据不一样dpr能够设置一些不一样的样式来实现视网膜屏幕的高清屏幕!

[data-dpr="1"] .selector {
width: 10px;
height: 32px;
font-size: 14px;
}
[data-dpr="2"] .selector {
width: 20px;
height: 64px;
font-size: 28px;
}

咱们根据不一样的自定义属性data-dpr来设置不一样的width和height 从而达到不一样dpr屏幕具备不一样的属性!(这个位置通常用来处理图片。。)

下面咱们来说解一下flexible的源代码:

;(function(win, lib) {

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

首先这个最外层结构是最基本的封装类库的方法:函数当即调用,这样能够防止封装方法污染全局变量,jquery的源码也是同样的道理!

    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 = {});

doc取文档的document对象

docEl取到了咱们html为根的整个dom树,后期咱们须要向html插入dpr和font-size就是用这个属性

metaEl取meta标签里面name=viewport的元素,没有返回空,为了判断是否有本身设置的meta值来作一些逻辑

flexibleEl取meta标签里面name=flexible的元素,没有返回空,为了判断用户是否本身手动的设置了一些meta值

dpr表示的是取你手机屏幕的dpr值

scale表示取你meta里面的scale,会根据不一样的scale设置dpr

咱们须要了解的大概就是上面的这些须要用到的属性!

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

 

这段代码是判断你的meta标签里面是否是设置了name=viewport属性,若是你设置了viewport而且设置了initial-scale(初始屏幕的大小)咱们将取到这个值做为dpr(作了逻辑运算,若是你的页面初始的放大为二,那么咱们的dpr会设置成0)

同理咱们若是动态设置了meta咱们直接就取出来而后设置dpr和scale

    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);

以后若是咱们动态设置了scale或者设置了meta标签里面的name=flexible的inital-scale,那么咱们就根据本身设置的dpr在判断iphone手机的retina屏幕的dpr比值判断不一样型号的倍数,最后咱们在html上设置了data-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);
        }
    }

以后当咱们以前没有设置metaEl标签的话,那么须要咱们手动的去建立meta标签,实现移动端的适配

     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);

这段代码的目的就是监听window里面的resize和pageshow方法来实现css样式的重绘。

函数里面就是实现取到当前设备的width以后根据width计算出rem的具体值,rem表明html的font-size,这里的rem表明的是一个自定义的rem,而不是rem属性!

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

以后咱们判断document对象是否处于complete状态,若是完成状态咱们给body一个font-size=12*dpr的值,不然咱们判断dom加载方法来实现body中的font-size的设置。这个设置是为了页面中字体的大小,而html中的font-size是为了设置页面的height,width等属性。

相关文章
相关标签/搜索