html移动端适配

# 响应式布局

# meta 标签

最简单的处理方式是加上一个 meta 标签; 其中,width = device-width 这一句的意思是让页面的宽度等于屏幕的宽度。

<meta name="viewport" content="width=device-width, initial-scale=1">
1

最适合移动设备的viewport,ideal viewport的宽度等于移动设备的屏幕宽度

为了让viewport能够等于ideal viewport,一般我们会添加meta标签。width=device-width,initial-scale=1.0的时候,视觉视口的大小。对于iPhone 6 Plus来说,是固定值414px。所以,理想视口就等于设备宽度。

<meta name="viewport" content="width=device-width, initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
<!-- initial-scale:初始的缩放比例->
<!--  minimum-scale:允许用户缩放到的最小比例->
<!--  maximum-scale:允许用户缩放到的最大比例->
<!--  user-scalable:用户是否可以手动缩放->
1
2
3
4
5

# 使用 rem(css3)

rem 指的是 html 元素的 font-size,html 元素的 font-size 默认是 16px,所以 1 rem = 16px;然后根据 rem 来计算各个元素的宽高,然后配合 media query 就可以实现自适应。

rem官方定义『The font size of the root element』,即根元素的字体大小。rem是一个相对的CSS单位,1rem等于html元素上font-size的大小。所以,只要设置html上font-size的大小,就可以改变1rem所代表的大小

# px 转 rem

css3的rem是基于根元素的字体大小计算的尺寸单位,所以通过改变html的font-size来实现rem的响应式布局,例如使用css媒体查询:

html{font-size:10px}
@media screen and (min-width:415px) and (max-width:639px){html{font-size:15px}}
@media screen and (min-width:640px) and (max-width:719px){html{font-size:20px}}
1
2
3

但不同分辨率的屏幕计算转换太复杂,媒体查询不能兼顾所有的尺寸,所以可以通过JS计算

var deviceWidth=document.documentElement.clientWidth;
document.documentElement.style.fontSize = deviceWidth / bodyRemWidth + 'px';

(function () {
    var html = document.documentElement;
    function onWindowResize() {
        html.style.fontSize = html.getBoundingClientRect().width / 20 + 'px';
    }
    window.addEventListener('resize', onWindowResize);
    onWindowResize();
})();
1
2
3
4
5
6
7
8
9
10
11
<script type="text/javascript">
    ;(function (doc, win) {
        var docEl = doc.documentElement,
            resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize',
            recalc = function () {
                var clientWidth = docEl.clientWidth;
                if(typeof(clientWidth)=='number' && clientWidth <= 750 && clientWidth>319){  //手机端自适应,   基础20
                    docEl.style.fontSize = 20 * (clientWidth / 375) + 'px';
                }else if(typeof(clientWidth)=='number' && clientWidth>750){   //pc端基础为40/2  20 手机端的适配
                    docEl.style.fontSize = '40'+'px';
                }
            };
        if (!doc.addEventListener) return;
        win.addEventListener(resizeEvt, recalc, false);
        doc.addEventListener('DOMContentLoaded', recalc, false);
        recalc();
    })(document, window);
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

例如:

设计稿基于iphone5的320px,deviceWidth为320px,根元素的font-size基于100px,那body的width可以写为3.2em; 当适配640px的屏幕时,deviceWidth为640px,bodyRemWidth为3.2rem,此时根元素的font-size计算为200px; deviceWidth就是viewport设置中的deviceWidth, viewport视图提供布局的窗口,包括移动端的显示/缩放比例等设置; 需要设置mate的viewport使得显示时的页面宽度等于设备逻辑像素大小,移动端常用代码:

<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
1

deviceWidth=设备逻辑像素/(dpr * scale); dpr为固定值,高清屏一般为dpr=2,所以scale=1/dpr,js动态设置scale:

var scale = 1 / devicePixelRatio;
document.querySelector('meta[name="viewport"]')
    .setAttribute('content','initial-scale=' + scale + ', 
    maximum-scale=' + scale + ', 
    minimum-scale=' + scale + ', 
    user-scalable=no');
1
2
3
4
5
6
# 插件

主要的插件都是基于PostCss的

px2rempostcss-px2rem:将css中px编译为rem,配合js根据不同的dpr,修改meta的viewport值和html的font-size

# 媒体查询(media query)

使用@media查询可以针对不同的媒体类型定义不同的样式;

@media 可以针对不同的屏幕尺寸设置不同的样式,特别是如果需要设置设计响应式的页面。

重置浏览器大小的过程中,页面也会根据浏览器的宽度和高度重新渲染页面。

# 属性说明

![](./_image/02.css-use/16-21-14.jpg)

/*大于1024并小于1360屏幕的媒体查询关键CSS代码*/
if ((screen.width == 1360) && (screen.height == 1024)){
    setActiveStyleSheet(CSS1);
}
1
2
3
4

示例:

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<title>不同浏览器窗口大小加载不同CSS样式(Media Queries)</title>
		<style type="text/css">
			/*Media Queries模块 */			
			@media screen and (min-width:200px) {
				.media_query_daynic_adapter {
					background-color: red;
					width: 200px;
					height: 200px;
				}
			}
			
			@media screen and (min-width:400px) {
				.media_query_daynic_adapter {
					background-color: green;
					width: 400px;
					height: 400px;
				}
			}
			
			@media screen and (min-width:800px) {
				.media_query_daynic_adapter {
					background-color: blue;
					width: 800px;
					height: 800px;
				}
			}
			/*自适应布局*/			
			div#auto_adapter_layout {
				width: 960px;
				margin: auto;
			}			
			div#left {
				width: 740px;
				float: left;
			}			
			p {
				line-height: 600px;
				text-align: center;
				font-weight: bold;
				font-size: 2em;
				margin: 0 0 20px 0;
				color: #ffffff;
			}			
			p#left-z {
				width: 200px;
				float: left;
				background-color: #0752ff;
			}			
			p#left-y {
				width: 520px;
				float: right;
				background-color: #41db50;
			}			
			p#right {
				width: 200px;
				float: right;
				background-color: #ff6600;
			}
			
			/*大于1000px的时候*/			
			@media screen and (min-width:1000px) {
				div#auto_adapter_layout {
					width: 1000px;
				}
				div#left {
					width: 780px;
					float: left;
				}
				p#left-z {
					width: 200px;
					float: left;
					background-color: #0752ff;
				}
				p#left-y {
					width: 560px;
					float: right;
					background-color: #41db50;
				}
				p#right {
					width: 200px;
					float: right;
					background-color: #ff6600;
				}
			}
			
			/*最大不超过999像素  最小不小于640px*/			
			@media screen and (min-width:640px) and (max-width: 999px) {
				div#auto_adapter_layout {
					width: 640px;
				}
				div#left {
					width: 640px;
					float: left;
				}
				p {
					line-height: 430px;
				}
				p#left-z {
					width: 200px;
					float: left;
				}
				p#left-y {
					width: 420px;
					float: right;
				}
				p#right {
					width: 100%;
					float: none;
					clear: both;
					line-height: 150px;
				}
			}
			
			/*最大不超过639px*/			
			@media screen and (max-width:639px) {
				div#auto_adapter_layout {
					width: 100%;
				}
				div#left {
					width: 100%;
					float: none;
				}
				p {
					line-height: 200px;
				}
				p#left-z {
					width: 100%;
					float: none;
				}
				p#left-y {
					width: 100%;
					float: none;
				}
				p#right {
					width: 100%;
					float: none;
					clear: both;
					line-height: 200px;
				}
			}
		</style>
	</head>
	<body>
		<div class="media_query_daynic_adapter">
		</div>
		<div id="auto_adapter_layout">
			<div id="left">
				<p id="left-y">Conter</p>
				<p id="left-z">Left</p>
			</div>
			<p id="right">Right</p>
		</div>
	</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158

# 移动端1像素边框问题

一般来说,在PC端浏览器中,设备像素比(dpr)等于1,1个css像素就代表1个物理像素;但是在retina屏幕中,dpr普遍是2或3,1个css像素不再等于1个物理像素,因此比实际设计稿看起来粗不少

实现1PX边框的方法有很多,各有优缺点,比如通过背景图片实现、通过transform: scale(0.5)实现。

# 解决方案

1、小数值px

<body>
    <div id="main" style="border: 1px solid #000000;"></div>
</body>
<script type="text/javascript">
if (window.devicePixelRatio && devicePixelRatio >= 2) {
    var main = document.getElementById('main');
    main.style.border = '.5px solid #000000';
}
</script>
1
2
3
4
5
6
7
8
9

或者用媒体查询实现:媒体查询利用设备像素比缩放,设置小数像素

IOS8下已经支持带小数的px值, media query对应devicePixelRatio有个查询值-webkit-min-device-pixel-ratio, css可以写成这样

.border { border: 1px solid #999 }
@media screen and (-webkit-min-device-pixel-ratio: 2) {
    .border { border: 0.5px solid #999 }
}
@media screen and (-webkit-min-device-pixel-ratio: 3) {
    .border { border: 0.333333px solid #999 }
}
1
2
3
4
5
6
7

缺点】对设备有要求,小数像素目前兼容性较差。

优点:简单,好理解

缺点:兼容性差,目前之余IOS8+才支持,在IOS7及其以下、安卓系统都是显示0px。

2、border-image

使用的背景图片

.border-image-1px {
    border-width: 1px 0px;
    -webkit-border-image: url(border.png) 2 0 stretch;
    border-image: url(border.png) 2 0 stretch;
    /*或者base64方式引入*/
    -webkit-border-image: url("data:image/png;base64,xxx") 2 0 stretch;
    border-image: url('如上');
}
1
2
3
4
5
6
7
8

优点: 图片可以用gif, png, base64多种格式, 以上是上下左右四条边框的写法, 需要单一边框只要定义单一边框的border, 代码比较直观.

缺点:大小、颜色更改不灵活; 放到PS里面看边框,是有点模糊的(因为带有颜色部分是1px,在retina屏幕上拉伸到2px肯定会有点模糊) 【缺点】需要制作图片,圆角可能出现模糊

3、background-img渐变

设置1px的渐变背景,50%有颜色,50%透明;

缺点】因为每个边框都是线性渐变颜色实现,因此无法实现圆角

.border {
    background:
    linear-gradient(180deg, black, black 50%, transparent 50%) top    left  / 100% 1px no-repeat,
    linear-gradient(90deg,  black, black 50%, transparent 50%) top    right / 1px 100% no-repeat,
    linear-gradient(0,      black, black 50%, transparent 50%) bottom right / 100% 1px no-repeat,
    linear-gradient(-90deg, black, black 50%, transparent 50%) bottom left  / 1px 100% no-repeat;
}
1
2
3
4
5
6
7

4、CSS3 box-shadow

box-shadow: h-shadow v-shadow [blur] [spread] [color] [inset];

参数分别表示: 水平阴影位置,垂直阴影位置,模糊距离, 阴影尺寸,阴影颜色,将外部阴影改为内部阴影,后四个可选。该例中为何将阴影尺寸设置为负数?设置成-1px 是为了让阴影尺寸稍小于div元素尺寸,这样左右两边的阴影就不会暴露出来,实现只有底部一边有阴影的效果。从而实现分割线效果(单边边框)。

.shadow {
    -webkit-box-shadow:0 1px 1px -1px rgba(255, 0, 0, 0.5);
    box-shadow:0 1px 1px -1px rgba(255, 0, 0, 0.5);
}
1
2
3
4

模拟效果:没觉得这个方法好用,模拟的效果差强人意,颜色也不好配置,不推荐

5、viewport和rem结合 【推荐】

viewport结合rem解决像素比问题; 示例详见后面;

手淘采用这这种方案使用Flexible实现手淘H5页面的终端适配 (opens new window) ,推荐;

缺点】以为缩放涉及全局的rem单位,比较适合新项目,对于老项目可能要涉及到比较多的改动。

**6、:before:after和transform ** 推荐 transform: scale(0.5) 方案 - 推荐: 很灵活

在以上的用法种,无非逃不开一种思想,就是将1px缩小为0.5px来展示,然而。0.5px并不是所有的设备或浏览器都支持,就考虑用媒体查询或viewport将其缩放比例。其实1像素问题的产生基本发生在设置边框或分割线的时候,场景并不覆盖全局样式,因此,直接缩放需要设置的元素,才是我们真正需要的。tranform就能实现这个需求。

原理是***把原先元素的 border 去掉,然后利用 :before 或者 :after 重做 border ,并 transform 的 scale 缩小一半,原先的元素相对定位,新做的 border 绝对定位***。

单条border样式设置:

.scale-1px{
     position: relative;
     border:none;
 } 
.scale-1px:after{
     content: '';
     position: absolute; 
     bottom: 0; 
     background: #000; 
     width: 100%; 
     height: 1px;
     -webkit-transform: scaleY(0.5); 
     transform: scaleY(0.5); 
     -webkit-transform-origin: 0 0; 
      transform-origin: 0 0; 
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

四条border样式设置:

.scale-1px{ 
    position: relative; 
    margin-bottom: 20px; border:none;
} 
.scale-1px:after{ 
    content: ''; 
    position: absolute;
    top: 0; 
    left: 0;
    border: 1px solid #000; 
    -webkit-box-sizing: border-box; 
    box-sizing: border-box; 
    width: 200%; 
    height: 200%; 
    -webkit-transform: scale(0.5); 
    transform: scale(0.5); 
    -webkit-transform-origin: left top; 
    transform-origin: left top; 
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

完整步骤示例

  1. 设置height: 1px,根据媒体查询结合transform缩放为相应尺寸。
div {
    height:1px;
    background:#000;
    -webkit-transform: scaleY(0.5);
    -webkit-transform-origin:0 0;
    overflow: hidden;
}
1
2
3
4
5
6
7

2.用::after::before,设置border-bottom:1px solid #000,然后在缩放-webkit-transform: scaleY(0.5);可以实现两根边线的需求

div::after{
    content:'';width:100%;
    border-bottom:1px solid #000;
    transform: scaleY(0.5);
}
1
2
3
4
5

3.用::after设置border:1px solid #000; width:200%; height:200%,然后再缩放scaleY(0.5); 优点可以实现圆角,京东就是这么实现的,缺点是按钮添加active比较麻烦。

.div::after {
    content: '';
    width: 200%;
    height: 200%;
    position: absolute;
    top: 0;
    left: 0;
    border: 1px solid #bfbfbf;
    border-radius: 4px;
    -webkit-transform: scale(0.5,0.5);
    transform: scale(0.5,0.5);
    -webkit-transform-origin: top left;
}
1
2
3
4
5
6
7
8
9
10
11
12
13

结合js来代码来判断是否是Retina屏

if(window.devicePixelRatio && devicePixelRatio >= 2){
  document.querySelector('div').className = 'scale-1px';
}
1
2
3

优点:所有场景都能满足;支持圆角

缺点:对于已经使用伪类的元素,可能需要多层嵌套

# 媒体查询 + transfrom 对方案1的优化
/* 2倍屏 */
@media only screen and (-webkit-min-device-pixel-ratio: 2.0) {
    .border-bottom::after {
        -webkit-transform: scaleY(0.5);
        transform: scaleY(0.5);
    }
}
/* 3倍屏 */
@media only screen and (-webkit-min-device-pixel-ratio: 3.0) {
    .border-bottom::after {
        -webkit-transform: scaleY(0.33);
        transform: scaleY(0.33);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

总结

1、0.5px,相信浏览器肯定是会慢慢支持的;目前而言,如果能用的话,可以hack一下;

2、阴影,border-img的方案不建议使用

3、背景图片和缩放可以在项目中配合使用,如单个线条使用缩放,四条框用背景图片模拟,额,如果要圆角的话,无能无力了

4、建议采用Viewport+rem组合或者transform和伪类

通过VIEWPORT+rem实现的,优点是可以自适应已知的各类手机屏幕,且不存在其它方法存在的变颜色困难、圆角阴影失效问题。缺点这方法适全新项目,如果老项目想用这种方法,改动量估计会比较大。

<html>
    <head>
        <title>1px question</title>
        <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
        <meta name="viewport" id="WebViewport" content="initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">		
        <style>
            html {
                font-size: 1px;
            }			
            * {
                padding: 0;
                margin: 0;
            }
            .bds_b {
                border-bottom: 1px solid #ccc;
            }
            .a,
            .b {
                margin-top: 1rem;
                padding: 1rem;				
                font-size: 1.4rem;
            }
            .a {
                width: 30rem;
            }
            .b {
                background: #f5f5f5;
                width: 20rem;
            }
        </style>
        <script>
            var viewport = document.querySelector("meta[name=viewport]");
            if (window.devicePixelRatio == 1) {
                viewport.setAttribute('content', 'width=device-width,initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no');
            }
            if (window.devicePixelRatio == 2) {
                viewport.setAttribute('content', 'width=device-width,initial-scale=0.5, maximum-scale=0.5, minimum-scale=0.5, user-scalable=no');
            }
            if (window.devicePixelRatio == 3) {
                viewport.setAttribute('content', 'width=device-width,initial-scale=0.3333333333333333, maximum-scale=0.3333333333333333, minimum-scale=0.3333333333333333, user-scalable=no');
            }
            var docEl = document.documentElement;
            var fontsize = 10 * (docEl.clientWidth / 320) + 'px';
            docEl.style.fontSize = fontsize;
        </script>
    </head>
    <body>
        <div class="bds_b a">下面的底边宽度是虚拟1像素的</div>
        <div class="b">上面的边框宽度是虚拟1像素的</div>
    </body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

#

上次更新: 2022/04/15, 05:41:26
×