使用rem和viewport的注意事项
2020/7/20 viewportrem
# dpr
- dpr: 物理像素数和 css 像素被称为设备像素比
- dpr 的值,js 通过
window.devicePixelRadio
获取 - dpr 的值,css 通过
-webkit-device-pixel-ratio | -webkit-min-device-pixel-ratio | -webkit-max-device-pixel-ratio
获取 - 物理像素(physical pixel): 手机屏幕上显示的最小单元
- 设备独立像素(density-indenpendent pixel): 逻辑像素(css 像素数)
- 设备像素比(device pixel ratio):
设备像素比(dpr) = 物理像素/设备独立像素
# viewport
- 移动端适配总结@juejin (opens new window)
- layoutviewport: 大于实际屏幕,通过 document.documentElement.clientWidth 获取
- visualviewport: 当前显示在屏幕上的页面,即浏览器可视区域的宽度
- idealviewport: 为浏览器定义的可完美适配移动端的理想 viewport,固定不变,可以认为是设备视口宽度。比如 iphone 7 为 375px, iphone 7p 为 414px
# viewport 设置
head meta
<meta
name="viewport"
content="width=device-width,initial-scale=1,user-scale=no"
/>
1
2
3
4
2
3
4
width
设置的是layoutviewport
的宽度initial-scale
设置页面的初始缩放值,并且这个初始缩放值是相对于 idealviewport 缩放的,最终得到的结果不仅会决定 visualviewport,还会影响到 layoutviewportuser-scalable
是否允许用户进行缩放的设置
# rem 示例 1
- js
// 动态计算 页面宽度/一个比例值(比如 10 或者 15)= 1rem
// iphone6: 750; 分成100份 1rem = 750/100 即 html 的宽度为 7.5rem(750 / 100)
// html(font-size) = deviceWidth / 7.5
(function() {
document.addEventListener(
"DOMContentLoaded",
function(e) {
document.getElementsByTagName("html")[0].style.fontSize =
window.innerWidth / 7.5 + "px";
},
false
);
})();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- scss
/* 单位px转化为rem */
@function px2rem($px) {
@return #{$px / 100}rem;
}
/* 设置字体大小,不使用rem单位, 根据dpr值分段调整 */
@mixin font-size($fontSize) {
font-size: $fontSize / $design-dpr;
[data-dpr="2"] & {
font-size: $fontSize / $design-dpr * 2;
}
[data-dpr="3"] & {
font-size: $fontSize / $design-dpr * 3;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# rem 示例 2
- js
!(function() {
var docElem = document.documentElement,
metaElem = document.querySelector('meta[name="viewport"]'),
dpr = window.devicePixelRatio || 1,
// 将页面分为10块
blocks = 10,
// 需要限制的最小宽度
defaultMinWidth = 320,
// 需要限制的最大宽度
defaultMaxWidth = 540,
// 计算的基准值
calcMaxWidth = 9999999;
if (!metaElem) {
metaElem = initMetaViewport();
}
if (metaElem.getAttribute("data-content-max") !== null) {
calcMaxWidth = defaultMaxWidth;
}
// 确保meta[name="viewport"]存在
function initMetaViewport() {
var meta = document.createElement("meta");
meta.setAttribute("name", "viewport");
meta.setAttribute(
"content",
"width=device-width,initial-scale=1,user-scalable=no"
);
document.head.appendChild(meta);
return meta;
}
// 大部分dpr为2以下的安卓机型不识别scale,需设置不缩放
if (navigator.appVersion.match(/android/gi) && dpr <= 2) {
dpr = 1;
}
setScale(dpr);
// 企业QQ设置了scale后,不能完全识别scale(此时clientWidth未收到缩放的影响而翻倍),需设置不缩放
if (navigator.appVersion.match(/qq\//gi) && docElem.clientWidth <= 360) {
dpr = 1;
setScale(dpr);
}
docElem.setAttribute("data-dpr", dpr);
// 设置缩放
function setScale(dpr) {
metaElem.setAttribute(
"content",
"initial-scale=" +
1 / dpr +
",maximum-scale=" +
1 / dpr +
",minimum-scale=" +
1 / dpr +
",user-scalable=no"
);
}
// 设置docElem字体大小
function setFontSize() {
var clientWidth = docElem.clientWidth;
clientWidth = Math.max(clientWidth, defaultMinWidth * dpr);
// 调整计算基准值
if (calcMaxWidth === defaultMaxWidth) {
clientWidth = Math.min(clientWidth, defaultMaxWidth * dpr);
}
docElem.style.fontSize = clientWidth / blocks + "px";
}
setFontSize();
window.addEventListener(
window.orientationchange ? "orientationchange" : "resize",
setFontSize,
false
);
})();
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
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
- px 和 rem
/* 移动端页面设计稿宽度 */
$design-width: 750;
/* 将移动端页面分为10块 */
$blocks: 10;
/* 单位px转化为rem */
@function px2rem($px) {
@return #{$px / $design-width * $blocks}rem;
}
/* 单位rem转化为px,可用于根据rem单位快速计算原px */
@function rem2px($rem) {
@return #{$rem / $blocks * $design-width}px;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
# 根据 dpr 选择图片
@mixin bg(url, type) {
background-image: url(url+"."+type);
@media only screen and (-webkit-min-device-pixel-ratio: 2) {
background-image: url(url+"@2x."+type);
}
@media only screen and (-webkit-min-device-pixel-ratio: 3) {
background-image: url(url+"@3x."+type);
}
}
#test {
width: 100px;
height: 100px;
background-color: yellow;
@include bg("../assets/test", "jpg");
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 根据 dpr 设置 font-size
- 设置字体大小,不使用 rem 单位,根据 dpr 值分段调整
/* 移动端页面设计稿dpr基准值 */
$design-dpr: 2;
@mixin font-size($fontSize) {
font-size: $fontSize / $design-dpr;
[data-dpr="2"] & {
font-size: $fontSize / $design-dpr * 2;
}
[data-dpr="3"] & {
font-size: $fontSize / $design-dpr * 3;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
# dpr 设置容器大小
/* 缩放所支持的设备最小宽度 */
$min-device-width: 320px;
/* 缩放所支持的设备最大宽度 */
$max-device-width: 540px;
/* 设置容器拉伸的最小宽度 */
@mixin container-min-width() {
margin-right: auto;
margin-left: auto;
min-width: $min-device-width;
@media (-webkit-device-pixel-ratio: 2) {
min-width: $min-device-width * 2;
}
@media (-webkit-device-pixel-ratio: 3) {
min-width: $min-device-width * 3;
}
}
/* 设置容器拉伸的最大宽度 */
@mixin container-max-width() {
margin-right: auto;
margin-left: auto;
max-width: $max-device-width;
@media (-webkit-device-pixel-ratio: 2) {
max-width: $max-device-width * 2;
}
@media (-webkit-device-pixel-ratio: 3) {
max-width: $max-device-width * 3;
}
}
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
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
# pxtorem
├── gulpfile.js
├── output
├── package-lock.json
├── package.json
└── src
├── index.js
├── m
│ └── m.css
├── other
│ ├── m
│ │ └── other-m.css
│ └── pc
│ └── other-pc.css
└── pc
└── pc.css
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
gulpfile.js
// 将 `src/xxx/m/xxx.css`转换为rem
const { src, dest } = require('gulp');
var postcss = require('gulp-postcss');
var pxtorem = require('postcss-pxtorem');
exports.default = function() {
// https://github.com/cuth/postcss-pxtorem 配置
var processors = [
pxtorem({
replace: true,
propList: ['*'],
exclude: function(path) {
const matchPath = /src\/(.+\/)*m\//.test(path)
return !matchPath
}
})
];
return src('src/**/*.css')
.pipe(postcss(processors))
.pipe(dest('output/css'));
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# vw 和 vh
# 相关链接
- 移动 web 适配之 rem (opens new window)
- 使用 Flexible 实现手淘 H5 页面的终端适配 (opens new window)
- 响应式布局的常用解决方案对比(媒体查询、百分比、rem 和 vw/vh) #13 (opens new window)
- 物理像素 Device-Viewport-and-Pixel-Introduction (opens new window)
- 如何在 Vue 项目中使用 vw 实现移动端适配 (opens new window)
- 物理像素与 css 像素 (opens new window)
- CSS 像素、物理像素、逻辑像素、设备像素比、PPI、Viewport #21 (opens new window)