Skip to content

移动端Web浏览器之viewport - 移动端响应式布局需要注意的问题 #15

@abcrun

Description

@abcrun

之前做手机Web开发,尝试用响应式布局来区分不同手机的样式,但是发现个问题: 当通过media query设置width范围时,但是却不起作用 ,这个问题困惑了我很久,当时是通过屏幕的物理分辨率来做区分的,后来当看到一篇关于介绍手机viewport的文章后才豁然开朗,问题就出在这里,错误的屏幕的物理分辨率和web页面的显示尺寸搞混。原文地址:http://www.quirksmode.org/mobile/metaviewport/,这个站点上面还有很多关于兼容性的研究,很值得学习。

关于viewport的文章,原文是从学术研究的角度来描述不同手机/浏览器关于viewport的各项参数和区别,为了方便理解,我将其总结如下。强烈建议阅读这篇文章之前先参考原文。

首先要区分移动Web页面视图三个比较重要的概念:layout viewport,visual viewport,ideal viewport。引用原文的解释说明一下layout viewport 和 visual viewport的区别:

Imagine the layout viewport as being a large image which does not change size or shape. Now image you have a smaller frame through which you look at the large image. The small frame is surrounded by opaque material which obscures your view of all but a portion of the large image. The portion of the large image that you can see through the frame is the visual viewport. You can back away from the large image while holding your frame (zoom out) to see the entire image at once, or you can move closer (zoom in) to see only a portion. You can also change the orientation of the frame, but the size and shape of the large image (layout viewport) never changes.

在理解了layout viewport和visual viewport的区别后,我再来大致解释一下这三个概念:

  • layout viewport:页面加载时,会根据是否存在viewport(\<meta name="viewport" />)或者viewport的参数,来初始化页面内容的尺寸。如果计算出的宽度小于设计稿的宽度,那么将只显示计算后layout viewport宽度范围内的尺寸,超出的部分将不会显示。(如设计稿页面宽1280px,而计算后的layout viewport宽980px,那么仅会显示页面980px内的内容),至于如何计算,下文将会讲到;
  • visual viewport:页面可见区大小,这个值是随着页面的缩放而改变的,页面放大的时visual viewport尺寸将会变小(由于屏幕大小不变,所以这是改变的是屏幕每英寸的点分辨率)
  • ideal viewport:浏览器给出的理想的页面尺寸,也就是device-width

需要注意的是:页面初始状态下,我们期望浏览器会自动缩放使layout viewport = visual viewport,但是有些时候并不如所愿

下面是不同设备浏览器ideal viewport尺寸图:

ideal viewport

页面初始化的时候会根据参数(有无)设置layout viewport的大小;一旦layout viewport确定,那么页面可现实的内容也就确定了。假如layout viewport的尺寸小于本身网页的尺寸,那么网页上在layout viewport区域之外的内容永远看不见的,而缩放或移动只是查看layout viewport范围内的内容。页面缩放时改变的是visual viewport的尺寸(改变的屏幕的点分辨率),而layout viewport尺寸是固定不会变的。

CSS布局是以layout viewport作为参考的,layout viewport可以通过document.documentElement.clientWidth/clientHeight来计算;visual viewport可以用window.innerWidth/innerHeight来表示,不过兼容性有问题,表示可视区域的尺寸。

页面横竖屏调整时,旋转的是visual viewport,而layout viewport不会旋转,所以当横屏时浏览器会重新计算scale值;页面的缩放是相对于ideal viewport来计算的;visual viewport = ideal viewport/scale

页面viewport计算模型

下面根据页面的解析过程来说明一下,浏览器在页面布局时的计算viewport的流程:

没有viewport

当页面没有声明viewport时,手机浏览器会取980px作为默认layout viewport值(IE/BlackBerry:1024px),然后全部显示,将visual viewport = layout viewport计算出scale值.

default layout viewport

存在viewport

当页面声明了viewport标签时,width = layout viewport的宽度,对于viewport声明存在以下情况:

  • 仅对于width=device-width,则layout viewport的宽高采用ideal viewport的尺寸(iPhone,iPad横屏时宽度是ideal viewport的宽度,而非高度)
  • 仅对于initial-scale=1,则layout viewport的宽高采用ideal viewport的尺寸(IE横屏时宽度是ideal viewport的宽度,而非高度)
  • 单独设置width=x或者initial-scale=x时,可以通过width直接获得或者通过initial-scale(相对于ideal vieport)计算出layout viewport的尺寸,由于layout viewport是有范围的(理论上1/10 * ideal viewport ~ 10000px),所以当超出尺寸范围时,浏览器会选择相应的最大的值或者最小值(对于IE和Android Webkit最小值不能低于320px )
  • 如果width和initial-scale都存在时,会根据initial-scale计算出来的结果(ideal viewport/initial-scale)取最大的值(而Android Webkit则采用width值,IE永远认为initial-scale=1,但是需要注意layout的取值范围)。

简单的画了个流程图:

viewport

示例说明

下面以 Android Sansung Webkit 为例来说明一下,假设当前有个页面,设计的宽度是1200px:

没有指定viewport
  1. 浏览器设置layout viewport宽度为默认值:980px(这样也就意味着尽管网页内容的宽度是1200px,但是却只能显示980px内的内容);
  2. 浏览器将页面缩放至最小,使之visual viewport:width = layout viewport:width = 980px,这时可以根据visual viewport和ideal viewport计算出scale值了:scale = ideal viewport/visual viewport = 400/980 = 0.41
  3. 由于初始化后layout viewport(980px)是固定不变的,所以当页面拖动、放大或者缩小时,只能显示980px像素内的内容;
  4. 当缩放时会根据ideal viewport重新计算visual viewport的尺寸。
  1. 浏览器设置layout viewport宽度为ideal viewport的宽度:400px,这时网页呈现最大的宽度为400px内容,其他的内容将会被忽略;
  2. 浏览器将页面缩放至最小,这时visual viewport:width = layout viewport:width = ideal viewport:width,scale = 1;
  3. 其他同上

需要说明的是:注意横屏时ipad/iphone和IE layout viewport宽度的取值(参考之前的说明)

  1. 对于本例Android Samsung Webkit,浏览器将忽略initial-scale取参数width = 400px为layout viewport宽度的宽度(注意前面写的关于ipad/iphone的取值),这时网页呈现最大的宽度为400px内容,其他的内容将会被忽略;
  2. 浏览器将页面缩放至最小,这时visual viewport:width = layout viewport:width = ideal viewport:width,scale = 1;
  3. 其他同上

回过头来谈一下响应式布局,通过上面分析,我国我们通过media query进行响应式布局,设置的width值实际上是参照layout viewport值,由于viewport在手机上的特殊性,企图通过width来区分不同手机是不可取的,实际上也没有特别好的方法来处理,或许可以通过:

@media screen and (device-aspect-ratio: *value*){}

来处理。

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions