最近须要一款移动端的产品,当时须要赶工期,在参考了天猫的flexbox布局和手淘的
rem布局方案后,决定选用libflexible。作完项目以后,稍有空闲时间,决定看看libflexible
如何实现动态设置根元素的字体,从而经过rem的方式改变其余元素大小javascript
首先咱们看一下flexible须要哪些属性css
;(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 = {});
在函数运行时,咱们须要将windows对象注入,而后在看看windows下是否已经存在lib(便是否已经有
使用过flexible)了,注入完以后,咱们依次得到doc,docEl,metaEl,flexibleEl,其中metaEl
是为了判断咱们是否已经有预设好的viewport,flexibleEl则是判断咱们已经手动设置好dpr来避免
flexible库动态设置dprhtml
if (metaEl) { console.warn('将根据已有的meta标签来设置缩放比例'); var match = metaEl.getAttribute('content').match(/initial\-scale=([\d\.]+)/); if (match) { scale = parseFloat(match[1]); dpr = parseInt(1 / scale); } }
若是metaEl存在的话,意味着页面上存在形如<meta name="viewport" content="initial-scale=1">
的标签,此时咱们已经明确了咱们须要的缩放,再也不须要flexible的介入,缩放值scale直接
使用预设的initial-scale,经过咱们预设的的缩放,咱们的layout viewport将会是 ideal viewport/scale,若是咱们的initual-scale为1的话
且ideal为414px的话,咱们的layout viewport将会是414pxjava
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)); } } }
若是咱们没有设置初始的viewport可是有<meta name="flexible" content="initial-dpr=2" />
这样的flexible自身的预设
那么咱们将会有预设的dpr,此时flexible将根据咱们预设的dpr经过scale=1/dpr
的方式来计算出咱们的缩放,进而影响layout viewport
的大小android
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; }
接下来,若是咱们既没有经过<meta name="flexible" content="initial-dpr=2" />
这种方式预设dpr,
也没有经过<meta name="viewport" content="initial-scale=1">
的方式预设缩放,此时flexible开始根据
设备的dpr来动态计算缩放。对于非苹果设备,flexible设置dpr为1,对于苹果设备,iPhone3如下非retina屏,dpr为1
iPhone4-iPhone6为retina屏,dpr为2,iPhone6Plus为retina HD屏,dpr为3,因为flexible是一个专一于移动端
的解决方案,因此平板(包括iPad)或者桌面端的dpr都为1windows
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); } }
计算完缩放后,将获取的dpr值设置到根元素上,这样咱们就能够经过如下方式:缓存
.selector { width: 2rem; border: 1px solid #ddd; } [data-dpr="1"] .selector { height: 32px; font-size: 14px; } [data-dpr="2"] .selector { height: 64px; font-size: 28px; } [data-dpr="3"] .selector { height: 96px; font-size: 42px; }
为不一样dpr的屏幕设置不一样的字体大小,字体之因此不用rem布局,是由于app
咱们不但愿文本在Retina屏幕下变小,另外,咱们但愿在大屏手机上看到更多文本,以及,如今绝大多数的字体文件都自带一些点阵尺寸,一般是16px和24px,因此咱们不但愿出现13px和15px这样的奇葩尺寸。iphone
同时当不存在metaEl时,flexible动态生成一条<meta name="viewport" content="initial-scale=${scale},maximum-scale=${scale},minimum-scale=${scale},user-scalable=no">
的标签,若是<html>
下存在元素(如<head>
等元素)那么,将meta标签插入,若是没有,就将meta标签用一个div包装,而后
经过document.write写入到文档中ide
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; }
好了,缩放和dpr都设置好了,下一步咱们要设置根元素的font-size了,这样咱们能够经过rem的方式
适配不一样屏幕。首先咱们须要经过docEl.getBoundingClientRect().width
得到layout viewport的宽度,
而后将layout viewport宽度分为10份,1份为1rem。至于设置540px,是为了让在ipad横屏这种状况下浏览页面,不至于由于拉伸适配后体验太差。固然这仍是有一点点问题的
由于这样10rem将不会是ipad的满屏了。固然这是移动端解决方案,并无考虑平板和桌面端
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); }
body上设置12 * dpr的font-size值,为了重置页面中的字体默认值,否则没有设置font-size的元素会继承html上的font-size,变得很大。
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; }
最后,flexible提供了两个工具函数px2rem和rem2px,这里就不在阐述了
看完flexible,实实在在的感觉到rem布局的妙处,经过动态设置缩放系数的方式,让layout viewport与设计图对应,极大地方便了重构,同时也避免了1px的问题,然而略有遗憾的就是在安卓和ipad上的表现不佳了。