详解flexible源码(@0.3.2)

flexible源码详解(@0.3.2)

有关文章中不理解的移动端适配的概念,建议大家先去储备一些移动端适配方面的知识,当然,我之后也会做一个比较详尽的移动端适配方案的总结。

  • 最近在看有关移动端适配的东西,lib-flexible是一款比较经典的适配框架,为了彻底的理解他,看了一下它的源码。我会在这里做逐段的分析,这里有完整的源码,大家可以参考一下。有任何意见和建议,希望大家积极讨论。

  • 说回flexible,它的主要思想还是使用rem单位,根据屏幕大小的变化,动态改变根字体的大小,并且识别设备的dpr,动态配置出适合当前环境的viewport配置。下面我们看下它的源码

  • 第一部分就是统一定义后面要用到的变量,都比较好理解

      var doc = win.document;
      //定义文档的根节点
      var docEl = doc.documentElement;
      //查询符合条件的meta标签
      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 = {});
    
  • 紧接着是对metaEl的判断,一般情况下,我们都会在html文件中做这样的设置。

      <meta name="viewport" content="width=device-width, initial-scale=1.0">
    
  • 如果我们已经做了这样的设置,flexible将会根据我们meta标签内的配置,对dpr和scale赋值,flexibleEl同理。我个人建议,如果要使用flexible,就不要在html做手动的配置,除非有特殊的要求

      if (metaEl) {
      	console.warn('将根据已有的meta标签来设置缩放比例');
      	//这里会把meta标签的content属性内容转化为一个数组
      	var match = metaEl.getAttribute('content').match(/initial\-scale=([\d\.]+)/);
      	//如果我们的content中有值
      	if (match) {
      		//按照默认设置,这里的dpr和scale都为1
      		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));
      		}
      	}
      }
    
  • 为什么不建议在html中配置上述的meta,原因就在这里,如果执行了以上代码,dpr和scale都已经存在值,那么下面的判断和一系列操作将不会执行。这里还有一个非常诡异的点,既然定义安卓手机的判断标准,但是并没有做出有效的区分,只是把iPhone以外的手机统一处理,可能当时的安卓机并没有dpr>=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;
      }
    
  • 代码执行至此,dpr和scale都已经有了值,首先,flexible会在html标签上创建“data-dpr”来标识当前设备的dpr,其次,如果到了这里还没有meta标签的配置,flexible会帮我们自动创建meta标签。

      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');
      	//这里就是判断是否存在<head>标签
      	if (docEl.firstElementChild) {
      		docEl.firstElementChild.appendChild(metaEl);
      	} else {
      		var wrap = doc.createElement('div');
      		wrap.appendChild(metaEl);
      		doc.write(wrap.innerHTML);
      	}
      }
    
  • 下面就是flexible能实时响应的核心,在项目初次运行、页面展示、页面大小改变的时候都会执行refreshRem()方法。这个方法中有很多有趣的设置。

      function refreshRem(){
      	//这里的width通俗理解就是页面缩放前的宽度
      	var width = docEl.getBoundingClientRect().width;
      	//width超过540时达到最大值,会影响到之后根字体的最大值。
      	if (width / dpr > 540) {
      		width = 540 * dpr;
      	}
      	//flexible中对rem的计算方式,除以十我认为是为了方便计算
      	var rem = width / 10;
      	docEl.style.fontSize = rem + 'px';
      	flexible.rem = win.rem = rem;
      }
      //监听页面的变化,触发refreshRem方法
      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);
    
  • 这里对body的操作,一开始我也不理解,其实是要清楚flexible使用的方法,它建议我们各类组件和容器的宽高布局使用rem单位,而对于字体使用px,这下就明白了,body中的fontsize是用来控制字体大小了,而不会影响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);
      }
    
  • 那么为什么要针对字体使用px单位呢?我个人的理解是flexible不希望字体的大小自适应变化,可能会影响美观,或者考虑到浏览器对最小字号的规定不统一。当然,在flexible中字体使用rem也是可以的。

flexible的原理大概就是这些,当然它会存在很多问题,需要搭配类似postcss-px2rem的插件去使用。也许他已经不是最完美的适配方案,但是flexible在移动端适配的道路上,起到了良好的过渡作用。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值