在上一片文章里,关于如何使用rem进行布局,我作了一个大概的描述。
今天这篇文字,主要说一个东西-dpr。
我们都知道,移动端开发,运行的平台主要是 ios 和 android ,做适配的时候,也会对不同的屏幕做一些特别的处理。
首先,先看一下这两家的屏幕吧。
眼花缭乱是不是,不要紧,我们只看我们需要的信息。
废话不多说,直接上结论:
IOS的移动设备:
iPhone4, 4s, 5, 5s, 6, 6+, 6s, 6s+ 都是Retina屏,
其中4, 4s, 5, 5s, 6, 6s的ppi都是326,dpr都是2,6+和6s+ppi是441,dpr是3。在ipad上同样只有dpr2和3两种的屏幕。
IOS设备 | PPI | DPR |
---|---|---|
iPhone4 | 326 | 2 |
iPhone4s | 326 | 2 |
iPhone5 | 326 | 2 |
iPhone5s | 326 | 2 |
iPhone6 | 326 | 2 |
iPhone6s | 326 | 2 |
iPhone6 PLUS | 441 | 3 |
iPhone6s PLUS | 441 | 3 |
iPad - | - | 2 |
iPad - | - | 3 |
总的来说,ios设备的dpr总体来说比较一致。
再看看安卓:
android | 尺寸 | dpr |
---|---|---|
低清设备 | - | 1 |
- | - | 1.5 |
- | - | 1.75 |
- | - | 2 |
mx2 | 800*1280 | 2.5 |
小米note | 720*1280 | 2.75 |
- | - | 3 |
三星note4 | - | 4 |
... | ... | ... |
上一篇的末尾,提到了淘宝的移动端布局方案 Flexible.
源码如下:
//去除了头部css部分
! function(a, b) {
function c() {
var b = f.getBoundingClientRect().width;
b / i > 540 && (b = 540 * i);
var c = b / 10;
f.style.fontSize = c + "px", k.rem = a.rem = c
}
var d, e = a.document,
f = e.documentElement,
g = e.querySelector('meta[name="viewport"]'),
h = e.querySelector('meta[name="flexible"]'),
i = 0,
j = 0,
k = b.flexible || (b.flexible = {});
if (g) {
console.warn("将根据已有的meta标签来设置缩放比例");
var l = g.getAttribute("content").match(/initial-scale=([\d.]+)/);
l && (j = parseFloat(l[1]), i = parseInt(1 / j))
} else if (h) {
var m = h.getAttribute("content");
if (m) {
var n = m.match(/initial-dpr=([\d.]+)/),
o = m.match(/maximum-dpr=([\d.]+)/);
n && (i = parseFloat(n[1]), j = parseFloat((1 / i).toFixed(2))), o && (i = parseFloat(o[1]), j = parseFloat((1 / i).toFixed(2)))
}
}
if (!i && !j) {
var p = (a.navigator.appVersion.match(/android/gi), a.navigator.appVersion.match(/iphone/gi)),
q = a.devicePixelRatio;
i = p ? q >= 3 && (!i || i >= 3) ? 3 : q >= 2 && (!i || i >= 2) ? 2 : 1 : 1, j = 1 / i
}
if (f.setAttribute("data-dpr", i), !g)
if (g = e.createElement("meta"), g.setAttribute("name", "viewport"), g.setAttribute("content", "initial-scale=" + j + ", maximum-scale=" + j + ", minimum-scale=" + j + ", user-scalable=no"), f.firstElementChild) f.firstElementChild.appendChild(g);
else {
var r = e.createElement("div");
r.appendChild(g), e.write(r.innerHTML)
}
a.addEventListener("resize", function() {
clearTimeout(d), d = setTimeout(c, 300)
}, !1), a.addEventListener("pageshow", function(a) {
a.persisted && (clearTimeout(d), d = setTimeout(c, 300))
}, !1), "complete" === e.readyState ? e.body.style.fontSize = 12 * i + "px" : e.addEventListener("DOMContentLoaded", function() {
e.body.style.fontSize = 12 * i + "px"
}, !1), c(), k.dpr = a.dpr = i, k.refreshRem = c, k.rem2px = function(a) {
var b = parseFloat(a) * this.rem;
return "string" == typeof a && a.match(/rem$/) && (b += "px"), b
}, k.px2rem = function(a) {
var b = parseFloat(a) / this.rem;
return "string" == typeof a && a.match(/px$/) && (b += "rem"), b
}
}(window, window.lib || (window.lib = {}));
很显然,这是经过混淆的代码。
经过一番挣扎,成功实现肉身还原。
核心方法如下:
if (metaEl) {
console.warn('将根据已有的meta标签来设置缩放比例');
var match = metaEl.getAttribute('content').match(/initial-scale=([\d.]+)/);
if (match) {
scale = parseFloat(match[1]);
dpr = parseInt(1 / scale);
}
//如果在meta标签中,我们手动配置了flexible,则使用里面的内容
} 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 = window.navigator.appVersion.match(/android/gi);
var isIPhone = window.navigator.appVersion.match(/iphone/gi);
//devicePixelRatio这个属性是可以获取到设备的dpr的
var devicePixelRatio = window.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;
// 其他设备仍旧使用1倍的方案
}
scale = 1 / dpr;
}
显而易见,这里只对ios作了处理,android 仍采用了1倍的布局方案。
个人觉得,android手机屏幕大小,宽高比是花开满地,要做的调整真的是太多了。如果根元素的font-size尺寸不对,页面效果不用多说。
就算把当前的设备信息都考虑进去了,那以后呢。
所以,考虑开发,维护,兼容性...淘宝这么做还是有道理的。
恩,也有看另一种方案,hotcss.js
http://imochen.github.io/hotcss/
(function(window, document) {'use strict'; //给hotcss开辟个命名空间,别问我为什么,我要给你准备你会用到的方法,免得用到的时候还要自己写。 var hotcss = {}; (function() { //根据devicePixelRatio自定计算scale //可以有效解决移动端1px这个世纪难题。 var viewportEl = document.querySelector('meta[name="viewport"]'), hotcssEl = document.querySelector('meta[name="hotcss"]'), dpr = window.devicePixelRatio || 1, maxWidth = 540, designWidth = 0; dpr = dpr >= 3 ? 3 : (dpr >= 2 ? 2 : 1); //允许通过自定义name为hotcss的meta头,通过initial-dpr来强制定义页面缩放 if (hotcssEl) { var hotcssCon = hotcssEl.getAttribute('content'); if (hotcssCon) { var initialDprMatch = hotcssCon.match(/initial\-dpr=([\d\.]+)/); if (initialDprMatch) { dpr = parseFloat(initialDprMatch[1]); } var maxWidthMatch = hotcssCon.match(/max\-width=([\d\.]+)/); if (maxWidthMatch) { maxWidth = parseFloat(maxWidthMatch[1]); } var designWidthMatch = hotcssCon.match(/design\-width=([\d\.]+)/); if (designWidthMatch) { designWidth = parseFloat(designWidthMatch[1]); } } } document.documentElement.setAttribute('data-dpr', dpr); hotcss.dpr = dpr; document.documentElement.setAttribute('max-width', maxWidth); hotcss.maxWidth = maxWidth; if (designWidth) { document.documentElement.setAttribute('design-width', designWidth); hotcss.designWidth = designWidth; } var scale = 1 / dpr, content = 'width=device-width, initial-scale=' + scale + ', minimum-scale=' + scale + ', maximum-scale=' + scale + ', user-scalable=no'; if (viewportEl) { viewportEl.setAttribute('content', content); } else { viewportEl = document.createElement('meta'); viewportEl.setAttribute('name', 'viewport'); viewportEl.setAttribute('content', content); document.head.appendChild(viewportEl); } })(); hotcss.px2rem = function(px, designWidth) { //预判你将会在JS中用到尺寸,特提供一个方法助你在JS中将px转为rem。就是这么贴心。 if (!designWidth) { //如果你在JS中大量用到此方法,建议直接定义 hotcss.designWidth 来定义设计图尺寸; //否则可以在第二个参数告诉我你的设计图是多大。 designWidth = parseInt(hotcss.designWidth, 10); } return parseInt(px, 10) * 320 / designWidth / 20; } hotcss.rem2px = function(rem, designWidth) { //新增一个rem2px的方法。用法和px2rem一致。 if (!designWidth) { designWidth = parseInt(hotcss.designWidth, 10); } //rem可能为小数,这里不再做处理了 return rem * 20 * designWidth / 320; } hotcss.mresize = function() { //对,这个就是核心方法了,给HTML设置font-size。 var innerWidth = document.documentElement.getBoundingClientRect().width || window.innerWidth; if (hotcss.maxWidth && (innerWidth / hotcss.dpr > hotcss.maxWidth)) { innerWidth = hotcss.maxWidth * hotcss.dpr; } if (!innerWidth) { return false; } document.documentElement.style.fontSize = (innerWidth * 20 / 320) + 'px'; hotcss.callback && hotcss.callback(); }; hotcss.mresize(); //直接调用一次 window.addEventListener('resize', function() { clearTimeout(hotcss.tid); hotcss.tid = setTimeout(hotcss.mresize, 33); }, false); //绑定resize的时候调用 window.addEventListener('load', hotcss.mresize, false); //防止不明原因的bug。load之后再调用一次。 setTimeout(function() { hotcss.mresize(); //防止某些机型怪异现象,异步再调用一次 }, 333) window.hotcss = hotcss; //命名空间暴露给你,控制权交给你,想怎么调怎么调。
})(window, document);
里面有这样的设置:
dpr = dpr >= 3 ? 3 : ( dpr >=2 ? 2 : 1 );
简单粗暴。
说到这里,说一下问题,也是我转去看flexible 代码的原因。
现在做的项目,使用的是hotcss , 然后出现了一个神奇的问题。
我的设备是iPhone5S ,系统版本8.3,微信版本6.3.15.
之前是6.3.13,更新到6.3.15之后,页面中的字体渲染完成之后,立刻会变大。刚开始怀疑是这些resize 的原因,断点调试,无果。
和腾讯的师兄说了一下,他并没有遇到这种情况。
然后,无意中发现,在微信字体设置中,改动一下字体,这种情况就没有了,恩,是在我的手机上没有了,在另一个同事的5s上依然有字体闪烁的情况。
我新建了一个分支,用上了flexible,表现良好,暂时没发现啥什么问题。
最后,附上我还原后的Flexible 源码, 有兴趣的小伙伴可以看看,希望能有所帮助。。
/**
- Created by kk on 16/3/28.
/
/
hotcss 可能有神奇的问题 - */
!function() {
var innerStyle = "@charset "utf-8";html{color:#000;background:#fff;overflow-y:scroll;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}html *{outline:0;-webkit-text-size-adjust:none;-webkit-tap-highlight-color:rgba(0,0,0,0)}html,body{font-family:sans-serif}body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,textarea,p,blockquote,th,td,hr,button,article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section{margin:0;padding:0}input,select,textarea{font-size:100%}table{border-collapse:collapse;border-spacing:0}fieldset,img{border:0}abbr,acronym{border:0;font-variant:normal}del{text-decoration:line-through}address,caption,cite,code,dfn,em,th,var{font-style:normal;font-weight:500}ol,ul{list-style:none}caption,th{text-align:left}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:500}q:before,q:after{content:''}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}a:hover{text-decoration:underline}ins,a{text-decoration:none}",
createStyle = document.createElement("style");
if (document.getElementsByTagName("head")[0].appendChild(createStyle),
createStyle.styleSheet)
createStyle.styleSheet.disabled || (createStyle.styleSheet.cssText = innerStyle);
else
try {
createStyle.innerHTML = innerStyle;
} catch (ex) {
createStyle.innerText = innerStyle;
}
}();
! function(window, nameSpace) {
var timer, doc = window.document,
docEl = doc.documentElement,
metaEl = doc.querySelector('meta[name="viewport"]'),
flexibleEl = doc.querySelector('meta[name="flexible"]'),
dpr = 0,
scale = 0,
Flexible = nameSpace.flexible || (nameSpace.flexible = {});
// 给Flexible 开创命名空间
//刷新rem
function refreshRem() {
var width = docEl.getBoundingClientRect().width;
//width / dpr > 540 && (width = 540 * dpr);
if(width / dpr > 540) {
width = 540 * dpr ;
}
var rootSize = width / 10;
docEl.style.fontSize = rootSize + "px",
Flexible.rem = window.rem = rootSize;
}
if (metaEl) {
console.warn('将根据已有的meta标签来设置缩放比例');
var match = metaEl.getAttribute('content').match(/initial\-scale=([\d\.]+)/);
if (match) {
scale = parseFloat(match[1]);
dpr = parseInt(1 / scale);
}
//如果在meta标签中,我们手动配置了flexible,则使用里面的内容
} 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 = window.navigator.appVersion.match(/android/gi);
var isIPhone = window.navigator.appVersion.match(/iphone/gi);
//devicePixelRatio这个属性是可以获取到设备的dpr的
var devicePixelRatio = window.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;
}
if (docEl.setAttribute("data-dpr", dpr), !metaEl)
if (metaEl = doc.createElement("meta"),
metaEl.setAttribute("name", "viewport"), //j = scale //j = scale //j = scale
metaEl.setAttribute("content", "initial-scale=" + scale + ", maximum-scale=" + scale + ", minimum-scale=" + scale + ", user-scalable=no"),
docEl.firstElementChild)
docEl.firstElementChild.appendChild(metaEl);
else {
var createDiv = doc.createElement("div");
createDiv.appendChild(metaEl),
doc.write(createDiv.innerHTML)
}
window.addEventListener("resize", function() {
clearTimeout(timer),
timer = setTimeout(refreshRem, 300);
}, !1),
window.addEventListener("pageshow", function(a) {
a.persisted && (clearTimeout(timer),
timer = setTimeout(refreshRem, 300))
}, !1),
"complete" === doc.readyState ? doc.body.style.fontSize = 17 * dpr + "px" : doc.addEventListener("DOMContentLoaded", function() {
doc.body.style.fontSize = 17 * dpr + "px"
}, !1),
refreshRem(),
Flexible.dpr = window.dpr = dpr,
Flexible.refreshRem = refreshRem,
Flexible.rem2px = function(a) {
var pxValue = parseFloat(a) * this.rem;
return "string" == typeof a && a.match(/rem$/) && (pxValue += "px"), pxValue ;
},
Flexible.px2rem = function(a) {
var remValue = parseFloat(a) / this.rem;
return "string" == typeof a && a.match(/px$/) && (remValue += "rem"), remValue ;
}
}(window, window.lib || (window.lib = {}));
运行截图:
亲测无误,希望能给大家带来一点帮助,谢谢。