【每天学一点】移动端适配库‘lib-flexible’核心是什么呢?

208 阅读2分钟

当使用rem来适配各种移动设备时,我们很多时候选用lib-flexible包,该包中通过监听视图窗口的大小resize event;通过它动态的改变根元素的font-size的大小;进而达到适配的效果;

docEl.style.fontSize = rem + 'px';

lib-flexible包代码精简,我们来看看源码:lib-flexible.js中核心代码:

refreshRem

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

resize

MDN:resize event

此处使用 setTimeout 节流;

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

pageshow

MDN:pageshow event

当浏览器由于导航而显示窗口的文档时,pageshow事件被发送到Window。这包括:

  • 初始加载页面
  • 从同一窗口或选项卡中的另一个页面导航到该页面
  • 在移动操作系统上恢复被冻结的页面
  • 使用浏览器的前进或后退按钮返回到页面

在初始页面加载期间,pageshow事件在load事件之后触发。

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

event.persisted

MDN:event.persisted

persisted属性指示网页是否从缓存加载。

readyState

MDN:Document.readyState

Document.readyState 属性描述dom文档的加载状态

let string = document.readyState; readyState值有以下值:

  • loading:document 仍然在加载
  • interactive: 文档已经完成了加载,并且已经解析了文档,但是scripts, images, stylesheets 和 frames等子资源仍在加载
  • complete:文档和所有子资源已经加载完毕。该状态指示加载事件即将触发。

当该readyState属性的值发生变化时,在文档对象上触发readystatechange事件。

  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);
    }
lib-flexible源码

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