dwqs / dwqs/blog

浅说移动前端中 Viewport 和 Viewport units

Open
#69 4 comments 2 reactions 0 assignees View on GitHub
Dominant language
JavaScript
Stars
3.8k
Forks
443
PR merge metrics
No merged PRs in 30d

Description

## Viewport units
Viewport units(视口单位)是一系列 CSS 长度单位的统称,主要包含 `vh`、`vw`、`vmin` 和 `vmax` 等四个 CSS3 中新增的[长度单位](https://developer.mozilla.org/en-US/docs/Web/CSS/length),在[CSS:7个你可能不认识的单位](https://github.com/dwqs/blog/issues/31)一文中有对其作了简单的介绍:

* `vh`:视口高度的1%
* `vw`:视口宽度的1%
* `vmin`:`vw` 和 `vh` 中的较小值
* `vmax`:`vw` 和 `vh` 中的较大值

### 兼容性
对于新属性,在使用之前都需要提前了解一下其兼容性。[caniuse](https://caniuse.com/#search=vh) 提供的兼容性数据如下:

![caniuse](https://user-images.githubusercontent.com/7871813/43363073-b668be6c-932e-11e8-8998-31031acf458b.png)

>数据截止: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 的支持如下:

![x5](https://user-images.githubusercontent.com/7871813/43365752-414e906c-9364-11e8-9d84-cc33ac3c175d.png)

> 截图来自: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](https://user-images.githubusercontent.com/7871813/43363609-26e0d164-933b-11e8-85e5-1ec21d5ba398.png)

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

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

![layout viewport](https://user-images.githubusercontent.com/7871813/43363786-25eb3444-933f-11e8-80fd-f12502523e73.png)

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

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

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

![diffrent](https://user-images.githubusercontent.com/7871813/43363906-0370bee0-9342-11e8-8223-77c3550201eb.png)

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

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

另外,需要注意的是:

* 通过 viewport 的 `meta` 标签,布局视口可被设置成理想视口的值:

```html

```

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

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

## Viewport units 中的视口
根据上文,`1vw` 和 `1vh` 分别是视口宽度和高度的 1%。在桌面端,视口指的就是浏览器的可视区域:

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

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

![viewport](https://user-images.githubusercontent.com/7871813/43364781-98bf4f5a-9353-11e8-91bf-b00275ae3dc1.png)

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

![vw/vh](https://user-images.githubusercontent.com/7871813/43364787-bd243dec-9353-11e8-888c-a41686287050.png)

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

准备一个简单 demo 如下:

```html
// demo.html




body {
height: 100vh
}



内容区域

```

* 在桌面端浏览器访问 demo.html,会发现会出现垂直滚动条。这是由于 `body` 有默认的 `margin` 值,因而解决该问题的方式也很简单,将 `body` 元素的 `margin` 值设置成 0 就行:

```css
body {
height: 100vh;
margin: 0
}
```
* 在移动端的浏览器访问 demo.html,也会出现垂直滚动条。但这跟`body` 有默认的 `margin` 值并无多大关系,而是浏览器「刻意为之」,具体可见 [CSS3 100vh not constant in mobile browser](https://stackoverflow.com/questions/37112218/css3-100vh-not-constant-in-mobile-browser)。如果要避免滚动条,可尝试如下方式:

```css
body {
height: 100%;
}
```

## 参考资料
* 《JavaScript 高级程序设计》(第3版)
* [A tale of two viewports — part one](https://www.quirksmode.org/mobile/viewports.html)
* [A tale of two viewports — part two](https://www.quirksmode.org/mobile/viewports2.html)
* [Meta viewport](https://www.quirksmode.org/mobile/metaviewport/)

Contributor guide

No contributing guide indexed for this repository

Research direction

The issue contains a complete article and an inline demo.html example, but names no repository file or test and does not state what should change. First clarify the intended documentation scope, then check the viewport compatibility claims, reference links, and demo.html example; done should be defined as an agreed, reviewable article update.

Written by the indexing model from the issue text.

Assessment

Tech stack
css, html, javascript
Domain
documentation, frontend, web-dev
Issue type
Documentation
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.