.jpg)
# 一些基本概念
- 视窗viewport
简单的理解,viewport是严格等于浏览器的窗口。在桌面浏览器中,viewport就是浏览器窗口的宽度高度。但在移动端设备上就有点复杂。
移动端的viewport太窄,为了能更好为CSS布局服务,所以提供了两个viewport:虚拟的viewportvisualviewport和布局的viewportlayoutviewport。
2. 物理像素(physical pixel)
物理像素又被称为设备像素,他是显示设备中一个最微小的物理部件。每个像素可以根据操作系统设置自己的颜色和亮度。正是这些设备像素的微小距离欺骗了我们肉眼看到的图像效果
3. 设备独立像素(density-independent pixel)
设备独立像素也称为密度无关像素,可以认为是计算机坐标系统中的一个点,这个点代表一个可以由程序使用的虚拟像素(比如说CSS像素),然后由相关系统转换为物理像素。
4. CSS像素
CSS像素是一个抽像的单位,主要使用在浏览器上,用来精确度量Web页面上的内容。一般情况之下,CSS像素称为与设备无关的像素(device-independent pixel),简称DIPs。
5. 屏幕密度
屏幕密度是指一个设备表面上存在的像素数量,它通常以每英寸有多少像素来计算(PPI)
6. 设备像素比(device pixel ratio)dpr
设备像素比 = 物理像素 / 设备独立像素
众所周知,iPhone6的设备宽度和高度为375pt * 667pt,可以理解为设备的独立像素;而其dpr为2,根据上面公式,我们可以很轻松得知其物理像素为750pt * 1334pt
在不同的屏幕上,CSS像素所呈现的物理尺寸是一致的,而不同的是CSS像素所对应的物理像素具数是不一致的。在普通屏幕下1个CSS像素对应1个物理像素,而在Retina屏幕下,1个CSS像素对应的却是4个物理像素。
# Media Queries
@media screen and (max-width: 600px) { /*当屏幕尺寸小于600px时,应用下面的CSS样式*/
/*你的css代码*/
}
# Flex弹性布局
它的viewport是固定的:
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no">
高度定死,宽度自适应,元素都采用px做单位
# rem + viewport 缩放
根据rem将页面放大dpr倍, 然后viewport设置为1/dpr.
如iphone6 plus的dpr为3, 则页面整体放大3倍, 1px(css单位)在plus下默认为3px(物理像素) 然后viewport设置为1/3, 这样页面整体缩回原始大小. 从而实现高
# Flexible
flexible实际上就是能过JS来动态改写meta标签,代码类似这样:
var devicePixelRatio = win.devicePixelRatio;
dpr = devicePixelRatio || 1;
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;
......
.......
metaEl.setAttribute('content', 'initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
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);
}
事实上他做了这几样事情:
- 动态改写
meta标签 - 给
html元素添加data-dpr属性,并且动态改写data-dpr的值 - 给
html元素添加font-size属性,并且动态改写font-size的值
目前Flexible会将视觉稿分成100份(主要为了以后能更好的兼容vh和vw),而每一份被称为一个单位a。同时1rem单位被认定为10a。针对我们这份视觉稿可以计算出:
1a = 7.5px
1rem = 75px
那么我们这个示例的稿子就分成了10a,也就是整个宽度为10rem,html对应的font-size为75px
这样一来,对于视觉稿上的元素尺寸换算,只需要原始的px值除以rem基准值即可。例如此例视觉稿中的图片,其尺寸是176px * 176px,转换成为2.346667rem * 2.346667rem。
如何快速计算
PostCSS(px2rem)
.selector {
width: 150px;
height: 64px; /*px*/
font-size: 28px; /*px*/
border: 1px solid #ddd; /*no*/
}
.selector {
width: 2rem;
border: 1px solid #ddd;
}
[data-dpr="1"] .selector {
height: 32px;
font-size: 14px;
}
[data-dpr="2"] .selector {
height: 64px;
font-size: 28px;
}
[data-dpr="3"] .selector {
height: 96px;
font-size: 42px;
}
文本字号不建议使用rem 前面大家都见证了如何使用rem来完成H5适配。那么文本又将如何处理适配。是不是也通过rem来做自动适配。
显然,我们在iPhone3G和iPhone4的Retina屏下面,希望看到的文本字号是相同的。也就是说,我们不希望文本在Retina屏幕下变小,另外,我们希望在大屏手机上看到更多文本,以及,现在绝大多数的字体文件都自带一些点阵尺寸,通常是16px和24px,所以我们不希望出现13px和15px这样的奇葩尺寸。
如此一来,就决定了在制作H5的页面中,rem并不适合用到段落文本上。所以在Flexible整个适配方案中,考虑文本还是使用px作为单位。只不过使用[data-dpr]属性来区分不同dpr下的文本字号大小。
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;
}
为了能更好的利于开发,在实际开发中,我们可以定制一个font-dpr()这样的Sass混合宏:
@mixin font-dpr($font-size){
font-size: $font-size;
[data-dpr="2"] & {
font-size: $font-size * 2;
}
[data-dpr="3"] & {
font-size: $font-size * 3;
}
}
使用@include font-dpr(16px);
# vw/vh
以前的Flexible方案是通过JavaScript来模拟vw的特性,那么到今天为止,vw已经得到了众多浏览器的支持,也就是说,可以直接考虑将vw单位运用于我们的适配布局中
众所周知,vw是基于Viewport视窗的长度单位,这里的视窗(Viewport)指的就是浏览器可视化的区域,而这个可视区域是window.innerWidth/window.innerHeight的大小。用下图简单的来示意一下:

:::Tip
vw:是Viewport's width的简写,1vw等于window.innerWidth的1%
vh:和vw类似,是Viewport's height的简写,1vh等于window.innerHeihgt的1%
vmin:vmin的值是当前vw和vh中较小的值
vmax:vmax的值是当前vw和vh中较大的值
:::
先来回归到我们的实际业务中来。目前出视觉设计稿,我们都是使用750px宽度的,从上面的原理来看,那么100vw = 750px,即1vw = 7.5px。那么我们可以根据设计图上的px值直接转换成对应的vw值
上面解决了px到vw的转换计算。那么在哪些地方可以使用vw来适配我们的页面。根据相关的测试:
:::Tip
容器适配,可以使用vw
文本的适配,可以使用vw
大于1px的边框、圆角、阴影都可以使用vw
内距和外距,可以使用vw
:::
采用vw来做适配处理并不是只有好处没有任何缺点。有一些细节之处还是存在一定的缺陷的。比如当容器使用vw单位,margin采用px单位时,很容易造成整体宽度超过100vw,从而影响布局效果。对于类似这样的现象,我们可以采用相关的技术进行规避。比如将margin换成padding,并且配合box-sizing。只不过这不是最佳方案,随着将来浏览器或者应用自身的Webview对calc()函数的支持之后,碰到vw和px混合使用的时候,可以结合calc()函数一起使用,这样就可以完美的解决。
另外一点,px转换成vw单位,多少还会存在一定的像素差,毕竟很多时候无法完全整除。