为理解各种适配方案的实现,首先先要了解一些基础概念
-
物理像素:
屏幕的物理像素又被称为设备像素,它是显示设备中一个最微小的物理部件,设备项目的物理像素出厂时就确定了且固定不变。
-
设备独立像素
设备独立像素也称为密度无关像素,可以认为是计算机坐标系统中的一个点,这个点代表一个可以由程序使用的虚拟像素(比如说CSS像素),然后由相关系统转换为物理像素
-
设备像素比
设备像素比简称为dpr,其定义了物理像素和设备独立像素的对应关系
设备像素比 = 物理像素 / 设备独立像素 以iphone6为例: iphone6的设备宽和高为375pt * 667pt,可以理解为设备的独立像素,而其设备像素比为2.固有设备像素为750pt * 1334pt
通过:window.devicePixelRatio获得。
设备像素比是区别是否是高清屏的标准,dpr大于1时就为高清屏,一般情况下dpr为整数,但是android有些奇葩机型不为整数
-
css像素
在CSS、JS中使用的一个长度单位。单位px
注:在pc端1物理像素等于1px,但是移动端1物理像素不一定等于1px,1物理像素与px的关系与以下因素有关
- 屏幕布局视口大小
- 屏幕的分辨率(物理像素) 对于一块屏幕,其物理像素是确定的。视觉视口尺寸是继承的布局视口的,而视觉视口里宽度即是css的px数。故在一块屏上物理像素与px的关系就是物理像素与布局视口的px数的关系。
比如iphone6,期物理像素为750,如果没有设置布局视口时,viewport为980px 此时:1物理像素长度等于980/750px = 1.3067px的长度 由于像素都是点阵的,故1物理像素相当于1.3067px * 1.3067px方格。 当在meta中设置了如下配置时 <meta name="viewport" content="width=device-width"> 相当于把布局视口设置为设备的宽度(即上面讲到的设备独立像素), 对于iphone6就是375px。 此时1物理像素长度等于375/750px = 0.5px的长度,故1物理像素相当于0.5px * 0.5px的方格
-
布局视口 在html中一般在meta中的name为viewport字段就是控制的布局视口。布局视口一般都是浏览器厂商给的一个值。一般在768px ~ 1024px 之间,最常用的宽度就是 980。布局视口可以通过:
document.documentElement.clientWidth(clientHeight) // 布局视口的尺寸。
-
视觉视口 浏览器可视区域的大小,即用户看到的网页的区域
window.innerWidth(innerHeight) // 视觉视口尺寸
-
理想视口 布局视口虽然解决了移动端查看pc端网页的问题,但是完全忽略了手机本身的尺寸。所以苹果引入了理想视口,它对设备来说是最理想的布局视口,用户不需要对页面进行缩放就能完美的显示整个页面。最简单的做法就是使布局视口宽度改成屏幕的宽度。可以通过window.screen.width获取。
移动端到底怎么适配不同的屏幕呢?最简单的方法是设置如下视口:
<meta name="viewport" content="width=device-width,initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
固定高度,宽度自适应。
这种方案是目前使用较多的方案,也是相对较简单的实现方案:
该方法使用了理想视口:
<meta name="viewport" content="width=device-width,initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
垂直方向使用固定的值,水平方向使用弹性布局,元素采用定值、百分比、flex布局等。这种方案相对简单,还原度也非常低。
固定布局视口宽度,使用viewport进行缩放。固定布局视口,宽度设置固定的值,总宽度为640px,根据屏幕宽度动态生成viewport。(设计稿应该是640px的)。网易:
var win = window,
width = 640,
iw = win.innerWidth || width,
ow = win.outerHeight || iw,
sw = win.screen.width || iw,
saw = win.screen.availWidth || iw,
ih = win.innerHeight || width,
oh = win.outerHeight || ih,
ish = win.screen.height || ih,
sah = win.screen.availHeight || ih,
w = Math.min(iw, ow, sw, saw, ih, oh, ish, sah),
ratio = w / width,
dpr = win.devicePixelRatio;
if (ratio = Math.min(ratio, dpr), 1 > ratio) {
var ctt = ",initial-scale=" + ratio + ",maximum-scale=" + ratio,
metas = document.getElementsByTagName("meta");ctt += "";
for (var i = 0, meta; i < metas.length; i++) meta = metas[i], "viewport" == meta.name && (meta.content += ctt)
}
根据不同屏幕动态写入font-size,以rem作为宽度单位,固定布局视口。
<meta name="viewport" content="width=device-width,initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
75px 对应 0.75rem, 距离占设计稿的10%;
在ipone6上:
width = document.documentElement.clientWidth = 375px;
rem = 375px / 7.5 = 50px;
0.75rem = 37.5px; (37.5/375=10%;占屏幕10%)
在ipone5上:
width = document.documentElement.clientWidth = 320px;
rem = 320px / 7.5 = 42.667px;
0.75rem = 32px; (32/320=10%;占屏幕10%)
以rem作为宽度单位,动态写入viewport和font-size进行缩放。根据设置的dpr设置font-size。如
document.documentElement.style.fontSize = 50 * dpr;
// dpr 为设置的设备像素比。(注意不是设备自身的设备像素比,而是认为设置的dpr)
这种情况下,dpr = 1时,1rem = 50px;
dpr = 2时, 1rem = 100px;
当设计以iphone6为标准,出750px的设计稿时,此时dpr=2,故1rem 等于100px,将图上的尺寸转换为rem非常方便,除以100就行。代码如下:
var scale = 1.0;
var dpr = 1;
var isAndroid = window.navigator.appVersion.match(/android/gi);
var isIPhone = window.navigator.appVersion.match(/iphone/gi);
var devicePixelRatio = window.devicePixelRatio;
// 此处只简单对ios做了伸缩处理,安卓没有做伸缩处理,统一dpr = 1
if ( isIPhone ) {
scale /= devicePixelRatio;
dpr *= devicePixelRatio;
}
var viewport = document.getElementById('viewport');
var content = 'initial-scale=' + scale + ', maximum-scale=' + scale + ',minimum-scale=' + scale + ', width=device-width, user-scalable=no';
viewport.setAttribute( 'content', content );
document.documentElement.style.fontSize = 50 * dpr + 'px';
document.documentElement.setAttribute('data-dpr', dpr);
根据不同屏幕动态写入font-size和viewport,以rem作为宽度单位
将屏幕分为固定的块数10:
var width = document.documentElement.clientWidth; // 屏幕的布局视口宽度
var rem = width / 10; // 将布局视口分为10份
这样在任何屏幕下,总长度都为10rem。1rem对应的值也不固定,与屏幕的布局视口宽度有关。
对于动态生成viewport,他们原理差不多,根据dpr来设置缩放。看看淘宝的:
var devicePixelRatio = window.devicePixelRatio;
var isIPhone = window.navigator.appVersion.match(/iphone/gi);
var dpr,scale;
if (isIPhone) {
if (devicePixelRatio >=3) {
dpr = 3;
} else if (devicePixelRatio >=2) {
dpr = 2;
} else {
dpr = 1;
}
} else {
dpr = 1;
}
scale = 1 / dpr;