4. viewport 视口
上一篇文章介绍了有关移动端中一些基本的概念,本文要介绍的 viewport 也非常重要。
本文主要包含以下内容:
- 视口的概念
- 视口的分类
- 视口相关属性
- 关于缩放
视口的概念
视口,英语全称 viewport,指的是设备的屏幕上能用来显示我们的网页的那一块区域,再具体一点,就是浏览器上(也可能是一个 app 中的 webview)用来显示网页的那部分区域。
在早期 PC 年代,电脑上的网页常见的宽度有:960px、980px、1190px、1210px,其中以 980px 居多。
到了移动端时代,由于一开始并不存在专门的移动端网页,所以在最开始的时候也就自然而然沿用了这一设定,以苹果的 Safari 为首的浏览器默认采用的视口宽度就是 980px 的宽度,后面的很多安卓机型也都就沿袭了这一设定。
故事是这样的,有一天乔帮主在想一个问题,就是自己的苹果手机如果在市场上火爆了,但是各个网站还没有来得及制作手机版本的网页,那么用户不得不用手机访问电脑版的网页。如何用小屏幕访问大屏幕的页面也同样可读呢?
乔帮主就想着为手机固定一个视口宽度,让手机的视口宽度等于世界上绝大多数 PC 网页的版心宽度,就是 980px。这样,用手机访问电脑版网页的时候,刚好没有留白。像从 3000 米高空,俯瞰整个页面,用户想看哪个区域,可以用两个指头捏合,放大页面。
安卓手机也非常尊重乔帮主的决定,都把自己的手机的视口定位 980px。
下图列出了一些设备上浏览器的默认 viewport 的宽度:

此时使用手机浏览器打开电脑版的网页的效果如下:
可以看到在手机端的浏览器中,对网页进行了缩放,以便显示整张网页。之后用户可以将两个指头捏合,放大页面。
我们也可以进行一个简单的验证,如下:
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
.container{
width: 375px;
display: flex;
}
.container>div{
width: 50%;
height: 50px;
}
.container>div:nth-child(1) {
background-color: red;
}
.container>div:nth-child(2) {
background-color: yellow;
}
</style>
</head>
<body>
<div class="container">
<div></div>
<div></div>
</div>
</body>
在上面的代码中,我们将默认的视口设置取消了,此时就会采用默认的 980px,而我们的 div.contianer 设置的为 375px,效果如下:
可以看到,由于默认的视口宽度为 980px,因此 div.container 并没有占满,只占了一半都还不到。
后来随着移动端的发展,渐渐的专门针对移动端的网页就出来,如下图所示:
此时就不能像之前一样视口一直按照 PC 端的 980px 来作为标准,因为 980px 实际上是超出了设备屏幕的,只不过浏览器智能的进行了缩放。至于缩放的比例是多少,我们后面再说。
此时就应该按照设备的 CSS 像素来作为标准。还记得 iPhone 6/7/8 的 CSS 像素是多少么?没错,是 375 x 667,也就是说现在的网页就应该按照 375px 的标准。
那么如何进行修改呢?
不着急,在此之前我们再来了解一个知识点,那就是视口的分类。
视口的分类
ppk 大神对于移动设备上的 viewport 有着非常多的研究(第一篇,第二篇,第三篇),ppk 认为,移动设备上有三个 viewport。分别是:
- layout viewport
- visual viewport
- ideal viewport
ppk 把浏览器默认的 viewport,也就是前面我们所讲的 980px 宽的 viewport 叫做 layout viewport。这个 layout viewport 的宽度可以通过 window.innerWidth 来获取。
然而,layout viewport 的宽度是大于浏览器可视区域的宽度的,所以我们还需要一个 viewport 来代表浏览器可视区域的大小,ppk 把这个 viewport 叫做 visual viewport。visual viewport 的宽度可以通过 document.documentElement.clientWidth 来获取。
现在我们已经有两个 viewport 了:layout viewport 和 visual viewport。layout viewport 是网页实际的宽度,而 visual viewport 是设备的宽度。
在早期 iPhone3GS 时代,由于一个 CSS 像素对应一个设备像素,因此 visual viewport 和设备 CSS 宽度是等价的。
但是从 iPhone4 时代开始,一个 CSS 像素对应 2 个设备像素,设备的 CSS 宽度仍然是 320px,但是物理像素翻了倍。
于是 ppk 又发明了一个新词,叫做 ideal viewport,也就是第三个 viewport ——移动设备的完美适配 viewport,其实对应的就是设备的 CSS 宽度。
例如 iPhone 3GS 和 iPhone4 的 ideal viewport 宽度都是 320px,无论它的设备像素是 320 还是 640,这两款 iPhone 中,CSS 中的 320px 就代表 iPhone 屏幕的宽度。
最后再总结一下:ppk 把移动设备上的 viewport 分为 layout viewport、visual viewport 和 ideal viewport 三类,其中的 ideal viewport 是最适合移动设备的 viewport。
ideal viewport 的意义在于,无论在何种分辨率的屏幕下,那些针对 ideal viewport 而设计的网站,不需要用户手动缩放,也不需要出现横向滚动条,都可以完美的呈现给用户。
视口相关属性
移动设备默认的 viewport 是 layout viewport,也就是那个比屏幕要宽的 viewport,但在进行移动设备网站的开发时,我们需要的是 ideal viewport。那么怎么才能得到 ideal viewport 呢?
这就该轮到 meta 标签出场了。
我们在开发移动设备的网站时,最常见的的一个动作就是把下面这个东西复制到我们的 head 标签中:
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no">
该 meta 标签的作用是让当前 viewport 为 ideal viewport,同时不允许用户手动缩放。
也许允不允许用户缩放不同的网站有不同的要求,但让 viewport 的宽度为 ideal viewport,这个应该是大家都想要的效果,如果你不这样的设定的话,那就会使用那个比屏幕宽的默认 viewport,在不缩放的情况下会出现横向滚动条。
name 为 viewport 的标 meta 标签最早是由苹果公司在其 safari 浏览器中引入的,目的就是解决移动设备的 viewport 问题。后来安卓以及各大浏览器厂商也都纷纷效仿,引入对 meta viewport 的支持,事实也证明这个东西还是非常有用的。
在苹果的规范中,meta viewport 有 6 个属性(暂且把 content 中的那些东西称为一个个属性和值),如下:
| 属性名 | 作用 |
|---|---|
| width | 设置 layout viewport 的宽度,为一个正整数,或字符串"device-width" |
| height | 设置 layout viewport 的高度,这个属性对我们并不重要,很少使用 |
| initial-scale | 设置页面的初始缩放值,为一个数字,可以带小数 |
| minimum-scale | 允许用户的最小缩放值,为一个数字,可以带小数 |
| maximum-scale | 允许用户的最大缩放值,为一个数字,可以带小数 |
| user-scalable | 是否允许用户进行缩放,值为"no"或"yes", no 代表不允许,yes 代表允许 |
这些属性可以同时使用,也可以单独使用或混合使用,多个属性同时使用时用逗号隔开就行了。
最简单的方式是设置 width=device-width,表示把默认的 layout viewport 的宽度设为移动设备的屏幕宽度。
下图是这句代码在各大移动端浏览器上的测试结果:
可见通过 width=device-width,所有浏览器都能把当前的 viewport 宽度变成 ideal viewport 的宽度。
例如:
<div class="container">
<div></div>
<div></div>
</div>
<script>
console.log("document.documentElement.clientWidth:",document.documentElement.clientWidth);
console.log("window.innerWidth:",window.innerWidth);
</script>
* {
margin: 0;
padding: 0;
}
.container{
width: 375px;
display: flex;
}
.container>div{
width: 50%;
height: 50px;
}
.container>div:nth-child(1) {
background-color: red;
}
.container>div:nth-child(2) {
background-color: yellow;
}
<meta name="viewport" content="width=device-width">
效果如下:
但要注意的是,在 iphone 和 ipad 上,无论是竖屏还是横屏,宽度都是竖屏时 ideal viewport 的宽度。
另外,如果我们只设置 initial-scale 为 1,也能达到相同的效果,也就是说能把当前的 viewport 变为 ideal viewport。
<meta name="viewport" content="initial-scale=1">
what?这句代码的作用只是不对当前的页面进行缩放,也就是页面本该是多大就是多大。那为什么会有 width=device-width 的效果呢?
要想清楚这件事情,首先你得弄明白这个缩放是相对于什么来缩放的,因为这里的缩放值是 1,也就是没缩放,但却达到了 ideal viewport 的效果,所以,那答案就只有一个了,缩放是相对于 ideal viewport 来进行缩放的,当对 ideal viewport 进行 100% 的缩放,也就是缩放值为 1 的时候,不就得到了 ideal viewport 吗?
事实证明,的确是这样的。下图是各大移动端的浏览器当设置了 content="initial-scale=1" 后是否能把当前的 viewport 宽度变成 ideal viewport 的宽度的测试结果。
测试结果表明 initial-scale=1 也能把当前的 viewport 宽度变成 ideal viewport 的宽度,但这次轮到了 windows phone 上的 IE 无论是竖屏还是横屏都把宽度设为竖屏时 ideal viewport 的宽度。但这点小瑕疵已经无关紧要了。
但如果 width 和 initial-scale=1 同时出现,并且还出现了冲突呢?比如:
<meta name="viewport" content="width=400, initial-scale=1">
width=400 表示把当前 viewport 的宽度设为 400px,initial-scale=1 则表示把当前 viewport 的宽度设为 ideal viewport 的宽度,那么浏览器到底该服从哪个命令呢?是书写顺序在后面的那个吗?
不是。当遇到这种情况时,浏览器会取它们两个中较大的那个值。例如,当 width=400,ideal viewport 的宽度为 320 时,取的是 400;当 width=400,ideal viewport 的宽度为 480 时,取的是 ideal viewport 的宽度。
最后,总结一下,要把当前的 viewport 宽度设为 ideal viewport 的宽度,既可以设置 width=device-width,也可以设置 initial-scale=1,但这两者各有一个小缺陷,就是 iphone、ipad 以及 IE 会横竖屏不分,通通以竖屏的 ideal viewport 宽度为准。
所以,最完美的写法应该是,两者都写上去,这样就 initial-scale=1 解决了 iphone、ipad 的毛病,width=device-width 则解决了 IE 的毛病:
<meta name="viewport" content="width=device-width, initial-scale=1">
关于缩放
最后要说一下关于缩放的问题。
前面我们说过,即使使用手机浏览器打开 PC 端的网页,手机浏览器也能很智能的对页面进行缩放,从而不出现滚动条。
那么这个缩放比例究竟是多少呢?
这里其实有一个公式,那就是:
当前缩放值 = ideal viewport宽度 / layout viewport 宽度
比如说,我们不设置任何的 viewport meta 标签,此时 layout viewport 的宽度为 980px,但我们可以看到浏览器并没有出现横向滚动条,因为浏览器默认的把页面缩小了。
根据上面的公式,缩放比就为 375 / 980 = 0.38 左右。
也就是当前的 initial-scale 默认值应该是 0.38 这样子。但是如果指定了 initial-scale 的值,那么这个默认值就不起作用了。
最后总结一下,为了保证在不同设备中,显示网页的视口正常,最常见的设置如下:
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no">
之后我们书写移动端页面,也会采取此视口设置。
-EOF-