viewpor网页自适应

viewport 手机端浏览器可视窗口大小

实际浏览器可视域的大小比默认viewport 宽度小 ,就会出现滚动条

1px 在不同设备上有不同定义

计算器显示器是通过显示像素点,显示图片的,像素越多看的细,像素越少越模糊,这个就是物理像素.
在css 中也有px属性,叫做设备独立像素
devicePixelRatio = 物理像素 / 独立像素。

viewport三大理论 ppk

  1. layout viewport layout viewport的宽度可以通过document.documentElement.clientWidth 来获取
  2. visual viewport visual viewport的宽度可以通过window.innerWidth 来获取
  3. ideal viewport 没有一个固定的尺寸,不同的设备拥有有不同的ideal viewport。

利用meta标签使用viewport

移动设备默认的viewport是layout viewport,也就是那个比屏幕要宽的viewport,但在进行移动设备网站的开发时,我们需要的是ideal viewport。
那么怎么才能得到ideal viewport呢?这就该轮到meta标签出场了。

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

width 设置layout viewport 的宽度,为一个正整数,或字符串”device-width”
initial-scale 设置页面的初始缩放值,为一个数字,可以带小数
minimum-scale 允许用户的最小缩放值,为一个数字,可以带小数
maximum-scale 允许用户的最大缩放值,为一个数字,可以带小数
height 设置layout viewport 的高度,这个属性对我们并不重要,很少使用
user-scalable 是否允许用户进行缩放,值为”no”或”yes”, no 代表不允许,yes代表允许

把当前的viewport宽度设置为 ideal viewport 的宽度

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

淘宝的布局方案解析

(1)动态设置viewport的scale

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

淘宝布局的第二个要点,就是html元素的font-size的计算公式,font-size = deviceWidth / 10:
(2)动态设置html的font-size

1
2
3
4
5
6
7
//flexible中定义<html>font-size
var width = docEl.getBoundingClientRect().width;
if (width / dpr > 540) {
width = 540 * dpr;
}
var rem = width / 10;
docEl.style.fontSize = rem + 'px';

(3)布局的时候,各元素的css尺寸=设计稿标注尺寸/设计稿横向分辨率/10
(4)font-size可能需要额外的媒介查询,并且font-size不使用rem

淘宝移动端自适应方案—lib.flexible库解析

它的主要js文件有三个,包括flexiblecss.js、flexible.js、makegrid.js
flexible.js—布局的核心js
flexiblecss.js—注入统一的css样式,比如去掉所有元素的内外边距,去掉默认边框等等
makegrid.js—栅格系统

阿里CDN:

其中initial-dpr会把dpr强制设置为给定的值。如果手动设置了dpr之后,不管设备是多少的dpr,都会强制认为其dpr是你设置的值。在此不建议手动强制设置dpr,因为在Flexible中,只对iOS设备进行dpr的判断,对于Android系列,始终认为其dpr为1。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
if (!dpr && !scale) {  
var isAndroid = win.navigator.appVersion.match(/android/gi);
var isIPhone = win.navigator.appVersion.match(/iphone/gi);
var devicePixelRatio = win.devicePixelRatio;
if (isIPhone) {
// iOS下,对于2和3的屏,用2倍的方案,其余的用1倍方案
if (devicePixelRatio >= 3 && (!dpr || dpr >= 3)) {
dpr = 3;
} else if (devicePixelRatio >= 2 && (!dpr || dpr >= 2)){
dpr = 2;
} else {
dpr = 1;
}
} else {
// 其他设备下,仍旧使用1倍的方案
dpr = 1;
}
scale = 1 / dpr;
}

flexible的实质 能通过js动态更改meta标签

1
2
3
4
5
6
7
8
9
10
11
var metaEl = doc.createElement('meta');  
var scale = isRetina ? 0.5:1;
metaEl.setAttribute('name', 'viewport');
metaEl.setAttribute('content', 'initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
if (docEl.firstElementChild) {
document.documentElement.firstElementChild.appendChild(metaEl);
} else {
var wrap = doc.createElement('div');
wrap.appendChild(metaEl);
documen.write(wrap.innerHTML);
}
  • 动态改写标签
  • 给元素添加data-dpr属性,并且动态改写data-dpr的值
  • 给元素添加font-size属性,并且动态改写font-size的值

把视觉稿中的px转换成rem

目前Flexible会将视觉稿分成100份,而每一份被称为一个单位a。同时1rem单位被认定为10a。
1a = 7.5px
1rem = 75px
那么我们的视觉稿就分成了10a,也就是整个宽度为10rem,对应的font-size为75px;

推荐CSSREM——px转化rem小工具

使用[data-dpr]属性来区分不同dpr下的文本字号大小。

1
2
3
4
5
6
7
8
9
10
11
div {  
width: 1rem;
height: 0.4rem;
font-size: 12px; // 默认写上dpr为1的fontSize
}
[data-dpr="2"] div {
font-size: 24px;
}
[data-dpr="3"] div {
font-size: 36px;
}

字体慎用rem,误差太大了,因为不能满足任何屏幕下字体大小相同,所以建议标题类用rem,要求字体大小相同的部分还是用px

栅格系统—makegrid.js

js 代码

1
2
3
4
5
6
7
8
9
10
var gridMode = {  
'750-12':{ designWidth:750,designUnit:6,columnCount:12,columnXUnit:7,gutterXUnit:3,edgeXUnit:4
},
'750-6': { designWidth:750,designUnit:6,columnCount:6,columnXUnit:17,gutterXUnit:3,edgeXUnit:4
},
'640-12': { designWidth:640,designUnit:4,columnCount:12,columnXUnit:11,gutterXUnit:2,edgeXUnit:3
},
'640-6': { designWidth:640,designUnit:4,columnCount:6,columnXUnit:24,gutterXUnit:2,edgeXUnit:3
}
}

参数说明
lib.flexible.makeGrid(params)
• [Object params]

  • designWidth - 设计稿宽度

  • designUnit - 设计稿最小单位a(以px为单位)

  • columnCount - 栅格列数
  • columnXUnit - 栅格列宽(以a为单位)
  • gutterXUnit - 栅格间距(以a为单位)

  • edgeXUnit - 页面左右边距(以a为单位)

  • className - 栅格样式的名称(可省略,默认为grid)

利用meta输出栅格样式


栅格化代码举例

1
2
3
4
5
6
<meta content="modeName=640-12" name="grid" />  
<div class="grid">
<div class="col-4"></div>
<div class="col-4"></div>
<div class="col-4"></div>
</div>

瞅瞅flexible.js 源码

flexible.js—布局的核心js,里边主要包含下边几点来实现适配布局

1)设置像素比dpr和scale
有两种模式:一种是自适应,一种是手动配置dpr
适应模式:根据已有的meta标签来设置dpr和scale
手动配置:
2)如果没有设置缩放比,根据苹果用户或者安卓用户设置缩放比。iOS下,对于2和3的屏,用2倍的方案,其余的用1倍方案,安卓的均采用1。
源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var isAndroid = win.navigator.appVersion.match(/android/gi);  
var isIPhone = win.navigator.appVersion.match(/iphone/gi);
var devicePixelRatio = win.devicePixelRatio;
if (isIPhone) {
// iOS下,对于2和3的屏,用2倍的方案,其余的用1倍方案
if (devicePixelRatio >= 3 && (!dpr || dpr >= 3)) {
dpr = 3;
} else if (devicePixelRatio >= 2 && (!dpr || dpr >= 2)){
dpr = 2;
} else {
dpr = 1;
}
} else {
// 其他设备下,仍旧使用1倍的方案
dpr = 1;
}

3)前边谈到淘宝布局方案的时候,说到淘宝触屏版布局的前提就是viewport的scale根据devicePixelRatio动态设置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//为html标签添加data-dpr属性
docEl.setAttribute('data-dpr', dpr);
//如果没有meta[name="viewport",添加meta标签
if (!metaEl) {
metaEl = doc.createElement('meta');
metaEl.setAttribute('name', 'viewport');
metaEl.setAttribute('content', 'initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
if (docEl.firstElementChild) {
docEl.firstElementChild.appendChild(metaEl);
} else {
var wrap = doc.createElement('div');
wrap.appendChild(metaEl);
doc.write(wrap.innerHTML);
}
}

4)为html标签添加data-dpr属性
document.documentElement.setAttribute(‘data-dpr’, dpr);
5)刷新页面的rem基准值 (API—lib.flexible.refreshRem())

1
2
3
4
5
6
7
8
9
10
11
12
//根据dpr和物理像素设置rem
function refreshRem(){
//getBoundingClientRect().width相当于物理像素
var width = docEl.getBoundingClientRect().width;
// width / dpr > 540等于独立像素
if (width / dpr > 540) {
width = 540 * dpr;
}
var rem = width / 10;
docEl.style.fontSize = rem + 'px';
flexible.rem = win.rem = rem;
}

6)什么时候执行refreshRem()呢?
第一种当窗口大小发生变化,也就是触发resize事件的时候;

1
2
3
4
win.addEventListener('resize', function() {  
clearTimeout(tid);
tid = setTimeout(refreshRem, 300);
}, false);

第二种是当重新载入页面时,判断是否是缓存,如果是缓存,执行refreshRem()

1
2
3
4
5
6
win.addEventListener('pageshow', function(e) {  
if (e.persisted) {
clearTimeout(tid);
tid = setTimeout(refreshRem, 300);
}
}, false);

7)rem转化px (API—lib.flexible.rem2px([Number|String digital]))

1
2
3
4
5
6
7
8
flexible.rem2px = function(d) {  
//已定义var rem = width / 10;
var val = parseFloat(d) * this.rem;
if (typeof d === 'string' && d.match(/rem$/)) {
val += 'px';
}
return val;
}

rem转化px计算公式=d*(width/10)
8)px转化rem (API—lib.flexible.px2rem([Number|String digital]))

1
2
3
4
5
6
7
flexible.px2rem = function(d) {  
var val = parseFloat(d) / this.rem;
if (typeof d === 'string' && d.match(/px$/)) {
val += 'rem';
}
return val;
}

px转化rem计算公式=d/(width/10)

css布局适配问题避免:

  1. 盒子,图片等宽度设置首选百分比,次而选择rem,高度可以是固定值
  2. 字体可以不用rem,误差太大了,且不能满足任何屏幕下字体大小相同,所以建议标题类用rem,要求字体大小相同的部分还是用px;
  3. 遇到内容排列显示的布局,建议放弃float,可以直接使用display:inline-block。
  4. 慎用position属性;absolute会脱离文档流,relative则不会
  5. 如何解决盒子边框溢出?当你把元素宽度设为 width:100%时,有时可能会遇到元素的宽度超出了屏幕,
    这时可对元素加box-sizing:border-box属性,用来指定盒子大小包含边框和内边距
  6. 去除button在ios上的默认样式
    -webkit-appearance: none; border-radius: 0;
  7. 不想让按钮touch时有蓝色的边框
    outline:none;
  8. 去除webkit的滚动条
    element::-webkit-scrollbar{

    display: none;
    }

  9. 遇到过一个问题就是,当手机端点击input弹出键盘,整个视窗的高度就会变为减去键盘的高度,
    页面底部样式会乱,当时解决方法是用js获取整个页面高度赋值给body,等于说在不同的设备下写死不同的body高度值,底部就不会乱了
    $(“body”).css(“height”,parseInt($(“.wrap”).height())+parseInt($(“.icon-main”).height()));
  10. 如果想改变 placeholder里的文字,需要用c伪类
    ::-webkit-input-placeholder{

    color:#ccc
    }

附上简单淘宝适配源码

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
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta content="modeName=640-12" name="grid" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="description" content="">
<meta name="keywords" content="">
<title></title>
<style>
</style>
</head>
<body>
<style>
.grid{
margin-top: 1rem ;
background-color: #ccc;
padding-top: 0.6rem;
}
.grid > div {
text-align: center;
font-size:0.4rem;
}
.pic{
width:2rem;
height:2rem;
margin-bottom: 0.4rem;
}
.pic img{
width:1.4rem;
height:1.4rem;
}
.pic p{
width:2rem;
height:0.6rem;
position:relative;
top:2px;
color:#666;
}
</style>
<div class="grid">
<div class="col-3">
<div class="pic">
<img src="./../../img_lib/persion.png" alt="">
<p>天猫</p>
</div>
<div class="pic">
<img src="./../../img_lib/persion.png" alt="">
<p>聚划算</p>
</div>
</div>
<div class="col-3">
<div class="pic">
<img src="./../../img_lib/persion.png" alt="">
<p>天猫</p>
</div>
<div class="pic">
<img src="./../../img_lib/persion.png" alt="">
<p>聚划算</p>
</div>
</div>
<div class="col-3">
<div class="pic">
<img src="./../../img_lib/persion.png" alt="">
<p>天猫</p>
</div>
<div class="pic">
<img src="./../../img_lib/persion.png" alt="">
<p>聚划算</p>
</div>
</div>
<div class="col-3">
<div class="pic">
<img src="./../../img_lib/persion.png" alt="">
<p>天猫</p>
</div>
<div class="pic">
<img src="./../../img_lib/persion.png" alt="">
<p>聚划算</p>
</div>
</div>
</div>
</body>
<script src="http://g.tbcdn.cn/mtb/lib-flexible/0.3.2/flexible_css.js"></script>
<script src="http://g.tbcdn.cn/mtb/lib-flexible/0.3.2/flexible.js"></script>
<script src="http://g.tbcdn.cn/mtb/lib-flexible/0.3.2/makegrid.js"></script>
</html>
分享到