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来度量

img-w150

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相关的单位有四个,分别为vwvhvminvmax

  • vw:是Viewport’s width的简写,1vw等于window.innerWidth1%
  • vh:和vw类似,是Viewport’s height的简写,1vh等于window.innerHeihgt1%
  • vminvmin的值是当前vwvh中较小的值
  • vmaxvmax的值是当前vwvh中较大的值

vminvmax是根据Viewport中长度偏大的那个维度值计算出来的,如果window.innerHeight > window.innerWidthvmin取百分之一的window.innerWidthvmax取百分之一的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%;
}

这样就可以完美的实现长宽比的效果。