webview布局适配实践
一、相關概念
1、viewport:移動設備(包括webview)用來顯示網頁的那一塊區域;
2、devicePixelRatio屬性(別名像素比,簡稱dpr):window.devicePixelRatio =物理像素 / 獨立像素(css中的px);
3、rem:相對于根元素(即html元素)font-size計算值的倍數(我以前真不大清楚)。
上面許多概念請看這篇 http://www.cnblogs.com/2050/p/3877280.html
二、淘寶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;
??下面是工程目錄(真的簡潔很多)
上面vue.config.js是新建配置相關loader, 但是3.0不需要配sass-loader,直接可以引用,mso-flex.js在main.js 頭部直接可以引用 postcss-pxtorem在package.json配置,其中“rootValue":192的值是根據設計稿的長度除10設置的。 pxtorem中,對于想忽略的px寫成大寫即可,諸如 width:50PX;
以上就是在webview 安卓機布局實踐,沒有太多原理,也非長篇大論,如果各位大神看到開心,麻煩點個贊,謝謝哈~_~,實踐的代碼地址:https://github.com/yuelinghunyu/mso-flex.git
總結
以上是生活随笔為你收集整理的webview布局适配实践的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 手把手教你写一份优质的前端技术简历
- 下一篇: 你敢在post和get上刁难我,就别怪我