webview佈局適配實踐
1、viewport:移動裝置(包括webview)用來顯示網頁的那一塊區域;
2、devicePixelRatio屬性(別名畫素比,簡稱dpr):window.devicePixelRatio =物理畫素 / 獨立畫素(css中的px);
3、rem:相對於根元素(即html元素)font-size計算值的倍數(我以前真不大清楚)。
上面許多概念請看這篇 ofollow,noindex">www.cnblogs.com/2050/p/3877…
二、淘寶flexible.js
下面是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'] = {})); 複製程式碼
我以前做移動端適配都採用百分百佈局,通常貼上複製下面一段程式碼,而且還樂此不疲
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"> 複製程式碼
實際網上大部分移動端適配的方案都是rem佈局外加使用flexble.js原理,其核心要點(以iPhone6為例)如下:
1、手動設定上述meta viewport( initial-scale=1.0)標籤,載入flexble.js成功後會在html標籤設定data-dpr=1,font-size=37.5px;
1rem = 37.5px = doc.documentElement.getBoundingClientRect().width/10 + “px”;
2、如果沒有手動新增meta viewport標籤,flexble.js會動態設定此標籤,但是和上面的情況有區別,會執行如下流程:
a、獲取devicePixelRatio(dpr)值,iPhone6值為2;
b、設定scale = 1/dpr = 0.5;
c、渲染viewport標籤,動態設定html標籤屬性data-dpr=2,font-size=75px;;
d、rem與1的獲取方式相同,只不過這裡面的1rem=75px;
所以flexible.js通過這兩種方式佈局,如果在css檔案中設定一個div標籤的寬度,設計圖的長度是75px,第一種方式需設定width=2rem,第二種需設定width=1rem。
以上呈現的方式適合蘋果手機,安卓手機flexble.js預設dpr值為1,所以網上大部分的結論是flexible.js不適合安卓手機,既然安卓手機devicePixelRatio 也有約等於1,2,3,為什麼就不能用呢。而且flexble.js只考慮豎屏的情況,設定doc寬度不大於540px。
三、我遇到的情況
工作中一直在安卓webview中進行web開發,從接手專案的css樣式中都是100%佈局,後來機器越來越多,解析度600到1920不等,而且rom層預設設定也會對機型的解析度有影響。關鍵佈局是否合理,一般由產品和UI決定,原專案通過media標籤對各種機型不同樣式適配,冗餘度和複雜度高,可維護性差。
UI設計師通常以一種機型為基準進行設計,比如說UI設計師給的設計圖是1920 x 1200,那麼她會以這個機型衡量其它機型,如960 x 600的機型就應該等比例縮放。佈局從視覺上看不會有差異,這才達到她理想的相容適配。
四、修改flexible.js
根據上面遇到的情況,如果以rem佈局,在1920 x 1200解析度下是1rem=192px,那麼在960 x 600解析度的機型中1rem = 96px,這裡將document寬度十等分。既然這樣我也可以運用flexble.js,只需修改相關邏輯就行,而且改動不大,修改後如下:
'use strict' const doc = window.document; const docEl = doc.documentElement; let metaEl = doc.querySelector('meta[name="viewport"]'); let dpr=0,scale=0,tid=null; let msoFlex = {}; if (!dpr && !scale) { const devicePixelRatio = window.devicePixelRatio; if (devicePixelRatio >= 3 && (!dpr || dpr >= 3)) { dpr = 3; } else if (devicePixelRatio >= 2 && (!dpr || dpr >= 2)){ dpr = 2; } 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 { const wrap = doc.createElement('div'); wrap.appendChild(metaEl); doc.write(wrap.innerHTML); } } const refreshRem = function(){ const width = docEl.getBoundingClientRect().width; const rem = width / 10; docEl.style.fontSize = rem + 'px'; msoFlex.rem = rem; } window.addEventListener('resize', function() { clearTimeout(tid); tid = setTimeout(refreshRem, 300); }, false); window.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(); msoFlex.dpr = dpr; msoFlex.refreshRem = refreshRem; msoFlex.rem2px = function(d) { var val = parseFloat(d) * this.rem; if (typeof d === 'string' && d.match(/rem$/)) { val += 'px'; } return val; } msoFlex.px2rem = function(d) { var val = parseFloat(d) / this.rem; if (typeof d === 'string' && d.match(/px$/)) { val += 'rem'; } return val; } 複製程式碼
修改點如下:
1、將flexible.js手動設定viewport標籤的邏輯刪除,如果不刪除,dpr不是1的情況,在viewport標籤縮放scale值是預設的1.0,1px可能就不是真正1畫素寬度,那麼隨之而來就是你要適配1畫素寬度,網上有很多方案,比如偽元素,圖片代替,另一種就是縮放,所以用程式碼動態設定viewport標籤,scale值會隨dpr值變化,1px也會縮放的,這裡面我一般在樣式中設定1畫素寬度的單位就是px,而非rem;
2、將flexible.js對iPhone的retain屏1,2,3設定三種情況同樣適用安卓機,並且document寬度由540改為不限制寬度。
3、命名改變了,flexible改成msoFlex,為了區別flexible.js。
五、專案微調
1在vue工程中引用mso-flex,先npm install mso-flex --save-dev,然後在main.js頭部引用import "mso-flex/mso-flex.js"即可(可以用yarn)。
2、字型的問題,一般如果UI和產品不介意字型有瑕疵的話可以不用修改,因為用rem換算的過程中,字型可能會出現奇數和小數的字型(13px或者13.343px),這樣的字型畫素眼看起來有模糊。如果你用的是scss樣式處理器@mixin方法對data-dpr判斷,根據屬性值的不同,設定字型大小。
3、rem和px轉換,需安裝postcss-pxtorem,在書寫樣式的時候直接在psd檔案中量大小畫素作為長寬的數值,不需要每次用計算器將px換算成rem,方便快捷。
六、vue-cli3.0使用配置
為了觀察效果,直接準備執行vue-cli工程,發現vue-cli大變樣,真的體會到在前端日益發展的過程中學不動了。
通過官網 yarn 命令啟動vue服務,先後安裝
yarn add -D mso-flex;
yarn add -D sass-loader node-sass;
yarn add -D postcss-pxtorem;
下面是工程目錄(真的簡潔很多)

直接可以引用,mso-flex.js在main.js 頭部直接可以引用

postcss-pxtorem在package.json配置,其中“rootValue":192的值是根據設計稿的長度除10設定的。 pxtorem中,對於想忽略的px寫成大寫即可,諸如 width:50PX;
以上就是在webview 安卓機佈局實踐,沒有太多原理,也非長篇大論,如果各位大神看到開心,麻煩點個贊,謝謝哈~_~,實踐的程式碼地址: github.com/yuelinghuny…