这里主要是对淘宝适配方案:flexible进行源码分析。
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 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 ; (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) { if (devicePixelRatio >= 3 && (!dpr || dpr >= 3 )) { dpr = 3 ; } else if (devicePixelRatio >= 2 && (!dpr || dpr >= 2 )) { dpr = 2 ; } else { dpr = 1 ; } } else { 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' ] = {}));
从代码里我们可以看到,这里dpr的值就是viewport中的initial-scale属性,一般情况下,这个值是不会变的。
最终我们得到的结果就是,根据屏幕宽度,动态计算出了根元素的font-size值。
当然,我们还有其他的方法动态修改根元素的font-size:
媒体查询
优点:CSS的方式,不会出现闪动
缺点:代码冗余,适配不够精确
用calc(vw / x)计算
优点:简单、代码量少
缺点:需要支持vm(未来会逐步支持)