dwqs/blog

浅说移动前端中 Viewport 和 Viewport units

dwqs opened this issue · 4 comments

dwqs commented

Viewport units

Viewport units(视口单位)是一系列 CSS 长度单位的统称,主要包含 vhvwvminvmax 等四个 CSS3 中新增的长度单位,在CSS:7个你可能不认识的单位一文中有对其作了简单的介绍:

  • vh:视口高度的1%
  • vw:视口宽度的1%
  • vminvwvh 中的较小值
  • vmaxvwvh 中的较大值

兼容性

对于新属性,在使用之前都需要提前了解一下其兼容性。caniuse 提供的兼容性数据如下:

caniuse

数据截止:2018/07/29

从兼容性来上看,目前并不是所有的 Viewport units 被现代浏览器所支持:

  • IE9 不支持 vmin
  • IE10+、Edge 15-、Safari 6、 iOS Safari 7.1- 以及 Chrome 25- 不支持 vmax

但从图表数据来看,无论是桌面端还是移动端,浏览器对 vhvw 的支持都很好。

微信内置浏览器的内核是 X5。X5 对 viewport units 的支持如下:

x5

截图来自:http://res.imtt.qq.com/tbs/incoming20160419/home.html

什么是Viewport(视口)

Viewport 的作用是限制网站的初始包含块元素,即 html 元素。

通常情况下,所有的块元素的宽度都是父元素宽度的100%,因而 body 元素和它的父元素(html 元素)一样宽。那 html 元素的宽度是多少?在实际开发中我们会发现,其宽度总是和浏览器窗口的宽度保持一致。从理论上来讲,html 元素的宽度是受限于 Viewport 的,其宽度总是 Viewport 宽度的 100%。

因而,Viewport 就是浏览器窗口,它不是一个 HTML 结构,因而不能通过 CSS 去改变它的形态。在桌面浏览器中,Viewport 仅表示浏览器窗口的大小(即窗口的可视区域),但在移动端,它要复杂很多,这涉及到三个视口。

视觉视口

视觉视口(visual viewport)是指网页在物理屏幕上的可视区域。用户可以通过滚动来改变网页在可视区域显示的部分,或者通过缩放改变显示部分的大小。

visual viewport

在现代浏览器中,如果要获取视觉视口的大小,可以通过 window.innerHeightwindow.innerWidth,其返回值分别包含了水平滚动条的高度和垂直滚动条的宽度。

布局视口

如果网页的大小超出了设备屏幕的大小,视觉视口仅能显示网页中的某一部分。在移动端,CSS 的布局,HTML 元素宽度的百分比则均是相对于布局视口(layout viewport)进行计算的。通常而言,布局视口的宽度要大于或等于可视视口的宽度。

layout viewport

html 元素的宽度则等于布局视口的初始宽度,而布局视口的宽度则和设备的浏览器有关,如 iOS Safari 是 980px,Android WebKit 则是 800px。

在现代浏览器中,如果要获取布局视口的大小,可以通过 document.documentElement.clientWidth/clientHeight 来得到对应的宽高值(不含滚动条),这两个属性始终返回布局视口的宽高。如果要获取 html 元素的宽高,则可以通过 document.documentElement.offsetWidth/offsetHeight 来得到对应的宽高值(含滚动条)。

一般情况下,二者返回的对应属性值是一致的。但如果给 html 元素设置了高宽值,则返回就不一样了:

diffrent

理想视口

理想视口(ideal viewport)即通常所说的屏幕分辨率,给出了网页在移动设备上的理想大小。理想视口没有一个固定的尺寸,不同的设备拥有不同的理想视口,可以在 viewportsizes 上查询不同设备的理想视口宽度。

理想视口是最适合移动设备的视口,其宽度等于移动设备的屏幕宽度。只要在 CSS 中把某一元素的宽度设为理想视口的宽度(单位用px),那么这个元素的宽度就是设备屏幕的宽度了,也就是宽度为100%的效果。理想视口的意义在于,无论在何种分辨率的屏幕下,那些针对理想视口而设计的网站,不需要用户手动缩放,也不需要出现横向滚动条,都可以完美的呈现给用户。

另外,需要注意的是:

  • 通过 viewport 的 meta 标签,布局视口可被设置成理想视口的值:
<meta name="viewport" content="width=device-width,initial-scale=1">

设置 meta 标签之后,则可以通过 document.documentElement.clientWidth/Height 来获取理想视口的大小;如果没设置,部分机型(如 iPhone 7)提供了 screen.width/height 来获取其大小。需要注意的是有的机型虽提供了 screen.width/height 的值,但这个值并不一定等于设备的物理像素。

  • 在 viewport 的 meta 标签中,所有的 scale 指令都是相对于理想视口而不是布局视口。

Viewport units 中的视口

根据上文,1vw1vh 分别是视口宽度和高度的 1%。在桌面端,视口指的就是浏览器的可视区域:

1vw = window.innerWidth / 100
1vh = window.innerHeight / 100

那在移动端,这里的「视口」则是指布局视口。

viewport

在布局视口中,1vw1vh 的值是不固定的,这跟设备的放置方向有关:

vw/vh

100vh 的问题

根据上文,如果给元素设置 height: 100vh,那元素的高度应该会刚好铺满整个可视区域,但事实并非如此。

准备一个简单 demo 如下:

// demo.html
<html>
  <head>
  	<meta name="viewport" content="width=device-width,initial-scale=1">
  	<style>
  	  body {
  	  	 height: 100vh
  	  }
  	</style>
  </head>
  <body>
    内容区域
  </body>
</html>
  • 在桌面端浏览器访问 demo.html,会发现会出现垂直滚动条。这是由于 body 有默认的 margin 值,因而解决该问题的方式也很简单,将 body 元素的 margin 值设置成 0 就行:
body {
  height: 100vh;
  margin: 0
}
  • 在移动端的浏览器访问 demo.html,也会出现垂直滚动条。但这跟body 有默认的 margin 值并无多大关系,而是浏览器「刻意为之」,具体可见 CSS3 100vh not constant in mobile browser。如果要避免滚动条,可尝试如下方式:
body {
  height: 100%;
}

参考资料

您好,我想转载您的这篇文章到公众号 前端指南中,我会注明出处,请问可以么

dwqs commented

非常感谢

兄弟 我必须关注你了。太溜了。学到了。