Viewport剖析
转载自W3CPlus创始人 大漠 博客: 原文链接
1. 桌面(pc)浏览器
1.1 设备的pixels和CSS的pixels
pixels决定了你工作所用的那些设备上正式的分辨率。在大多数情况下,能够从screen.width/height上取出具体值
如果用户缩放(zoom)了浏览器,当然必须改变计算方式。
现代浏览器上的缩放,是基于“伸展”pixels。结果是,html元素上的宽度并没有因为缩放200%而由128pix变成256px,而是真实的pixels的被计算成了双倍。html元素在形式上依然是128CSS的pixels,即便它占用了256设备的pixels 。
- 有4个1像素,缩放为100%的html元素,CSS的pixels完整的和设备的pixels重叠:
- 当我们缩小浏览器时,CSS的pixels开始收缩,导致1单位的设备的pixels上重叠了多个CSS的pixels
- 同理,放大浏览器时,相反的事情发生了,CSS的pixels开始扩大,导致1单位的CSS的pixels上重叠了多个设备的pixels
你只需要关注CSS的pixels,这些pixels指定你的样式被如何渲染.
1.2 100% 缩放
在缩放级别为100%时,1单位的CSS的pixel是严格相等于1单位的设备pixel,通常会在100%缩放级别下测试网站
1.2.1 屏幕尺寸 Screen size
含义:用户的屏幕的完整大小。这两个属性包含了用户屏幕的完整宽度/高度,尺寸使用设备的pixels来定义,他们的值不会因为缩放而改变(显示器的特征,而不是浏览器)
度量:设备的pixels,screen.width/height (基本无用)
兼容性问题:IE8里,不管使用IE7模式还是IE8模式,都以CSS的pixels来度量
1.2.2 浏览器尺寸 Window size
含义:包含滚动条尺寸的浏览器完整尺寸; 想要知道的浏览器的内部尺寸。它定义了当前用户有多大区域,可供你的CSS布局占用 度量:CSS的pixels, window.innerWidth/Height 兼容性问题:IE不支持,Opera用设备pixels来度量
1.2.3 滚动移位 Scrolling offset
含义:页面的移位 , 定义了页面(document)的相对于窗口原点的水平、垂直位移。因此你能够定位用户滚动了多少的滚动条距离。
度量:CSS的pixels, window.pageX/YOffset
兼容性问题:pageXOffset 和 pageYOffset 在 IE 8 及之前版本的IE不支持, 使用”document.body.scrollLeft” and “document.body.scrollTop” 来取代
1.2.4 视窗 viewport
viewport的功能在于控制你网站的最高块状(block)容器:<html>
元素。viewport是严格的等于浏览器的窗口,viewport不是一个HTML的概念,不能通过CSS修改它。
举个例子~假设你定义了一个可变尺寸的布局(liquid layout),且你定义一个侧边栏的宽度为width: 10%。当你改变浏览器窗口大小时,该侧边栏会自动扩张和收缩。
原理是侧边栏的宽度为它父元素宽度的10%
一个块级元素占有起父元素的100%的宽度(这里有异常情况,暂时忽略)。所以<body>
的宽度就是其父元素<html>
的宽度。
那么<html>
元素到底有多宽?因为它的宽度恰好为浏览器的宽度。所以你的侧边栏宽度width: 10%会占用10%的浏览器宽度。所以的web开发人员都直观的知道和使用该特性了。 但是你也许不知道原理。在原理上,<html>
的宽度受viewport所限制,<html>
元素为viewport宽度的100%。
1.2.5 页面宽度 document width
- 度量viewport 尺寸 :
含义:viewport的尺寸
度量:CSS的pixels, document. documentElement. clientWidth/Height
兼容性问题:无
document.documentElement实际上就是·<html>
元素:HTML文档的根元素,然而viewport是比<html>
更高级别的元素,打个比喻,它是容纳<html>
元素的元素。
document. documentElement. clientWidth/Height只会给出viewport的尺寸,而不管<html>
元素尺寸如何改变
- 度量
<html>
元素
含义:
<html>
的尺寸度量:CSS的pixels, document. documentElement. offsetWidth/Height
兼容性问题:IE用这个值标示viewport的尺寸而非
<html>
如果clientWidth/Height一直用以标示viewport的尺寸,我们该如何去获取<html>
元素的尺寸呢?答案是:document.documentElement.offsetWidth/Height。
这个特性对真实的让你访问块级元素<html>
元素,如果你为<html>
元素赋值了宽度,offsetWidth会真实的反应出来
1.3 几种尺寸区别
- window.innerWidth/Height 包含滚动条
- document. documentElement. clientWidth/Height: 不包含滚动条 viewport尺寸
- document. documentElement. offsetWidth:html尺寸
1.4 事件坐标
- pageX/Y:从
<html>
原点到事件触发点的CSS的 pixels (使用概率:90%) - clientX/Y:从viewport原点(浏览器窗口)到事件触发点的CSS的 pixels (使用概率:10%)
- screenX/Y:从用户显示器窗口原点到事件触发点的设备 的 pixels。(使用概率:0%)
- 兼容性问题:IE不支持pageX/Y,IE使用CSSpixels来度量screanX/Y
1.5 Media查询
- 含义:可以根据页面的特定宽度来定义特殊的CSS规则。
- 度量:width/height 和 device-width/device-heigh
- 兼容性问题:IE不支持.
div.sidebar { width: 300px; }
@media all and (max-width: 400px) {
div.sidebar { width: 100px; }
}
/*如果宽度大于400px,那么sidebar宽度为300px。反之,sidebar宽度为100px*/
- device-width/height: 使用screen.width/height来做为的判定值。该值以设备的pixels来度量
- width/height: 使用documentElement.clientWidth/Height即viewport的值。该值以CSS的pixels来度量
2. 移动端浏览器
2.1 一些概念
2.1.1 视窗 viewport
简单的理解,viewport是严格等于浏览器的窗口。在桌面浏览器中,viewport就是浏览器窗口的宽度高度。但在移动端设备上就有点复杂。
移动端的viewport太窄,为了能更好为CSS布局服务,所以提供了两个viewport:虚拟的viewportvisualviewport和布局的viewportlayoutviewport。
2.1.2 物理像素(physical pixel)
又称为设备像素
- 设备独立像素(density-independent pixel)
设备独立像素也称为密度无关像素,可以认为是计算机坐标系统中的一个点,这个点代表一个可以由程序使用的虚拟像素(比如说CSS像素),然后由相关系统转换为物理像素。
- CSS像素
CSS像素是一个抽像的单位,主要使用在浏览器上,用来精确度量Web页面上的内容。一般情况之下,CSS像素称为与设备无关的像素(device-independent pixel),简称DIPs。
- 屏幕密度
屏幕密度是指一个设备表面上存在的像素数量,它通常以每英寸有多少像素来计算(PPI)。
- 设备像素比(device pixel ratio)
设备像素比简称为dpr,其定义了物理像素和设备独立像素的对应关系。它的值可以按下面的公式计算得到:
设备像素比 = 物理像素 / 设备独立像素
在JavaScript中,可以通过window.devicePixelRatio获取到当前设备的dpr。
在CSS中,可以通过-webkit-device-pixel-ratio,-webkit-min-device-pixel-ratio和 -webkit-max-device-pixel-ratio进行媒体查询,对不同dpr的设备,做一些样式适配(这里只针对webkit内核的浏览器和webview)。
dip或dp,(device independent pixels,设备独立像素)与屏幕密度有关。dip可以用来辅助区分视网膜设备还是非视网膜设备。
iPhone6的设备宽度和高度为
375pt * 667pt
,可以理解为设备的独立像素;而其dpr为2
,根据上面公式,我们可以很轻松得知其物理像素为750pt * 1334pt
。
在不同的屏幕上,CSS像素所呈现的物理尺寸是一致的,而不同的是CSS像素所对应的物理像素具数是不一致的。在普通屏幕下1个CSS像素对应1个物理像素,而在Retina屏幕下,1个CSS像素对应的却是4个物理像素。
在移动端时代屏幕适配除了Layout之外,还要考虑到图片的适配,因为其直接影响到页面显示质量
- meta标签
viewport的meta
标签,其主要用来告诉浏览器如何规范的渲染Web页面,而你则需要告诉它视窗有多大。
在开发移动端页面,我们需要设置meta
标签如下:
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
代码以显示网页的屏幕宽度定义了视窗宽度。网页的比例和最大比例被设置为100%。
- CSS单位rem
在
W3C
规范中是这样描述rem
的: font size of the root element
简单的理解,rem就是相对于根元素<html>
的font-size来做计算。
2.2 适配方案
在移动端布局,我们需要面对两个最为重要的问题:
- 各终端下的适配问题
- Retina屏的细节处理
2.2.1 适配终端
以前的
Flexible方案
是通过JavaScript来模拟vw
的特性,如今vw
已经得到了众多浏览器的支持,也就是说,可以直接考虑将vw
单位运用于我们的适配布局中。
vw
是基于Viewport视窗的长度单位,这里的视窗(Viewport)指的就是浏览器可视化的区域,而这个可视区域是window.innerWidth/window.innerHeight
的大小。用下图简单的来示意一下:
在
CSS Values and Units Module Level 3
中和Viewport相关的单位有四个,分别为vw
、vh
、vmin
和vmax
。
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
中较大的值
vmin
和vmax
是根据Viewport中长度偏大的那个维度值计算出来的,如果window.innerHeight > window.innerWidth
则vmin
取百分之一的window.innerWidth
,vmax
取百分之一的window.innerHeight
计算。
这个方案中大胆的使用vw
来替代以前Flexible中的rem
缩放方案
目前出视觉设计稿,我们都是使用750px
宽度的,从上面的原理来看,那么100vw = 750px
,即1vw = 7.5px
。那么我们可以根据设计图上的px
值直接转换成对应的vw
值。看到这里,很多同学开始感到崩溃,又要计算,能不能简便一点,能不能再简单一点,其实是可以的,我们可以使用PostCSS的插件
postcss-px-to-viewport
,让我们可以直接在代码中写px
,比如:
[w-369]{ width: 369px; }
[w-369] h2 span {
background: #FF5000;
color: #fff;
display: inline-block;
border-radius: 4px;
font-size: 20px;
text-shadow: 0 2px 2px #FF5000;
padding: 2px 5px;
margin-right: 5px;
}
PostCSS编译之后就是我们所需要的带vw代码:
[w-369] { width: 49.2vw; }
[w-369] h2 span {
background: #ff5000;
color: #fff;
display: inline-block;
border-radius: .53333vw;
text-shadow: 0 0.26667vw 0.26667vw #ff5000;
padding: .26667vw .66667vw; }
[w-369] h2 span,
[w-369] i {
font-size: 2.66667vw;
margin-right: .66667vw;
}
在实际使用的时候,你可以对该插件进行相关的参数配置:
"postcss-px-to-viewport": {
viewportWidth: 750,
viewportHeight: 1334,
unitPrecision: 5,
viewportUnit: 'vw',
selectorBlackList: [],
minPixelValue: 1,
mediaQuery: false
}
假设你的设计稿不是750px
而是1125px
,那么你就可以修改vewportWidth
的值。有关于该插件的详细介绍,
可以阅读其官方使用文档
。
在哪些地方可以使用vw
来适配我们的页面。根据相关的测试:
- 容器适配,可以使用
vw
- 文本的适配,可以使用
vw
- 大于
1px
的边框、圆角、阴影都可以使用vw
- 内距和外距,可以使用
vw
另外有一个细节需要特别的提出,比如我们有一个这样的设计:
如果我们直接使用:
[w-188-246] {
width: 188px;
}
[w-187-246]{
width: 187px
}
最终的效果会造成[w-187-246]
容器的高度小于[w-188-246]
容器的高度。这个时候我们就需要考虑到
容器的长宽比缩放
。这方面的方案很多,但我还是推荐工具化来处理,这里推荐@一丝 姐姐写的一个PostCSS插件
postcss-aspect-ratio-mini
。这个插件使用很简单,不需要做任何的配置,你只需要本地安装一下就OK。使用的时候如下:
[aspectratio] {
position: relative;
}
[aspectratio]::before {
content: '';
display: block;
width: 1px;
margin-left: -1px;
height: 0;
}
[aspectratio-content] {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: 100%;
height: 100%;
}
[aspectratio][aspect-ratio="188/246"]{
aspect-ratio: '188:246';
}
编译出来:
[aspectratio][aspect-ratio="188/246"]:before {
padding-top: 130.85106382978725%;
}
这样就可以完美的实现长宽比的效果。