viewports 剖析一

因为工作的原因,很少有机会接触移动 web 开发,一直都还挺遗憾的,偶尔写几个页面也都是 “按照套路” 出牌,最近终于有空了解一些概念性的东西,记录一下
文中的图片为了说明问题均为盗图,具体出处会在文末注明

本文大概将以下几个概念以做对比

设备的 pixels 和 CSS 的 pixels

**设备的pixels** 设备像素是我们直觉上觉得「靠谱」的像素。这些像素为你所使用的各种设备都提供了正规的分辨率 大多数情况下能从`screen.width/height` 取出具体值 当然了 设备的pixels对web开发人员几乎毫无用处 这里只需要知道它的概念即可 **CSS的pixels** 这些就是那些控制你的样式表如何被渲染的像素

现代浏览器上的缩放是基于伸展 pixels
所以 html 元素上的宽度不会因为你缩放了 200% 而变成了两倍宽,它在形式上还是一倍宽 只不过占用了两倍的设备 pixels
如下图 1-1 有 4 个 1pixels,缩放为 100% 的 html 元素 此时 css pixels 和设备的 pixels 完全重合

此时我们如果缩小浏览器 css 的 pixels 开始收缩,导致 1 单位的设备 pixels 上重叠了多个 css 的 pixels 如下图 1-2

如果放大浏览器 css 的 pixels 就会放大 导致 1 单位的 css pixels 上重叠了多个设备 pixels 如图 1-3

总而言之 你只需要关注 CSS 的 pixels 这些 pixels 将指定你的样式被如何渲染
就像上面所说的 设备的 pixels 对开发人员无用 但是对用户有用,因为用户回手动缩放页面,这些开发人员不用关注 浏览器会自动保证你的 css pixels 会被伸展还是被收缩

所谓的 100% 缩放

100% 缩放的情况下 1 单位的的 CSS pixels 严格等于 1 单位的设备 pixels

屏幕尺寸和浏览器尺寸

**屏幕尺寸(Screen size)** 含义:用户的屏幕的完整大小 度量:设备的pixels 不会因为缩放而改变 是显示器的特征 对我们来说没用 获取方式 如下图1-4 ![](/images/viewports/4.jpg)

浏览器尺寸(Window size)
含义:包含滚动条尺寸的浏览器完整尺寸
度量:CSS pixels
浏览器内部尺寸,它定义了当前用户有多大区域。可供你的 css 布局占用
如下图 1-5

页面的滚动移位

含义:页面的移位 度量:CSS pixels 定义了页面(document)的相对于窗口远点的位移,可以利用这个特性获取用户滚动了多少的滚动条距离 如下图1-6 ![](/images/viewports/6.jpg)

viewport 以及度量 viewport

**viewport** 啊啊啊 终于提到viewport了 鸡冻 划重点 `viewport`是控制``元素的容器 是``的爹

你发现了么?
百分比布局时 你定义的一个侧边栏宽度为 10% 当你改变大小时 它的宽度会自动扩张和收缩 原理是啥
当然了 它的宽度是依赖父元素 假如它父元素就是 <body> 那么 <body> 多宽?
向上类推 <body> 的宽度取决于它的父元素 <html>
呃.. 废话好多 <html> 宽度取决于它的父元素
<html> 恰好等于浏览器的宽度 所以你的 10% 会占用浏览器宽度的 10% 我们都是这么用的 今天深扒发现
<html> 宽度受 viewport 限制 ,等于 viewport 宽度的 100%
也就是说
viewport 严格等于浏览器窗口
需要注意的是:viewport 不是一个 html 的概念 所以不能通过 CSS 修改它

真实页面宽度概念
如果你放大页面几倍 如何标识页面宽度(此时已经有横向滚动条了,也就是说页面的内容溢出了 <html> 元素)
使用 document width
如图 1-7

如图 1-8

度量 viewport
含义:viewport 尺寸
度量:CSS pixels
如下图 1-9

document.documentElement 代表 HTML 文档根元素<html>
来 先看张图

这张图是在为 <html> 元素赋值 25% 但是 document.documentElement.clientWidth 值仍然不变
说明 document. documentElement. clientWidth/Height 只会给出 viewport 的尺寸,而不管元素尺寸如何改变
那么问题来了
我是不是也可以用 window.innerWidth 来定义 viewport
呃.
他与 document.documentElement.clientWidth 有一点细微的差别
前者不包含滚动条

html 元素以及度量 html

**html** ta爹(`viewport`)如果`document.documentElement.clientWidth`表示那么``这样获取 `document.documentElement.offsetWidth` ![](/images/viewports/11.jpg) 如果给``元素赋值了宽度 那么`offsetWidth`就会真实的反映出来

关于事件坐标

**pageX/Y, clientX/Y, screenX/Y**
  • pageX/Y:从原点到事件触发点的 CSS 的 pixels
  • clientX/Y:从 viewport 原点(浏览器窗口)到事件触发点的 CSS 的 pixels
  • screenX/Y:从用户显示器窗口原点到事件触发点的设备 的 pixels。
    上图


媒体查询 width/height 与 device-width/height

* `device-width/height`使用`screen.width/height`来做为的判定值。该值以设备的pixels来度量 * `width/height`使用`documentElement.clientWidth/Height`即viewport的值。该值以CSS的pixels来度量 ![](/images/viewports/16.jpg) 桌面浏览器上使用width

mobile 浏览器

移动设备的屏幕宽度比桌面浏览器小(好多废话.) 试想一下 如果我们只是copy桌面的样式到移动设备 该有多丑 如下图 移动设备浏览器在初始默认打开以最小缩放模式打开网站。(即在手机屏幕上展示完整宽度的页面)

假设当前设备的宽度是 400px 还是之前说过的 10% 侧边栏,如果移动设备上做同样的处理,会显示 40px 的宽 太窄了,布局会变得非常可怕
那么 如何处理?

两种 viewport

因为viewport太窄,最显然的解决方式就是将它变宽 由此 引出了 虚拟视口 (`viewportvisualviewport`) 与 布局视口(`viewportlayoutviewport`) 关于它们 有一个很好的解释
1
2
3
4
5
6
  想象一下viewportlayoutviewport是一张大的不能改变大小和角度的图片 现在你有个更小的框来观看这张大图片
这个框被不透明的材料包围 因而你只能看到大图片的一部分 你通过这个框子看到的大图片的那部分就叫做虚拟视口(viewportvisualviewport)

你拿着这个框拿着站的离大图原点(用户的缩小页面功能)以一次性看到这个大图片
你站的离的近一点(用户的放大页面功能)以看到一部分
你能改变这个框框的远近 但是这张大图片的大小和形状都不会改变

visualviewport 是当前显示在屏幕上的部分页面。用户会滚动页面来改变可见部分,或者缩放浏览器来改变 visualviewport 的尺寸。
如下图

但是 CSS 布局 通常是按照 layoutviewport 定义的,这要比 visualviewport 宽很多

元素的宽度继承于layoutviewport

缩放 Zooming

两种viewports都以CSS的 pixels来度量。当你通过缩放改变visualviewport时,layoutviewport保存不变。

屏幕尺寸

**理解layout viewport** 许多移动设备浏览器在初始默认打开以最小缩放模式打开网站(也就是在手机屏幕上展示完整宽度的页面)


此时浏览器已经选择好他们的 layoutviewport 的尺寸 它完整覆盖了最小缩放模式下的移动浏览器的屏幕,这个时候 layoutviewport 的宽度高度和最小缩放模式下能在页面上显示的内容的宽度和高度一致。


那么移动端如何计算 layoutviewport 的尺寸?
document. documentElement. clientWidth/Height


理解 visual viewport
那么移动端如何计算 visualviewport 的尺寸?
window.innerWidth/Height 随着用户的缩放浏览器 值会改变 更多 更少的 CSS pixels 放进了屏幕

屏幕尺寸 screen
和 pc 浏览器一样 screen.width/height 标示了设置屏幕的尺寸 以设备的 pixels 显然 这跟开发人员没有什么关系

页面的滚动移位

你同样需要知道当前的虚拟视口相对于布局视口的距离 这叫做`滚动位移` ,它像在pc端获取一样 使用**window.pageX/YOffset**

html 元素以及度量 html

html元素的整体尺寸,和pc端一致 使用`document.documentElement.offsetWidth/Height`,元素以CSS pixels度量

关于事件坐标

同pc浏览器 只需要关注 pageX/Y

媒体查询 width/height 与 device-width/height

也如同pc浏览器 `width/height `使用css的pixels度量layoutviewport 即`document. documentElement. clientWidth/Height ` `device-width/height `使用设备的pixels 即 `screen.width/height. ` 所有浏览器都遵循这个原理

viewport 的 meta 标签

1
<meta name="viewport" content="width=320">
最初这是apple的一个html扩展标签,被很多浏览器复用 设置 `虚拟视口`的宽度

假设你现在创建一个页面 并不为它设置宽度 那么它会伸展开来占据 100% 的 viewlayout 的宽度 绝大多数浏览器缩小这个页面在一屏的宽度上显示这个 layoutviewport


当用户放大页面 绝大多数会保存元素的宽度(保持元素的定位不变)而导致文字超出屏幕

当你设置

1
<meta name="viewport" content="width=320">

你网站的 layoutviewport 变成了 320px。页面的初始状态就很正确了