五种移动端前端适配方案:
- 百分比布局 图片比例混乱
- 媒体查询 即CSS3的 meida queries
- 以天猫首页为代表的 flex 弹性布局
- 以淘宝首页为代表的 rem+viewport缩放
- rem 方式
1. 百分比布局 图片会乱
2. 媒体查询 代码冗余
media queries 实现原理
meida queries 主要是通过查询设备的宽度 来执行不同的 css 代码。
@media screen and (max-width: 600px) { /*当屏幕尺寸小于600px时,应用下面的CSS样式*/
/*你的css代码*/
}
比如:
@media screen and (min-device-width: 320px)and (-webkit-min-device-pixel-ratio: 2) {
html{
font-size:10px;
}
}
@media only screen and (min-device-width: 375px)and (-webkit-min-device-pixel-ratio: 2) {
html{
font-size:12px;
}
}
@media only screen and (min-device-width: 375px)and (-webkit-min-device-pixel-ratio: 3) {
html{
font-size:16px;
}
}
@media only screen and (min-device-width:412px) and (-webkit-min-device-pixel-ratio: 3) {
html{
font-size:20px;
}
}
优缺点
3. Flex弹性布局 天猫
实现原理
它的viewport是固定的:
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no">
高度定死,宽度自适应,元素都采用px做单位。
随着屏幕宽度变化,页面也会跟着变化,效果就和PC页面的流体布局差不多,在哪个宽度需要调整的时候使用响应式布局调调就行(比如网易新闻),这样就实现了『适配』。
4. rem + viewport 缩放 淘宝
这也是淘宝使用的方案,根据屏幕宽度设定 rem 值,需要适配的元素都使用 rem 为单位,不需要适配的元素还是使用 px 为单位。
知识点
像素:
中文全称为图像元素。是指在由一个数字序列表示的图像中的一个最小单位。像素仅仅只是分辨率的尺寸单位,而不是画质。
物理像素:
显示器(手机屏幕)上最小的物理显示单位,在操作系统的调度下,每一个设备像素都有自己的颜色值和亮度值。也就是设备的像素,买电脑要要检查的坏点其实就是这个玩意。
设备独立像素:
设备独立像素(也叫密度无关像素),可以认为是计算机坐标系统中得一个点,这个点代表一个可以由程序使用的虚拟像素(比如css像素),然后由相关系统转换为物理像素。
CSS像素:
也就是CSS中设置的10px就是
设备像素比dpr:
简称dpr,某个方向上(x或者y)一个CSS像素对应多少个设备像素,在缩放比例100%的时候,设备像素和CSS像素是一样的,如果高分辨屏幕的话就可能不是一个设备像素对应一个CSS像素了
dpr = 某个方向上 (x或者y) 物理像素 / CSS像素
在js中 通过window.devicePixelRatio 获取到当前设备的dpr
在css中 通过-webkit-device-pixel-ratio,-webkit-min-device-pixel-ratio和 -webkit-max-device-pixel-ratio 进行媒体查询,做一些样式适配。
顺便:关于 em 和 rem
顺便: viewport 深入理解
实现原理
根节点 fontSize 根据宽度决定,viewport动态设置为1/dpr
如iphone6 plus的dpr为3, 则页面整体放大3倍, 1px(css单位)在plus下默认为3px(物理像素)
我们将viewport设置为1/3, 这样页面整体缩回原始大小。
!function(win, lib) {
var timer,
doc = win.document,
docElem = doc.documentElement,
vpMeta = doc.querySelector('meta[name="viewport"]'),
flexMeta = doc.querySelector('meta[name="flexible"]'),
dpr = 0,
scale = 0,
flexible = lib.flexible || (lib.flexible = {});
// 设置了 viewport meta
if (vpMeta) {
console.warn("将根据已有的meta标签来设置缩放比例");
var initial = vpMeta.getAttribute("content").match(/initial\-scale=([\d\.]+)/);
if (initial) {
scale = parseFloat(initial[1]); // 已设置的 initialScale
dpr = parseInt(1 / scale); // 设备像素比 devicePixelRatio
}
}
// 设置了 flexible Meta
else if (flexMeta) {
var flexMetaContent = flexMeta.getAttribute("content");
if (flexMetaContent) {
var initial = flexMetaContent.match(/initial\-dpr=([\d\.]+)/),
maximum = flexMetaContent.match(/maximum\-dpr=([\d\.]+)/);
if (initial) {
dpr = parseFloat(initial[1]);
scale = parseFloat((1 / dpr).toFixed(2));
}
if (maximum) {
dpr = parseFloat(maximum[1]);
scale = parseFloat((1 / dpr).toFixed(2));
}
}
}
// viewport 或 flexible
// meta 均未设置
if (!dpr && !scale) {
// QST
// 这里的 第一句有什么用 ?
// 和 Android 有毛关系 ?
var u = (win.navigator.appVersion.match(/android/gi), win.navigator.appVersion.match(/iphone/gi)),
_dpr = win.devicePixelRatio;
// 所以这里似乎是将所有 Android 设备都设置为 1 了
dpr = u ? ( (_dpr >= 3 && (!dpr || dpr >= 3))
? 3
: (_dpr >= 2 && (!dpr || dpr >= 2))
? 2
: 1
)
: 1;
scale = 1 / dpr;
}
docElem.setAttribute("data-dpr", dpr);
// 插入 viewport meta
if (!vpMeta) {
vpMeta = doc.createElement("meta");
vpMeta.setAttribute("name", "viewport");
vpMeta.setAttribute("content",
"initial-scale=" + scale + ", maximum-scale=" + scale + ", minimum-scale=" + scale + ", user-scalable=no");
if (docElem.firstElementChild) {
docElem.firstElementChild.appendChild(vpMeta)
} else {
var div = doc.createElement("div");
div.appendChild(vpMeta);
doc.write(div.innerHTML);
}
}
function setFontSize() {
var winWidth = docElem.getBoundingClientRect().width;
if (winWidth / dpr > 540) {
(winWidth = 540 * dpr);
}
// 根节点 fontSize 根据宽度决定
var baseSize = winWidth / 10;
docElem.style.fontSize = baseSize + "px";
flexible.rem = win.rem = baseSize;
}
// 调整窗口时重置
win.addEventListener("resize", function() {
clearTimeout(timer);
timer = setTimeout(setFontSize, 300);
}, false);
// 这一段是我自己加的
// orientationchange 时也需要重算下吧
win.addEventListener("orientationchange", function() {
clearTimeout(timer);
timer = setTimeout(setFontSize, 300);
}, false);
// pageshow
// keyword: 倒退 缓存相关
win.addEventListener("pageshow", function(e) {
if (e.persisted) {
clearTimeout(timer);
timer = setTimeout(setFontSize, 300);
}
}, false);
// 设置基准字体
if ("complete" === doc.readyState) {
doc.body.style.fontSize = 12 * dpr + "px";
} else {
doc.addEventListener("DOMContentLoaded", function() {
doc.body.style.fontSize = 12 * dpr + "px";
}, false);
}
setFontSize();
flexible.dpr = win.dpr = dpr;
flexible.refreshRem = setFontSize;
flexible.rem2px = function(d) {
var c = parseFloat(d) * this.rem;
if ("string" == typeof d && d.match(/rem$/)) {
c += "px";
}
return c;
};
flexible.px2rem = function(d) {
var c = parseFloat(d) / this.rem;
if ("string" == typeof d && d.match(/px$/)) {
c += "rem";
}
return c;
}
}(window, window.lib || (window.lib = {}));
5. rem实现 魅族
viewport也是固定的:
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no">
宽度是设备宽度,缩放比例默认是1,不允许用户缩放
实现原理
通过以下代码来控制rem基准值(设计稿以720px宽度量取实际尺寸)
!function (d) {
var c = d.document;
var a = c.documentElement;
var b = d.devicePixelRatio;
var f;
function e() {
var h = a.getBoundingClientRect().width, g;
if (b === 1) {
h = 720
}
if(h>720) h = 720;//设置基准值的极限值
g = h / 7.2;
a.style.fontSize = g + "px"
}
if (b > 2) {
b = 3
} else {
if (b > 1) {
b = 2
} else {
b = 1
}
}
a.setAttribute("data-dpr", b);
d.addEventListener("resize", function () {
clearTimeout(f);
f = setTimeout(e, 200)
}, false);
e()
}(window)
参考:
移动端的几种适配方案及rem dpr px font-size之间转换理解
移动端字体大小单位rem
移动端前端适配方案(总结) – 面试重点