两个 viewports 的故事-第二部分
原文:A tale of two viewports — part two
译者:nzbin
在这个迷你系列中,我将解释 viewports 和各种重要元素的宽度是如何工作的,比如说 <html>
元素,窗口以及屏幕。
我们将在本页讨论移动浏览器。如果你是刚刚接触移动端,我建议你首先阅读关于桌面浏览器的第一部分。这会让你在熟悉的环境中循序渐进。
移动浏览器的问题
手机浏览器与桌面浏览器的最大不同就是屏幕尺寸。对于一个基于桌面优化的网站,移动浏览器的显示效果明显不如桌面浏览器,要么缩小后文字太小无法阅读,要么放大后只能看到网站的一部分。
手机屏幕远小于桌面屏幕,最大宽度也就 400px 甚至更小(有些手机据称有更大的宽度,那是骗人的,或者至少给我们的是无用的信息)。
平板设备如 ipad 以及传闻基于 webOs 的惠普产品将缩小桌面与手机的差距,但也无法改变最基本的问题。因为网站也需要在移动端显示,所以我们必须让它们在小屏幕上正常显示。
最重要的问题与 CSS 有关,尤其视图的尺寸。如果我们一比一的复制桌面模型,CSS 可能不会正常工作。
将侧边栏设置为 width: 10%
。如果移动浏览器和桌面浏览器的工作原理相同,侧边栏至多显示 40px 宽,确实太窄了。你的自适应布局看上去被压扁了。
解决这个问题的方法之一就是为移动浏览器设计特殊的网站。除了你是否应该这样做的问题之外,实际的问题是只有很少的网络公司会为移动单独设计网站。
手机浏览器的供应商希望为客户提供最好的用户体验,这意味着“尽可能如桌面显示的一样”。因此必须使用一些小花招。
两个viewports
所以视图太窄而不能作为你 CSS 布局的基础。很明显解决方式就是让视图更宽一点,我们要将视图分为两部分:视觉视图和布局视图。
George Cummins 在 Stack Overflow 上很好的解释了视图的基本概念,“把布局视图想象成一张无法改变大小和形状的很大的图片,你可以通过一个很小的相框来看这张图片。这个小相框的周围是不透明的材料,你只能看到相框内的图片。你从相框内看到的图片就是视觉视图。你可以拿着你的相框远离图片来看整张图片(缩小),或者离近一点只看图片的一部分(放大)。你也可以改变相框的角度,但是图片(视觉视图)的大小和尺寸不会变。”
视觉视图是页面的一部分,如下所示。用户可以通过滚动来查看页面,或者通过缩放改变视觉视口的大小。
CSS 的布局是根据布局视图计算的,所以比视觉视图更宽。
由于 <html>
元素首先获取布局视图的尺寸,所以 CSS 被编译后页面就会比手机屏幕宽。这使得你的网站和在桌面浏览器中显示的一样。
布局视图有多宽呢?不同的浏览器各有差异。Safari/980px,Opera/850px,Android Webkit/800px,IE/974px。
还有一些浏览器比较特殊:
- Symbian WebKit 会保持 layout viewport 与 visual viewport 相等,是的,这意味着拥有百分比宽度元素的行为可能会比较奇怪。但是,如果页面由于设置了绝对宽度而不能放入 visual viewport 中,那么浏览器会把layout viewport 拉伸到最大 850px 宽。
- Samsung WebKit (on bada)使 layout viewport 和最宽的元素一样宽。
- 在 BlackBerry 上,layout viewport 在 100% 缩放比例的情况下等于 visual viewport。这不会变。
缩放
很明显,两种视图都是用 CSS 像素测算。当视觉视图通过缩放改变时(如果是放大,屏幕上的 CSS 像素会变少),布局视图的尺寸不会变。(如果变化了,你的页面会用百分比的宽度被重新计算)
理解布局视图
为了理解布局视图的尺寸,我们应该看一下页面被完全缩小后发生了什么。大多数手机浏览器默认以完全缩小模式显示页面。
关键的一点是:布局视图在缩小模式下能够完全显示在屏幕上。(此时视觉视图等于布局视图)
如下图,布局视图的宽高与完全缩小模式下的宽高是相同的。当用户放大后,这些尺寸仍然相同。
布局视图的宽度始终相同。如果你旋转手机,视觉视图会发生改变,但是浏览器会放大布局视图来适应新的方向,所以布局视图和视觉视图的宽度仍然相等。
这会对布局视图的高度产生影响,纵向模式下布局视图的高度小于实际高度。但是网页开发者不关心高度,只关心宽度。
测算布局视图
现在我们想要测算两个视图的尺寸。由于浏览器之间的竞争我们有幸获得了一对属性值。
document.documentElement.clientWidth
和 -Height
包含了布局视图的尺寸。
旋转方向会影响高度,但不会影响宽度。
测算视觉视图
视觉视图通过 window.innerWidth/Height
测算。很明显,当用户放大或缩小时,由于更多或更少的 CSS 像素会适配屏幕,视觉视图尺寸会发生变化。
不幸的是这种方法并不兼容。很多浏览器仍然需要增加对视觉视图尺寸的支持。还没有浏览器具有其他保存该尺寸的属性值。所以我猜想 window.innerWidth/Height
是一个标准属性,尽管支持性不太好。
屏幕
和在桌面上一样, screen.width/height
能够得到屏幕的尺寸(设备像素)。作为开发者你可能不需要这些信息。你对屏幕的物理尺寸不感兴趣,而只关心当前屏幕上有多少 CSS 像素。
缩放比例
你无法直接获得缩放比例,但是可以通过 screen.widt
和 window.innerWidth
的值求出来。当然只有两种属性都被支持时才有效。
幸运的是,缩放比例并不重要。你需要知道的是当前屏幕上有多少 CSS 像素,你可以通过 window.innerWidth
获得(如果该属性被支持的话)。
滚动偏移
你同样需要知道的是当前视觉视图相对于布局视图的位置。这就是滚动偏移,和桌面端一样,这个属性保存在 window.pageX/YOffset
中。
<html>
元素
和在桌面上一样, document.documentElement.offsetWidth/Height
可以获得 <html>
元素在 CSS 像素中的尺寸。
媒体查询
媒体查询的工作方式和在桌面端相同。 width/height
将布局视图作为参照,使用 CSS 像素计算。而 device-width/height
将设备屏幕作为参照,使用物理像素计算。
换句话说, width/height
表示 document. documentElement. clientWidth/Height
的值,而 device-width/height
表示 screen.width/height
的值。所有浏览器都是如此,即使它们表示的值是错误的。
哪一种测算对web开发者更有用?我不知道。
我开始认为 device-width
是最重要的,因为它可以提供我们可能用到的设备信息。举例来说,你需要不同宽度的布局视图适应设备宽度。可是,你也可以使用 <meta viewport>
,没有必要使用 device-width
的媒体查询。
那么 width
是更重要的媒体查询吗?也许是,有线索表示浏览器厂商认为这一数值对于设备上的网站是友好的宽度。但仍然有些含糊不清
的媒体查询没有提供更多的其他信息。width
因此我仍然不确定。目前我认为媒体查询对于区分桌面、平板或手机很重要,但是对于区分不同的平板或手机用处不大。
事件坐标
事件坐标和在桌面端多少有些差异。不幸的是,12 个测试浏览器中只有两个(Symbian WebKit 和 Iris)获得的三个属性的值完全正确。其他的浏览器或多或少的有些问题。
pageX/Y
获取的仍是相对于页面的 CSS 像素。这也是目前为止最有用的属性对,和桌面端一样。
clientX/Y
是相对于视觉视图的 CSS 像素。这是有意义的,虽然我不确定到底有什么好处。
screenX/Y
是相对于屏幕的设备像素。当然,它与 clientX/Y
的参照是相同的,而设备像素没什么用。所以我们不用关心 screenX/Y
,它在桌面端同样没什么用。
Meta viewport
最后,让我们讨论一下 <meta name="viewport" content="width=320">
。这个标签最初是苹果的扩展,之后被更多浏览器效仿。它其实就是调整布局视图的大小。为了理解它的作用,让我们退一步来讲。
假设你创建了一个简单页面,并且其中的元素没有设置 width
。它们会被拉伸到布局视图的 100% 宽度。大多数浏览器会通过缩小来在屏幕上显示整个布局视图,如下图的效果
所有的用户会立即放大查看,但是大多数浏览器会保持元素的宽度不变,这使得文本很难阅读。
现在你可能会设置 html {width: 320px}
。<html>
元素收缩了,其他元素的宽度是 320px 的 100%。当用户放大的时候会看出来,而不是最初用户可能面对包含空白的缩小的页面。
为了解决这个问题,苹果公司发明了meta viewport 标签。当你设置 <meta name="viewport" content="width=320">
,布局视图就是 320px。现在页面的初始状态已经正确。
你可以设置你想要的布局视图的宽度,包括 device-width
。最后一个将 screen.width
作为参照来缩小布局视图。
这里有一个隐藏的问题。有时因为像素数太高, screen.width
并没有太大意义。举例来说,Nexus One 实际宽度为 480px,但是谷歌的工程师认为使用 device-width
时布局视图 480px 的宽度太大。它们将其降低到 2/3,所以 device-width
会得到 320px 的宽度,这和 iPhone 是一样的。
据说新 iPhone 会有更大的像素数(不一定需要更大的屏幕)。我可能不会对苹果效仿谷歌的行为感到吃惊。也许以后 device-width
就意味着 320px。
感谢您的阅读,如果您对我的文章感兴趣,可以关注我的博客,我是叙帝利,下篇文章再见!
开发低代码平台的必备拖拽库 https://github.com/ng-dnd/ng-dnd
低代码平台必备轻量级 GUI 库 https://github.com/acrodata/gui
适用于 Angular 的 CodeMirror 6 组件 https://github.com/acrodata/code-editor
基于 Angular Material 的中后台管理框架 https://github.com/ng-matero/ng-matero
Angular Material Extensions 扩展组件库 https://github.com/ng-matero/extensions
Unslider 轮播图插件纯 JS 实现 https://github.com/nzbin/unsliderjs
仿 Windows 照片查看器插件 https://github.com/nzbin/photoviewer
仿 Windows 照片查看器插件 jQuery 版 https://github.com/nzbin/magnify
完美替代 jQuery 的模块化 DOM 库 https://github.com/nzbin/domq
简化类名的轻量级 CSS 框架 https://github.com/nzbin/snack
与任意 UI 框架搭配使用的通用辅助类 https://github.com/nzbin/snack-helper
单元素纯 CSS 加载动画 https://github.com/nzbin/three-dots
有趣的 jQuery 卡片抽奖插件 https://github.com/nzbin/CardShow
悬疑科幻电影推荐 https://github.com/nzbin/movie-gallery
锻炼记忆力的小程序 https://github.com/nzbin/memory-stake