一、viewport:移動設備(包括webview)用來顯示網頁的那一塊區域;
二、devicePixelRatio屬性(別名像素比,簡稱dpr):window.devicePixelRatio =物理像素 / 獨立像素(css中的px);
三、rem:相對於根元素(即html元素)font-size計算值的倍數(我之前真不大清楚)。css
上面許多概念請看這篇 www.cnblogs.com/2050/p/3877…html
下面是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'] = {}));
複製代碼
我之前作移動端適配都採用百分百佈局,一般粘貼複製下面一段代碼,並且還樂此不疲vue
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
複製代碼
實際網上大部分移動端適配的方案都是rem佈局外加使用flexble.js原理,其核心要點(以iPhone6爲例)以下:
一、手動設置上述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」;
二、若是沒有手動添加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;node
因此flexible.js經過這兩種方式佈局,若是在css文件中設置一個div標籤的寬度,設計圖的長度是75px,第一種方式需設置width=2rem,第二種需設置width=1rem。android
以上呈現的方式適合蘋果手機,安卓手機flexble.js默認dpr值爲1,因此網上大部分的結論是flexible.js不適合安卓手機,既然安卓手機devicePixelRatio 也有約等於1,2,3,爲何就不能用呢。並且flexble.js只考慮豎屏的狀況,設置doc寬度不大於540px。git
工做中一直在安卓webview中進行web開發,從接手項目的css樣式中都是100%佈局,後來機器愈來愈多,分辨率600到1920不等,並且rom層默認設置也會對機型的分辨率有影響。關鍵佈局是否合理,通常由產品和UI決定,原項目經過media標籤對各類機型不一樣樣式適配,冗餘度和複雜度高,可維護性差。
UI設計師一般以一種機型爲基準進行設計,好比說UI設計師給的設計圖是1920 x 1200,那麼她會以這個機型衡量其它機型,如960 x 600的機型就應該等比例縮放。佈局從視覺上看不會有差別,這才達到她理想的兼容適配。github
根據上面遇到的狀況,若是以rem佈局,在1920 x 1200分辨率下是1rem=192px,那麼在960 x 600分辨率的機型中1rem = 96px,這裏將document寬度十等分。既然這樣我也能夠運用flexble.js,只需修改相關邏輯就行,並且改動不大,修改後以下:web
'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;
}
複製代碼
修改點以下:
一、將flexible.js手動設置viewport標籤的邏輯刪除,若是不刪除,dpr不是1的狀況,在viewport標籤縮放scale值是默認的1.0,1px可能就不是真正1像素寬度,那麼隨之而來就是你要適配1像素寬度,網上有不少方案,好比僞元素,圖片代替,另外一種就是縮放,因此用代碼動態設置viewport標籤,scale值會隨dpr值變化,1px也會縮放的,這裏面我通常在樣式中設置1像素寬度的單位就是px,而非rem;
二、將flexible.js對iPhone的retain屏1,2,3設置三種狀況一樣適用安卓機,而且document寬度由540改成不限制寬度。
三、命名改變了,flexible改爲msoFlex,爲了區別flexible.js。vue-cli
1在vue工程中引用mso-flex,先npm install mso-flex --save-dev,而後在main.js頭部引用import "mso-flex/mso-flex.js"便可(能夠用yarn)。
二、字體的問題,通常若是UI和產品不介意字體有瑕疵的話能夠不用修改,由於用rem換算的過程當中,字體可能會出現奇數和小數的字體(13px或者13.343px),這樣的字體像素眼看起來有模糊。若是你用的是scss樣式處理器@mixin方法對data-dpr判斷,根據屬性值的不一樣,設置字體大小。
三、rem和px轉換,需安裝postcss-pxtorem,在書寫樣式的時候直接在psd文件中量大小像素做爲長寬的數值,不須要每次用計算器將px換算成rem,方便快捷。
爲了觀察效果,直接準備運行vue-cli工程,發現vue-cli大變樣,真的體會到在前端日益發展的過程當中學不動了。
經過官網 yarn 命令啓動vue服務,前後安裝
yarn add -D mso-flex;
yarn add -D sass-loader node-sass;
yarn add -D postcss-pxtorem;
下面是工程目錄(真的簡潔不少)
以上就是在webview 安卓機佈局實踐,沒有太多原理,也非長篇大論,若是各位大神看到開心,麻煩點個贊,謝謝哈~_~,實踐的代碼地址:github.com/yuelinghuny…