浅说移动前端中 Viewport 和 Viewport units
dwqs opened this issue · 4 comments
Viewport units
Viewport units(视口单位)是一系列 CSS 长度单位的统称,主要包含 vh
、vw
、vmin
和 vmax
等四个 CSS3 中新增的长度单位,在CSS:7个你可能不认识的单位一文中有对其作了简单的介绍:
vh
:视口高度的1%vw
:视口宽度的1%vmin
:vw
和vh
中的较小值vmax
:vw
和vh
中的较大值
兼容性
对于新属性,在使用之前都需要提前了解一下其兼容性。caniuse 提供的兼容性数据如下:
数据截止:2018/07/29
从兼容性来上看,目前并不是所有的 Viewport units 被现代浏览器所支持:
- IE9 不支持
vmin
- IE10+、Edge 15-、Safari 6、 iOS Safari 7.1- 以及 Chrome 25- 不支持
vmax
但从图表数据来看,无论是桌面端还是移动端,浏览器对 vh
和 vw
的支持都很好。
微信内置浏览器的内核是 X5。X5 对 viewport units 的支持如下:
什么是Viewport(视口)
Viewport
的作用是限制网站的初始包含块元素,即 html
元素。
通常情况下,所有的块元素的宽度都是父元素宽度的100%,因而 body
元素和它的父元素(html
元素)一样宽。那 html
元素的宽度是多少?在实际开发中我们会发现,其宽度总是和浏览器窗口的宽度保持一致。从理论上来讲,html
元素的宽度是受限于 Viewport
的,其宽度总是 Viewport
宽度的 100%。
因而,Viewport
就是浏览器窗口,它不是一个 HTML 结构,因而不能通过 CSS 去改变它的形态。在桌面浏览器中,Viewport
仅表示浏览器窗口的大小(即窗口的可视区域),但在移动端,它要复杂很多,这涉及到三个视口。
视觉视口
视觉视口(visual viewport)是指网页在物理屏幕上的可视区域。用户可以通过滚动来改变网页在可视区域显示的部分,或者通过缩放改变显示部分的大小。
在现代浏览器中,如果要获取视觉视口的大小,可以通过 window.innerHeight
和 window.innerWidth
,其返回值分别包含了水平滚动条的高度和垂直滚动条的宽度。
布局视口
如果网页的大小超出了设备屏幕的大小,视觉视口仅能显示网页中的某一部分。在移动端,CSS 的布局,HTML 元素宽度的百分比则均是相对于布局视口(layout viewport)进行计算的。通常而言,布局视口的宽度要大于或等于可视视口的宽度。
html
元素的宽度则等于布局视口的初始宽度,而布局视口的宽度则和设备的浏览器有关,如 iOS Safari 是 980px,Android WebKit 则是 800px。
在现代浏览器中,如果要获取布局视口的大小,可以通过 document.documentElement.clientWidth/clientHeight
来得到对应的宽高值(不含滚动条),这两个属性始终返回布局视口的宽高。如果要获取 html
元素的宽高,则可以通过 document.documentElement.offsetWidth/offsetHeight
来得到对应的宽高值(含滚动条)。
一般情况下,二者返回的对应属性值是一致的。但如果给 html
元素设置了高宽值,则返回就不一样了:
理想视口
理想视口(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 中的视口
根据上文,1vw
和 1vh
分别是视口宽度和高度的 1%。在桌面端,视口指的就是浏览器的可视区域:
1vw = window.innerWidth / 100
1vh = window.innerHeight / 100
那在移动端,这里的「视口」则是指布局视口。
在布局视口中,1vw
和 1vh
的值是不固定的,这跟设备的放置方向有关:
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%;
}
参考资料
- 《JavaScript 高级程序设计》(第3版)
- A tale of two viewports — part one
- A tale of two viewports — part two
- Meta viewport
您好,我想转载您的这篇文章到公众号 前端指南中,我会注明出处,请问可以么
@nanhupatar 可以
非常感谢
兄弟 我必须关注你了。太溜了。学到了。