前几天在研究移动版开发时,意外查阅到了PPK大神的关于viewport(视口)的三篇研究文档,但其中一篇无奈全是英文,只好硬着头皮看下去,并顺便试着翻译一下,以备日后回顾。
本人水平有限,如翻译错误,欢迎指正
原文地址:http://www.quirksmode.org/mobile/metaviewport/#t10
另外两篇文档的地址:
原文:http://www.quirksmode.org/mobile/viewports.html
译文:http://weizhifeng.net/viewports.html
原文:http://www.quirksmode.org/mobile/viewports2.html
译文:http://weizhifeng.net/viewports2.html
读前须知:PPK写这篇文章的时候,IPhone还没有生产出4S之后的产品。所以,这篇文章中提到的IPhone,都是指IPhone4S及之前的手机。
序言
本文涵盖了我关于meta标签中的viewport属性的所有规则(directives)的研究
@viewport的概念不在本文讨论范围之内,我将在研究清楚它的工作原理后再作说明
视口元信息标签
viewport元信息标签向浏览器指明了viewport大小和页面缩放比例的信息。而且,它允许web开发人员设置layout viewport的宽度,这个宽度可以是相对于CSS声明(如width:20%)的计算结果。
viewport元信息标签的语法如下:
<meta name="viewport" content="name=value,name=value">
规则
每一个名/值对就是一个规则(这个说法是我创建的),共有6个规则:
width:用于设置layout viewport(布局视口)的宽度
initial-scale:设置页面的初始缩放比和 layout viewport(布局视口)的宽度
minimum-scale:设置页面最小缩放比例,即用户能将页面缩小多少
maximum-scale:设置页面最大缩放比例,即用户能将页面放大多少
height:应该是用于设置layout viewport(布局视口)的高度,但该属性无效
user-scalable:当设置为no时,可禁止用户缩放页面。但请不要这样去做。
device-width值
width规则下有一个特殊的值device-width,通过设置width=device-width可以将layout viewport(布局视口)的宽度等于 ideal viewport(理想视口)的宽。
理论上,有一个类似的device-height的值,但它不起作用。
三种viewport
几年前,我在一篇文章中说过“手机浏览器有2个viewport:visual viewport(可视视口)和layout viewport(布局视口)”。如果大家觉得有必要,请再阅读一下这篇文档:
以下内容将在2个viewport知识的基础上继续讨论。
理想视口 ideal viewport
事实证明,还有第三个viewport,我称之为理想视口( ideal viewport)。它给出了移动设备上一个网页的理想大小。因此,理想视口( ideal viewport)的尺寸在每一个设备上都是不同的。
点击PPK的常见设备的理想视口和最小布局视口一览表可以查看一些设备的理想视口的尺寸
在一些老旧或者低端的非Retina屏的移动设备上,理想视口( ideal viewport)的大小等于其物理像素,但这并非一定正确。拥有较高的物理像素密度的一些新设备也可能保留了老版本手机的理想视口( ideal viewport)大小,因为这个尺寸很适合这些设备。
包括4S在内的早期IPhone手机,不论它们是否有Retina屏,它们的理想视口( ideal viewport)都是320x480。这是因为320x480是这些手机设备上网页的理想尺寸。
关于理想视口( ideal viewport),有如下2个重要的说明:
1、使用 width=device-width 和initial-scale=1,可以将 layout viewport的值设置为 ideal viewport的大小。
2、所有的缩放规则都是相对于 ideal viewport而言的,而与layout viewport多宽无关。因此maximum-scale=3的含义就是页面最多放大到 ideal viewport的3倍。
获取 ideal viewport 的尺寸
如果幸运的话,偶尔读出来的 ideal viewport的尺寸是有用的。
在一个页面上使用下面的的meta标签:
<meta name="viewport" content="width=device-width,initial-scale=1">
读取出document.documentElement.clientWidth和 document.documentElement.clientHeight
的值就是ideal viewport的尺寸。
如果没法读出 ideal viewport的尺寸不是你的选项(即必须要读出 ideal viewport的尺寸),通过screen.width/height的值来获取ideal viewport的尺寸也可以,但这种方式只有在黑莓(BlackBerry)设备上才有效,在其他浏览器上都是些无用值。
Open question:通过screen.width/height应该获取到ideal viewport的尺寸吗?
赞成:这个属性最终应该包含ideal viewport尺寸的有用信息
反对:ideal viewport的大小并不一定等同于设备的物理像素
ideal viewport的兼容性
现在用表格表示一下上述内容的兼容性
上表中的英语内容的译文如下:
Test | Devices |
---|---|
ideal viewport 尺寸 | 这些值没有对错,它们都由设备本身决定,这些值都是正常的 |
是否通过设置width = device-width,layout viewport的宽等于ideal viewport | Safari上横屏模式和竖屏模式的宽都是竖屏模式的宽(portrait width)(即320 or 768) |
是否通过设置initial-scale=1,layout viewport的宽等于ideal viewport | IE10上横屏模式和竖屏模式的宽都是竖屏模式的宽(portrait width)(即320) |
是否通过screen.width/height可以获取理想视口的尺寸 | Safari总是给出竖屏的理想视口的尺寸; Chrome和Opera给出的是有效高度(屏幕高度减去工具栏等),但宽度是正确的; Android和IE给出的是屏幕的物理像素; |
Layout viewport 的宽度
在渲染网页之前,浏览器得知道layout viewport的宽度。这个视口的宽度是根据CSS声明的宽度(例如width:20%)计算得出的。
当浏览器没有获得更详细的设置信息时,它们会自主决定layout viewport的宽度。在测试的8个浏览器中的6个上,这个值是980px,在BlackBerry 和IE10上是1024px。取哪个值由浏览器厂商决定,没什么对错之说。
我们已经知道,当在viewport元信息标签中设置width=400或者其他数值时,layout viewport的宽度就等于这个数值。
但是,在Android浏览器的webkit内核和IE上,layout视口的最小值是320px。当这个视口小于320px时,它们会恢复到ideal viewport的宽度。
因此,当layout viewport的尺寸等于ideal viewport时,会出现一种很复杂的情况(这种情况发生在当设置width=device-width 或者initial-scale=1时):在Safari和IE10上会有一个bug,因为它们在识别initial-scale上有兼容问题(但这只是一般规律)。
最大和最小尺寸
layout viewport的最大宽度是10,000px,我不完全相信这个数值,因为浏览器不允许页面缩放到这个大。但尽管不相信,目前我接受这个官方数值。
layout viewport的最小宽度大约是ideal viewport的十分之一,这也是最大的缩放程度了。layout viewport永不会变得比最小的visual viewport更小。但有一个例外情况:Android WebKit内核和IE浏览器的layout viewport永不会小于320px。
layout viewport的兼容性
现在用表格表示一下上述内容的兼容性
上表中的英语内容的译文如下:
Test | Devices |
---|---|
默认layout viewport的大小 | 这些值没有对错,它们都由设备本身决定,它们都是正常的 |
通过设置width=10000,minimum-scale=0.01来获取layout viewport的最大宽度 | 当数值超过10000时,所有浏览器都保持为10000,除了android webkit内核浏览,它会恢复到默认的980px |
通过设置width=5来获取layout viewport的最小宽度 | 下面声明的320px宽度是Android WebKit内核和IE浏览器采用的ideal viewport尺寸 |
通过设置一些正常的值: width = 200 width=300 width=400 width=1200 |
浏览器会尽量使layout viewport的宽度和width属性定义的值相等 a、当设置了width=200,在IPad的横屏模式会是205px宽,这与它最小layout viewport宽是一致的 b、当小于320px时,Android WebKit内核和IE浏览器将会将ideal viewport的宽当作layout viewport的最小宽度 |
缩放
缩放很麻烦,尽管在理论上它听起来很简单:决定能放大或缩小的缩放因子。
1、无法直接获取缩放因子。事实上,我们得先获得visual viewport的宽,它与缩放因子呈反比例关系。缩放因子越大,visual viewport的宽度越小。所以最小缩放因子决定了最大视觉视口的宽度,反之亦然。
2、事实证明,所有的缩放因子都与ideal viewport存在关系,而不论当时layout viewport的大小是多少。
还有一个关于名称的问题。在苹果系统上,zoom称为scale,所以在viewport meta标签中,规则就写成:initial-scale,minimum-scale和maximum-scale。其他浏览器*遵守这条规则,以兼容特定于IPhone端的网页。
使用这三个规则规定了一个缩放因子。例如缩放因子为2表示缩放到ideal viewport宽的200%。
公式
先给出公式:
visual viewport width = ideal viewport width / zoom factor
zoom factor = ideal viewport width / visual viewport width
因此,已知ideal viewport的宽为320px,缩放因子为2,就可以算出visual viewport宽等于160px (=320px/2)。layout viewport的宽度在这个公式中没有作用。
最大最小缩放因子
浏览器支持的缩放因子的最小值和最大值是多少?
首先说一个约束规定:visual viewport永不会比layout viewport更宽。所以在大多数实际情况下,最小缩放因子= ideal viewport的宽 / layout viewport的宽。
然而在这些测试中,我用了一个荒诞的layout viewport宽度值—5000,得到如下结果:
1、Android WebKit内核浏览器的最小缩放因子是0.25,最大值是4。这个值不可修改。通过640 / 0.25得到的2,560px这个值在横屏和竖屏模式下都是正确的。
2、IE浏览器的最大visual viewport宽是1024px,最大缩放因子在竖屏下是6,在横屏下是20/3。这个值不可修改。
3、在其他浏览器, 不写任何缩放规则时,最小缩放因子大约是0.25,最大大约是5。
4、增加这样一条缩放规则:当layout viewport无限宽或maximum-scale无限大的情况下,最小缩放因子约是0.1,最大约10
因此,理论上IPhone的visual viewport宽可以是介于32px(缩放因子为10)和3200px(缩放因子为0.1)之间的任意值。
这些缩放规则在不同设备上有些细微差别,详见下图。
我额外在Huawei C8813, Android 4.1.1上做了些测试。因为它们横屏模式下的ideal viewport宽是569。同样,它们最小和最大缩放因子也是0.25和4。因此,上述4条规则是Android WebKit内核浏览器的通用规则,而不只是三星、HTC等测试手机在640px这个特定宽度下结果。同样,最大visual viewport宽是2277px,大体上等于4 x 569。
缩放的兼容性
现在用表格表示一下上述内容的兼容性
上表中的英语内容的译文如下:
initial-scale
设置 initial-scale这条规则实际上做了如下2件事:
1、将页面初始缩放因子设置为给定的值,计算出相对于ideal viewport,得到visual viewport的宽。
2、设置layout viewport的宽等于刚刚计算出来的visual viewport的宽
如果一个IPhone手机处于竖屏模式,设置其initital-scale=2并且没有其他设置。那么不要奇怪,它其实是设置了visual viewport的宽为160px (=320px/2),这就是缩放规则的工作方式。
而且,它同时也设置了layout viewport的宽度为160px。因此我们现在拥有一个在最小缩放比例下160px宽的网页。(visual viewport不能比layout viewport大,所以页面不可能放大了)
但是,这好像没什么意义。如果你问我的真实想法是什么,我会说“完全TMD没用”,但毫无疑问,浏览器就是这么干了。
浏览器bugs
很明显Android WebKit浏览器是个例外,它允许使用initial-scale设置layout viewport的宽,当且仅当其值为1并且没有其他宽度设置规则。即Android WebKit浏览器的规则中仅有initial-scale=1起作用。
对于IE,会获取一个错误的ideal viewport(320x320而不是320x480),并且会将任意值都当作成1,所以在IE上initial-scale取什么值都无所谓。
定义宽度规则时的冲突
由于initial-scale是设置layout viewport的宽度的,所以可以利用如下的代码制造指令冲突:
<meta name="viewport" content="initial-scale=1,width=400">
那么浏览器得到相互冲突的指令后会发生什么呢?让我们再次回到IPhone 4S上看看:
1、initial-scale=1告诉浏览器要将layout viewport的宽度设置为320px(竖屏模式)和480px(横屏模式)
2、width=400告诉浏览器要将layout viewport的宽度在横屏和竖屏模式下都设置为400px
浏览器解决这个冲突的方法是:宽度最大优先原则(横屏或竖屏模式下均是)。
在我们的例子中,竖屏模式下layout viewport的宽度将变为400px(取320和400中的较大值),横屏模式下layout viewport的宽度将变为480px(取480和400中的较大值)。
有什么道理或依据吗?没有!但浏览器就这么任性地去做了。
在任何情况下,我们这里说一个layout viewport的min-width的情况。如果在上面的meta标签设置了min-width为400px,但允许浏览器增加layout viewport的宽度并超过这个值,只有设备尺寸或者设备旋转需要。
我不清楚是否有layout viewport的min-width的实际应用,但如果你需要,就在这里。
浏览器bugs
Android WebKit内核浏览器不遵循这条规则。如果width等于device-width或者小于320px,Android WebKit内核浏览器总会采用ideal viewport的宽作为layout viewport的宽。超过320都会遵循这个规律。
IE does not follow these rules above width=480, when it sets the layout viewport width to 1024px.
在width=480以上,IE不遵循这些规则,如当设置了layout viweport宽为1024px
initial-scale和 width的兼容性
iPhone手机上的小bug
我在IPhone上发现一个小bug,但IPad上没有。
1、如果在横屏模式下使用width和initial-scale组合,将会引起浏览器自动缩小。(即视觉视口小于布局视口)
2、并且,如果用户在横屏模式下放大页面,手机就会切换到竖屏模式。
3、竖屏模式下的最小缩放比等于横屏模式下的最小缩放比(即viewport宽) * 竖屏/横屏比率(So a landscape viewport width of 400 causes a portrait minimum-scale of 268)。
4、解决方案:在竖屏模式下尽可能的缩小,这个bug就消失了。
你可以前往这个页面,按照以上步骤操作试试。
minimum-scale 和maximum-scale
我只是做了一系列关于minimum-scale和maximum-scale的小测试,测试结果都很正常,除了在Android WebKit(因为它不支持minimum-scale)和IE(因为它制造出一系列糟糕的问题)上,所以我放弃了尝试搞懂它们的工作原理。
当layout viewport的宽按照上述计算,然后缩放被限制在50%和200%之间,即视觉视口可以变为从理想视口的一倍大到两倍小。猜猜会发生什么?(答案如下图)
一个例外情况是:visual viewport永不会变得比layout viewport小。
浏览器
[an error occurred while processing this directive]
(在处理这个规则时发生错误)
—————–华丽的分隔线———————
以下是本人的总结
总结
大致翻译了PPK大牛的神作,但我读完也还是特别凌乱,特总结一下
1、ideal viewport的尺寸在每一个设备上都不同
2、所有的缩放规则都是相对于 ideal viewport而言的,而与layout viewport无关
3、使用width=device-width和initial-scale=1两个规则,都可以将 layout viewport的值设置为ideal viewport的大小,但存在兼容问题:IPhone上只识别initial-scale;IE上只识别width=device-width;所以要结合起来一起使用。
4、设置了上述meta指令后, 读取出document.documentElement.clientWidth和 document.documentElement.clientHeight的值就是ideal viewport的尺寸(黑莓上是通过screen.width/height来获取)
5、layout viewport的宽度默认是980px或1024px
6、visual viewport width = ideal viewport width / zoom factor
7、Android WebKit内核和IE浏览器的layout viewport永不会小于320px。当这个视口小于320px时,它们会恢复到ideal viewport的宽度
8、layout viewport的最小宽度大约是ideal viewport的十分之一,这也是最大的缩放程度了;layout viewport的最大宽度是10,000px,IE为1024px
9、visual viewport永不会比layout viewport宽
10、Android WebKit内核浏览器的最小缩放因子是0.25,最大值是4,这个值不可修改
11、IE浏览器的最大visual viewport宽是1024px,最大缩放因子在竖屏下是6,在横屏下是20/3,这个值不可修改
12、在其他浏览器, 不写任何缩放规则时,最小缩放因子大约是0.25,最大大约是5
13、当layout viewport无限宽或maximum-scale无限大的情况下,最小缩放因子约是0.1,最大约10
14、设置 initial-scale这条规则实际上做了如下2件事:
a、将页面初始缩放因子设置为给定的值,计算出相对于ideal viewport,得到visual viewport的宽。
b、设置layout viewport的宽等于刚刚计算出来的visual viewport的宽
15、如果在meta中同时设置了initial-scale=1和width=400,则发生宽度规则冲突的解决方案是:宽度最大优先原则
16、width:用于设置layout viewpor的宽度