Flexible方案是淘宝的移动端适配方案,相关的文章在这里
1 2 3 4
| ;(function(win, lib) { })(window, window['lib'] || (window['lib'] = {}));
|
这是一个插件的写法之一。你还可以在这里找到其它方法(IIFE)[http://benalman.com/news/2010/11/immediately-invoked-function-expression/]。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| 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" content="initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
元素。如果有则从页面中取出对应的缩放系数(scale)。然后通过(scale)算出对应的设备像素比(dpr).
如果页面上写了<meta name="flexible" content="initial-dpr=2,maximum-dpr=3" />
则会取initial-dpr
和maximum-dpr
之中最大者。
如果以上两都都没设置。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| 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倍屏使用2倍方案,3的屏3倍的方案,其余的用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; }
|
这里有检测当在安卓下的时候并没有使用高清方案,具体的原因可见issue
大概的意思就是说有些安卓设置的initial-scale
不为1的时候会无效。。由于这个原因会产生一些问题比如1px边框线的问题,具体可见网友的文章基于淘宝弹性布局方案lib-flexible的问题研究
这里在实际的工作过程假设有用vue
的话,在移动端的适配过程中假设引用了一个vue-star-rating
组件由于组件的star-size
是设置的是数值,然后当你在安卓下的时候会发现这个星星会变得很大解决办法是利用lib.flexible.dpr
或者lib.flexible.rem
来动态设置这个组件的星星的大小,这里暂且只发现这种解决方案,如果有其它的方法或者其它的评价组件,或者自己写一个?欢迎指正-^.^-。
接下来设置页面根元素的data-dpr
属性,
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| 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); } }
|
接下来是刷新rem函数计算出rem的值:
1 2 3 4 5 6 7 8 9
| 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; }
|
docEl.getBoundingClientRect().width
这里是计算出页面在视窗里面的宽度,具体可见这里。
当宽度大于540*dpr
的时候,则最大为540 * dpr
。rem值为width / 10,即假设是750的iphone6的时候rem值为75,设置根元素html
的字体大小值。
接下来是监听页面事件resize
和pageshow
刷新页面rem大小。
1 2 3 4 5 6 7 8 9 10
| 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.document的状态。
1 2 3 4 5 6 7
| 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); }
|
当文档的readyState
为complete
或者为DOMContentLoaded
即页面内容载入完成,设置body的字体大小。
最后获得主动获得rem值,页面的dpr赋值到lib.flexible
下还有一些工具函数rem2px
和px2rem
。这里在实际的工作过程中,lib.flexible.dpr
相当的有用,比如
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 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; }
|
2017.10.19后续
翻看了ant-design中关于1px线的处理再和libflexible.js结合整理出了以下的sass函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
| @mixin hairline($color: #C7C7C7, $direction: left, $radius: 0) { @if $direction != 'all' { border-#{$direction}: 1PX solid $color; [data-dpr="1"] & { border-#{$direction}: none; @media (min-resolution: 2dppx), (min-resolution: 192dpi) { position: relative; &:before { content: " "; position: absolute; border-#{$direction}: 1PX solid $color; @if $direction == 'top' { left: 0; top: 0; right: 0; height: 1PX; transform-origin: 0 0; transform: scaleY(0.5); } @else if $direction == 'right' { right: 0; top: 0; bottom: 0; width: 1PX; transform-origin: 100% 0; transform: scaleX(0.5); } @else if $direction == 'bottom' { left: 0; bottom: 0; right: 0; height: 1PX; transform-origin: 0 100%; transform: scaleY(0.5); } @else { left: 0; top: 0; bottom: 0; width: 1PX; transform-origin: 0 0; transform: scaleX(0.5); } } @media (min-resolution: 3dppx), (min-resolution: 288dpi) { &:before { @if $direction == 'top' { transform: scaleY(0.33); } @else if $direction == 'right' { transform: scaleX(0.33); } @else if $direction == 'bottom' { transform: scaleY(0.33); } @else { transform: scaleX(0.33); } } } } } } @else { border: 1PX solid $color; border-radius: $radius; [data-dpr="1"] & { @media (min-resolution: 2dppx), (min-resolution: 192dpi) { position: relative; border: none; transform: translateZ(0); &:before { content: ''; position: absolute; left: 0; top: 0; width: 200%; height: 200%; border: 1PX solid $color; border-radius: $radius * 2; transform-origin: 0 0; transform: scale(0.5); box-sizing: border-box; pointer-events: none; z-index: -1; } } } } } @mixin hairline-remove($direction: left) { border-#{$direction}: 0; &:before { display: none !important; } }
|
大体的意思是当处理高清方案的时候边框线是用的那个border-#{$direction}
或者border
边框线,然后利用缩放功能来获得细腻的边框线,当在安卓下的时候由伪类来显示那个边框线。
后面的更新的2.0版本,接下来将会进行一些思考和研究。
Todolist: