在阅读这篇文章之前,你需要了解设备像素、逻辑像素(设备独立像素)和CSS像素的区别,见我的前一篇文章。
layout viewport
layout viewport是网页布局的区域,它是<html>元素的父容器。只要你不在css中修改<html>元素的宽度,<html>元素的宽度就会撑满layout viewport的宽度。
很多时候浏览器窗口没有办法显示出layout viewport的全貌,但是它确实是已经被加载出来了,这个时候滚动条就出现了,你需要通过滚动条来浏览layout viewport其他的部分。
layout viewport用css像素来衡量尺寸,在缩放、调整浏览器窗口的时候不会改变。缩放、调整浏览器窗口改变的只是visual viewport。
在桌面浏览器中,缩放为100%的时候,Layout Viewport宽度等于内容窗口的宽度。(你几乎不会在电脑上见过横向滚动条,除非你调整缩放)
但是在移动端,缩放为100%的时候,Layout Viewport不一定等于内容窗口的大小。当你用手机浏览浏览宽大的网页(这些网页没有采用响应式设计)的时候,你只能一次浏览网页的一个部分,然后通过手指滑动浏览其他部分。这就说明整个网页(Layout Viewport)已经加载出来了,只不过你要一部分一部分地看。
不要以为所有元素的位置都会在layout viewport的内部,你将body的一个子元素添加out类:
.out {
position: absolute;
right: -30px;
background-color: red;
}
这个元素就会超出layout viewport 30px!
visual viewport
visual viewport就是显示在屏幕上的网页区域。通过前面的说明你应该已经知道visual viewport了:它往往只显示layout viewport的一部分。visual viewport就像一台摄像机,layout viewport就像一张纸,摄像机对准纸的哪个部分,你就能看见哪个部分。你可以改变摄像机的拍摄区域大小(调整浏览器窗口大小),也可以调整摄像机的距离(调整缩放比例),这些方法都可以改变visual viewport,但是layout viewport始终不变。
visual viewport用css像素来衡量尺寸,表示有多少个css像素能够被用户看到。
举个栗子:
为了让小小的手机屏幕也能够浏览宽大的网页(这些网页没有采用响应式设计),手机浏览器将layout viewport的默认宽度设为与电脑浏览器一样,比如980px,1024px(单位:CSS像素)。由于手机的屏幕逻辑像素宽度一般只有300~400逻辑像素,因此需要将多个css像素由1个逻辑像素显示(也就是缩小,千万不要忘记缩放比例=css像素边长/逻辑像素边长
),让手机屏幕显示的css像素与网页的css像素一样多了,也就是visual viewport = layout viewport。
但是这会引发一个问题:字体小得难以阅读。为了方便阅读我们又不得不用手指将缩放调整到100%左右(一个设备独立像素显示一个css像素,对于我的手机来说,水平方向只有360个设备独立像素),这个时候visual viewport 只显示layout viewport的一部分了。
我们做前端开发一定要为移动端浏览器进行适配,也就是进行响应式设计。响应式设计的第一步就是在html中加入:
<meta name="viewport" content="width=device-width, initial-scale=1.0">
这个标签只对移动端浏览器生效,它将layout viewport的横向css像素数量设为屏幕的横向dips(定值),然后将初始缩放的值设为1.0,也就是让css像素的大小=dips的大小。这样,数值、单位都想等了,网页宽度也就等于屏幕宽度了,也不会出现横向滚动条。
dips是设备独立像素的缩写。设备独立像素也叫逻辑像素。
可以看出,现在的字体大小合适了。网页的排版变化了,和电脑版的网页差别很明显。排版变化的原因是网站使用了媒体查询:在加载css的时候检测显示设备的属性,为不同的设备属性应用不同的样式。
相关属性
1. screen.width/height
上一篇文章说过的screen.width/height:整个屏幕的宽度和高度。这两个数值的单位是设备独立像素。这两个数值不随页面缩放、浏览器窗口大小而改变,在前端开发的过程中可以认为是固定不变的(除非你通过操作系统改变屏幕的分辨率)。这两个数值是操作系统决定的,由于设备独立像素比设备像素经常不等于1:1,实际屏幕物理像素的分辨率不一定是screen.width×screen.height。
在上图中列出了iphone各个手机的设备分辨率和IOS决定的分辨率(逻辑分辨率),我们只需要看这两行。
设备分辨率就是屏幕上的物理像素的数量,当手机厂商宣传自己的屏幕有多么清晰锐利的时候,相互攀比的就是这个数值。
逻辑分辨率就是screen.width/height。为什么iphone3GS以后的iphone都要把这个值设为实际屏幕分辨率的1/2或1/3呢?因为随着屏幕上塞进越来越多的真实像素,屏幕大小的变化却不那么明显,因此像素密度也越来越高。如果还让逻辑分辨率:真实屏幕分辨率=1:1,那么12px的字体就会越来越小,影响阅读体验。因此,后续的iphone用4个真实像素(甚至9个像素)组合成一个“逻辑像素”。这样,即使真实像素越来越小,每一个“逻辑像素”的大小变化不大。浏览器可以放心地使用逻辑像素来衡量大小,而不用担心真实大小在不同的显示器上出现严重偏差。
2. window.innerWidth/Height
visual viewport的大小,也就是浏览器内容窗口的大小,不包括菜单栏、地址栏、状态栏等,但是包括滚动条。单位是CSS像素。通过这个属性你可以知道,当前的浏览器窗口可以容纳多少个css像素。当用户放大的时候这个数值会减少(因为css像素变大了),当用户缩小的时候这个数值增大。缩放改变浏览器窗口都会改变这个属性的值。
Opera浏览器是例外,其单位是设备独立像素,也就是说如果你只调整缩放,这两个值不会改变。
与之对应的,window.outerWidth/outerHeight给出整个浏览器窗口的大小(包括各种栏),但是单位是设备独立像素。
3. document.documentElement.clientWidth/Height
Layout Viewport的尺寸(Layout Viewport是<html>元素的父容器),单位是CSS像素。
它与window.innerWidth/Height的唯一区别是,document.documentElement.clientWidth/Height不包含滚动条。
document.documentElement指的是html元素,通常Element.clientWidth应该给出元素的大小,但是document.documentElement.clientWidth/Height并不衡量html元素的大小,这是一个特例。
4. document.documentElement.offsetWidth/Height
<html>元素的尺寸,除非你在css中改变html的大小(很少有人这么做),否则html的宽度始终与Layout Viewport宽度相同。单位是CSS像素。
5. window.pageX/YOffset
滚动距离,描述用户已经向右、向下滚动了多少个像素,也可以理解为visual viewport相对于layout viewport的偏移值。单位是CSS像素。
当用户进行缩放的时候,浏览器会尽量保证:原先在内容区顶部的元素,在缩放以后依然在内容区顶部,看以下例子:
原本数字3在顶部,放大后3依然在顶部。window.pageYOffset大致相同。大致相同的原因是CSS像素数量不随着缩放而变化,原本在上方的内容高度有多少个CSS像素,放缩以后依然是多少个CSS像素。至于为什么不是完全相同,是因为"原先在内容区顶部的元素,在缩放以后依然在内容区顶部"这一机制无法完美地做到。
以下是测试用的代码,可以自己改动试试看(在电脑上使用chrome可以模拟不同的移动设备来调试,在开发者工具点击右上角的“toggle device toolbar”即可)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<!--<link rel="stylesheet" href="test.css" type="text/css" />-->
<title>test viewport</title>
<style>
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
.box {
width: 100%;
height: 200px;
background-color: greenyellow;
}
.out {
position: absolute;
right: -30px;
background-color: rosybrown;
}
</style>
</head>
<body>
<div class="box">box</div>
<div class="out">out</div>
</body>
</html>
谢谢阅读!后续我会更新更多有关响应式设计的内容!