前一篇介绍了桌面浏览器的 viewport 的行为。本篇是来讨论移动端浏览器的行为的。此翻译来自这里

移动端浏览器的问题

当我们比较两种浏览器的不同的时候,最为显著的区别就是屏幕的大小。在一个桌面浏览器为主的网站中,移动浏览器所显示的内容要比桌面浏览器明显少很多,除非要么缩小字体到难以阅读的地步或者为了在手机屏幕上显示而只显示网站的一小部分。

一个移动端屏幕比桌面端的屏幕要小上很多,设想一下一个最大为 400px 有时候甚至要更小的移动端屏幕。(某些手机可能会检测出更大的屏幕宽度不过其实这是设备在撒谎-至少他们提供是对我们无用的信息)。

处于中间层的平板设备比如 iPad 或者传闻中的基于 HP webOS 的设备会抹平移动端和桌面端之间的差异,然而还是无法解决根本的问题。网站必须在移动端上运行良好,所以我们不得不让其在小屏幕上适配显示得好些。

主要的问题在于 CSS 样式表,特别是视口的分辨率。如果我们照抄桌面端的话,移动端的样式将会是灾难性地失败。

让我们回到设置侧边栏宽度为 10% 上。如果移动端如桌面端的行为那样,会给侧边栏最多 40px 的宽度而这显然太拥挤了。你的流式布局看上去将会压得非常扁平。

解决问题的方法之一是单独为移动端构建一个网站。即使抛开是否应该那样做的最根本的问题,实际的问题是只有极少数的建站者能够胸有成竹地构建适配移动端设备。

移动浏览器产商想要为客户提供最好的可能的体验即尽量向桌面端靠拢。一些技巧是必要的。

两种视口视觉

视口太小无法作为你的 CSS 布局的基础。最容易想到的办法就是让视口更宽。即,然而需要划分为两部分:visual viewport(视觉的视口)和 layout viewport(布局视口)。

George Cummins 在 Stack Overflow 这里 解释道:

把布局视口想象为一张不会改变大小和形状的图片。想象一下你有一个比图片小的相框,透过它你观看到整个大图像。小相框是由不透明的材料所包裹着这样就会遮挡你的视线除了大图片中的一小部分。你能透过相框看见的图像的一部分即为视觉的视口。当你握住相框的时候你可以往后退(缩小)以便看到整张图片,或者你可以走近(放大)只看图片的一小部分。你也可以转动相框的角度,然而这张大图的大小和形状(布局视口)从来不会改变。

也可以看 chris 这里 的评论:

视觉视口是此时在屏幕上显示的页面的一部分。用户可以滚动来浏览网页或者缩放来改变视觉口的大小。

然而,在 CSS 布局中,特别是百分比宽度,是相对于布局视口来计算宽度的,布局视口往往比视觉视口要宽得多。

所以 <html> 元素初始化会是布局视口的宽度,并且你的样式会被渲染得好似屏幕会明显比手机屏幕要宽大得多。这样会确保你网站的布局和桌面端的保持一致。

布局视口的宽度有大呢?每个浏览器显示不一样,iPhone Safari 浏览器使用 980px,Opera 850px,Android WebKit 800px,IE 是 974px。

一些浏览器有些非常奇怪的行为:

  • 塞班 Webkit 试图让布局视口和视觉视口保持一致,这会导致百分比的元素看起来非常古怪。然而如果页面没有适配进视觉视口的话是由于浏览器的把布局视口的绝对宽度拉伸到了最大宽度 850px。
  • 三星 WebKit(bada)以最宽的元素作为布局视口的最大宽度。
  • 黑霉手机上布局视口在 100% 缩放的时候和视觉视口保持一致。

缩放

两种视口都用像素表示。但是当视觉视口分辨率随着用户缩放而改变的时候(如果你放大,屏幕上会显示更少像素的样式),布局视口分辨率保持不变。(如果不是那样的话你的页面会一直不断地重绘就像重新计算百分比宽度一样)。

理解布局视口

为了理解布局视口的大小我们不得不来理解一下当页面完全放大的时候会发生什么。许多移动浏览器初始化完全缩小的模式来显示页面。

原理是这样的:浏览器选择让他们的布局视口的分辨率以完全缩小的模式来完全覆盖屏幕(即等于视觉视口)。

这样一来布局视口的宽度和高度将在最大化缩放模式下等于屏幕上而不管屏幕上显示的任何元素。当用户放大分辨率保持不变。

布局视口是保持不变的。如果你放置你的苹果手机,视觉视口会改变,但是浏览器是以轻微地放大来适应这个新方向,所以布局视口再一次和视觉视口保持一致。

这会导致布局视口的高度受到影响,大体上会比纵向模式要小。但是开发者并不开心高度而只关心宽度。

布局视口的分辨率

现在要测量这两个视口的分辨率。多亏了浏览器大战给予我们的两个属性对。

document.documentElement.clientWidthHeight 包含了视口的分辨率。

当更改方向的话只是高度改变了,宽度不变。

视觉视口的分辨率

视觉视口的分辨率是由 window.innerWidth/Height 得出。很明显地会随着用户放大或缩小而改变,这样屏幕上显示的更多或者更少的像素的内容。如果看过上一篇文章这个就当就是相当于桌面浏览器的 html 元素。

很不幸的是存在大量的不兼容性。这么多浏览器仍然不得不为视觉视口的测量添加支持。然后,没有任何浏览器在其它属性对中应用这种测量方法,所以我猜 window.innerWidth/Height 是一个标准的,虽然没有被很好地支持。

屏幕

在桌面浏览器中,screen.width/height 以设备像素为单位输出屏幕大小。在桌面端,作为开发进行你从来不关心这个信息。你只是关心在其上有多少像素的样式而不是屏幕的物理大小。

缩放的比例

直接读取缩放的比例是不可能的,但你可以通过 window.innerWidth / screen.width 来获得。当然这必须是两个属性都被支持的情况下。

幸运的是缩放比例并不重要。你只需要关心屏幕上有多少CSS像素即可。如果浏览器支持用 window.innerWidth 来获得。

滚动位移

你也需要知道视觉视口相对于布局视口的坐标即滚动位移,在桌面端是存储在window.pageX/pageY中。

html 元素

在桌面端,document.documentElement.offsetWidth/Height 获得 html 元素的 CSS 像素的总大小。

媒体查询

媒体查询和桌面端保持一致。width/height使用布局视口作为参考并且是以样式像素为单位,device-width/height 使用设备屏幕并且以设备像素来测算。

换句话说,width/height 表示 document.documentElement.clientWidth/Height 即布局视口的值,然而 device-width/height 表示 screen.width/height。(在所有浏览器均有效,即使表示的值不正确)。

那么对于开发者来说哪种计算方式更加有用呢?答案是,不知道。

起先我认为 device-width 会是更加重要的,因为它提供了设备更多的信息以便我们使用。比如,你会调整你的布局的宽度来适配设备的宽度。但是其实你可以就不用 <meta view-port> 标签,并不一定要在媒体查询中使用 device-width

那么最终 width 会是更加重要的媒体查询吗?可能,它为浏览器产商提供了其认为的在这个设备上一个网站的合适的宽度的线索。但是是相当模糊的是,width 属性实际上并没有提供其它信息了。

所以我没下定论。我只是觉得媒体查询用来区分你是在桌面,平板或者移动端相当有用,但是在区分各种各样的平板和移动设备上却并不是那么有用。

事件坐标

事件坐标在桌面端基本上是有效的。然而不幸的是,12 个被测试的浏览器中只有两个浏览器,塞班 WebKit 和 Iris, 得出了三个完全准确的属性值。其它浏览器都或多或少的严重问题。

pageX/Y 还是相对于页面的位移的样式像素,和桌面端一样这是三个中最为有用的一个属性对。

clientX/Y 是相对于视觉视口的样式像素值。这有用,虽然我不完全确定这有何好处。

screenX/Y 是相对于设备的设备像素值。当然了这个和 clientX/y 的值是一样的,所以这个属性没啥用。所以就像桌面端的一样,不必关心这个值。

Meta viewport

最后来讨论元素 <meta name="viewport" content="width=320">; 之前是苹果的一个扩展然后被许多其它产商拷贝了。这是为了改变布局视口的大小的,为了理解它为什么是属性的,可以先后退一步看下。

假设你在写一个简单页面并且没有为元素设置宽度。现在它们会扩张来占满布局视口 100% 的宽度。大多数浏览器会缩小以便在屏幕上显示整个的布局视口。效果如下:

所有的用户会立即放大,这是可行的,但是大多数浏览器会保持元素的宽度不变,让文字难以阅读。

(安卓的 WebKit是个意外,它会减小包含文字的元素的大小以适配屏幕。这是明智的,我觉得其它浏览器产商应该复制这一行为。

现在你可以设置 html {width: 320px}。现在 <html> 元素收缩,其它元素现在也适配于是 320px 的宽度。但是当用户面对一个缩小到基本没有内容的时候并不是一开始时就会产生效果除非用户放大的时候这才会有用。

为了解决这个问题苹果发明了视口元标签。当你设置<meta name="viewport" content="width=320">你设置布局视口的宽度为 320px。现在页面的初始状态是正确的。

你可以设置布局视口为任意的分辨率,包括device-widthdevice-width引用的是screen.width并且相应地调整布局视口 的大小。

有一个特殊情况。有时候准确的screen.width并没多大用处因为像素值太高了。举个例子,谷歌的 Nexus One 准确宽度是 480px,但是谷歌工程师认为当使用 device-width 赋值一个 480px 宽的布局视口显得太大了。所以他们决定缩小到之前的 2/3 大小,所以 device-width 变成 320px,和 iPhone一样。

据说,新苹果会拥有更高的像素值(并不一定是要更大的屏幕)。苹果复制这一行为我并不感到惊讶。或许最后 device-width 还是 320px呢。

最后还有一些相关的研究文章,在这就不一一写出了。

总结

那么,这里个人的感觉就是一直不是明白这个移动端的渲染策略,现在明白了个大概,当没有设置 <meta name="viewport"> 的时候会自动完全缩小以适配到视觉视口的大小。然后最形象的还是开头的那个相框里面的画,当你拿得越远的时候就会看到整幅画,当你离得越近的时候就只会看清某一部分。越远即缩小,越近即放大。视觉视口大小是会改变的,而布局视口是不变的。

和桌面端对比这里的布局视口就是桌面端的视口,而视觉视口即 html 元素