rem移动端适配

先了解一下基础概念:

像素: 一小块,具有特定的位置和颜色

屏幕分辨率: 一个屏幕具体由多少像素组成

图片分辨率:图片含有的像素数

PPI:每英寸表示的像素数

css像素:一个CSS像素就是一个设备独立像素

视网膜屏幕: 不管分辨率多高,展示的界面的比例是类似的

如一个宽度为320个像素的水平线,屏幕分辨率为320*480的屏幕会使用320个像素渲染,屏幕分辨率为640*960会使用640个像素渲染。这里水平线的320个像素,这个像素是设备独立像素, 这个就是我们使用的css像素。

屏幕的物理大小可以通过 screen.width screen.height 获得,那么屏幕的物理像素大小呢?我们就可以通过screen.width * window.devicePixelRatio来获得。window.devicePixelRatio就是设备像素比,设备像素比就是物理像素和设备独立像素的比值,当比值为1时,说明1个设备独立像素等于1个物理像素,当比值为2时,一个设备独立像素等于4个物理像素

设备像素比 = 物理像素 / 设备独立像素

下面再了解视口的概念:

理想视口:网页在移动端展示的理想大小,这个也是视觉稿的大小,一般为750px

布局视口:PC就是浏览器的窗口大小,移动端的布局视口一般都比较小,没有办法完全展示传统的桌面web上的网页,因此移动端上默认为960px,但是用户可以放大

以上是一些概念性的名词解释,那么对于web移动端的适配应当如何理解呢?

其实我们要明白一件事,适配主要是使得我们的布局尺寸和设备的大小成一定的比例。大屏上,绝对尺寸要大一些,小屏上,布局的绝对尺寸就要小一些。

那最先想到的就是百分比布局,但是它有一些缺点,那就是它需要配合媒体查询,因为如果父元素没有设置宽高的话,单纯的设置百分比是没有用的,因此往往是通过媒体查询,将不同宽度的设备下html元素,body的宽度设定一个值,这样子元素才能使用百分比定位。同时另一个问题就是有些尺寸的百分比不好计算。

第二种那就是使用rem了,rem的定义就是font size of the root element,也就是1rem等于html的字体大小。那为什么rem可以实现适配呢?通过rem的定义就能知道,只需要将html的字体大小和设备的宽度大小建立联系,那么自然而然的就可以自适应屏幕宽度了。因此有两种方式来建立这种联系,一种就是通过css的媒体查询,在不同宽度的设备下,html的字体大小响应的改变。这种方式就是我们需要设置不同的设备媒体查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@media all and (max-width: 375px) {
html {
font-size: 37.5px;
}
}
@media all and (max-width: 414px) {
html {
font-size: 41.4px;
}
}
@media all and (max-width: 750px) {
html {
font-size: 75px;
}
}

这样的话,每增加一种设备宽度,我们就需要增加一条媒体查询。

另一种自然而然就是通过js动态的为html的字体大小赋值。

1
2
3
4
5
6
function setRem() {
var width = document.documentElement.clientWidth;
var rem = width / 3.75;
document.documentElement.style.fontSize = rem + 'px';
}
setRem();

这样子的话,我们就能通过设备的宽度来动态改变html的font-size的值了。但是还差了一点,由于我们上面已经知道,移动端上默认的大小为960px。因此这时候

1
document.documentElement.clientWidth

的宽度和屏幕的大小并没有什么关系。

因此,完整的js代码为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<script>
(function(win, doc, obj){
var rem, tid;
var documentElement = doc.documentElement;
var metaElement = doc.querySelector('meta[name="viewport"]');
metaElement.setAttribute(
'content',
'width=device-width, initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no'
);
function setRemUnit() {
var width = documentElement.clientWidth;
// 375px设计稿 1rem = 100px
rem = width / 3.75;
rem = rem > 50 ? rem : 50;
documentElement.style.fontSize = rem + 'px';
win.rem = obj.rem = rem;
}
tid = setTimeout(setRemUnit, 300);
win.addEventListener('resize', function() {
clearTimeout(tid);
tid = setTimeout(setRemUnit, 300);
}, false);
})(window, document, window['flexible'] || (window['flexible'] = {}));
</script>

这里通过设置viewport的width=device-width,这样我们的布局宽度就和屏幕的宽度对应起来了。之后设置的rem就能根据屏幕宽度适配。

那这个时候和我们遇到的多倍屏又有什么关系呢?其实是由于css像素是一个独立像素,正常情况下1css像素=1物理像素。但是我们知道一个屏幕物理像素越高,其实它展示的内容越逼真,因此就有了多倍屏。其实这个多倍屏和我们的css的布局是没有关系的,因为布局需要的是相对的关系,我只需要能够相对于我的屏幕大小能够自适应,不用在意我的1css像素对应的是1物理像素,还是2物理像素。

但是呢?多倍屏给我们带来了好处,同样还是有一些其他问题的,那就是对于一些位图在多倍屏上展示模糊,1px展示的太粗。

针对位图那就是针对不同倍数的屏幕展示的增加相同倍数的像素。对于1px来说,我们就需要缩放,才能达到我们的效果。

这里主要讨论一下1px的问题,这个问题的解决思路,那就是通过css的scale.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.horizaton-border{
position: relative;
}
@media screen and (-webkit-min-device-pixel-ratio: 2){
.horizaton-border:before{
content: "";
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 1px;
transform(scaleY(0.5));
transform-origin: 0 50%;
}
}

那么我们再往深处想一想,为什么缩放就能达到我们的目的?其实是因为1css像素此时等于2物理像素。因此我们把我们的css尺寸缩放0.5,这样就可以使得1px等于1物理像素。

这是一种方法,这是我们改变特定的元素的尺寸。那么我们能不能统一修改布局视口的尺寸呢?如果我们使得布局视口的尺寸完全等于了设备的物理像素宽度,那么对于1px就等于1物理像素了。因此我们可以修改viewport的内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var documentElement = doc.documentElement;
var metaElement = doc.querySelector('meta[name="viewport"]');

dpr = win.devicePixelRatio || 1;
if (dpr > 1) {
dpr = 2;
scale = 0.5;
}

metaElement.setAttribute(
'content',
'initial-scale=' + scale + ',minimum-scale=' + scale + ',maximum-scale=' + scale + ',user-scalable=no'
);
documentElement.setAttribute('data-dpr', dpr);
function setRemUnit() {
var width = documentElement.clientWidth * scale;
// 375px设计稿 1rem = 100px
rem = width / 3.75;
rem = rem > 50 ? rem : 50;
documentElement.style.fontSize = rem + 'px';
win.rem = obj.rem = rem;
}

这样子的话,布局视口整体缩放,从而使得在整个屏幕中1px就等于了1物理像素了。但是它也会产生一个问题,那就是我们在审查元素的时候可以发现,整个屏幕的像素到放大了相应的倍数。整体的布局没有问题,但是这个时候对于字体来说,就有问题了,因为这个时候相当于把整个字体缩放了。因此对于字体来说需要通过less设置

1
2
3
4
5
6
7
8
9
.font-size(@value) {
font-size: @value * 1PX;
[data-dpr="2"] & {
font-size: @value * 2PX;
}
[data-dpr="3"] & {
font-size: @value * 3PX;
}
}

因此综上所述,针对适配布局,其实只需要根据相对的css像素就能解决问题。而对于多倍屏的问题,和移动端适配布局是没有关系的,它影响的主要是位图和1px的细线问题。这个时候我们就可以根据自己的需要来选择其中的方案。

-->