`

两个viewport的故事(第二部分)

 
阅读更多

http://weizhifeng.net/viewports2.html

原文:http://www.quirksmode.org/mobile/viewports2.html

在这个迷你系列的文章里边我将会解释viewport,以及许多重要元素的宽度是如何工作的,比如<html>元素,也包括窗口和屏幕。

这篇文章我们来聊聊关于移动浏览器的内容。如果你对移动开发完全是一个新手的话,我建议你先读一下第一篇关于桌面浏览器的文章,先在熟悉的环境中进行下热身。

移动浏览器的问题

当我们比较移动浏览器和桌面浏览器的时候,它们最显而易见的不同就是屏幕尺寸。为桌面浏览器所设计的网站在移动浏览器中显示的内容明显要少于在桌面浏览器中显示的;不管是对其进行缩放直到文字小得无法阅读,还是在屏幕中以合适的尺寸只显示站点中的一小部分内容。

移动设备的屏幕比桌面屏幕要小得多;想想其最大有400px宽,有时候会小很多。(一些手机声称拥有更大的宽度,但是它在撒谎-或者也可以说它给我们提供了没用的信息。)

平板设备中的像素中间层会在桌面环境和移动环境的缺口之间架起一段桥梁,比如像iPad或者传说中HP基于webOS所研发的设备,但是这并没有改变根本问题。站点必须也能在移动设备上工作,所以我们不得不让它们能在小尺寸的屏幕上正常显示。

最重要的问题在CSS上,特别是viewport的尺寸。如果我们照搬桌面环境的模式,那么我们的CSS就要立马熄火了(译者:即显示混乱)。

让我们看下之前sidebar为width: 10%的例子。如果移动浏览器想要实现跟桌面浏览器一样的行为,它们最多为元素设置40px的宽度,但是这太窄了。你的流式布局会看起来被挤乱了。

解决这个问题的一个方法是为移动浏览器建立一个特定的站点。先抛开你是否有必要这么做这个基本问题,而实际的情况是只有很少的网站拥有者真正知道要对移动设备做特殊的处理。

移动浏览器厂商想给它们的客户尽可能的提供最好的体验,这现在指的就是「尽可能的跟桌面一样」。因此耍一些花招是必要的。

两个viewport

viewport太窄了,以至于不能正常展示你的CSS布局。明显的解决方案是使viewport变宽一些。无论如何,需要把它分成两部分:visual viewport和layout viewport。

George Cummins在Stack Overflow上对基本概念给出了最佳解释:

把layout viewport想像成为一张不会变更大小或者形状的大图。现在想像你有一个小一些的框架,你通过它来看这张大图。(译者:可以理解为「管中窥豹」)这个小框架的周围被不透明的材料所环绕,这掩盖了你所有的视线,只留这张大图的一部分给你。你通过这个框架所能看到的大图的部分就是visual viewport。当你保持框架(缩小)来看整个图片的时候,你可以不用管大图,或者你可以靠近一些(放大)只看局部。你也可以改变框架的方向,但是大图(layout viewport)的大小和形状永远不会变。

也看一下Chris给出的解释

visual viewport是页面当前显示在屏幕上的部分。用户可以通过滚动来改变他所看到的页面的部分,或者通过缩放来改变visual viewport的大小。

mobile_visualviewport

无论怎样,CSS布局,尤其是百分比宽度,是以layout viewport做为参照系来计算的,它被认为要比visual viewport宽。

所以<html>元素在初始情况下用的是layout viewport的宽度,并且你的CSS是在屏幕(译者注:宽度等于layout viewport的虚拟屏幕)好像明显比电话屏幕宽(物理屏幕)要宽的假设基础上进行解释的。这使得你站点布局的行为与其在桌面浏览器上的一样。

layout viewport有多宽?每个浏览器都不一样。Safari iPhone为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。这不会变。

缩放

很显然两个viewport都是以CSS像素度量的。但是当进行缩放(如果你放大,屏幕上的CSS像素会变少)的时候,visual viewport的尺寸会发生变化,layout viewport的尺寸仍然跟之前的一样。(如果不这样,你的页面将会像百分比宽度被重新计算一样而经常被重新布局。)

理解layout viewport

为了理解layout viewport的尺寸,我们不得不看一下当页面被完全缩小后会发生什么。许多移动浏览器会在初始情况下以完全缩小的模式来展示任何页面。

重点是:浏览器已经为自己的layout viewport选择了尺寸,这样的话它在完全缩小模式的情况下完整的覆盖了屏幕(并且等于visual viewport)。

mobile_viewportzoomedout

所以layout viewport的宽度和高度等于在最大限度缩小的模式下屏幕上所能显示的任何内容的尺寸。当用户放大的时候这些尺寸保持不变。

mobile_layoutviewport

layout viewport宽度一直是一样的。如果你旋转你的手机,visual viewport会发生变化,但是浏览器通过轻微的放大来适配这个新的朝向,所以layout viewport又和visual viewport一样宽了。

mobile_viewportzoomedout_la

这对layout viewport的高度会有影响,现在的高度比肖像模式(竖屏)要小。但是web开发者不在乎高度,只在乎宽度。

mobile_layoutviewport_la

度量layout viewport

我们现在有两个需要度量的viewport。很幸运的是浏览器战争给我们提供了两个属性对。

document.documentElement.clientWidth-Height包含了layout viewport的尺寸。

document.documentElement.clientWidth/Height

  • 意义:Layout viewport的尺寸
  • 度量单位:CSS像素
  • 完全支持Opera, iPhone, Android, Symbian, Bolt, MicroB, Skyfire, Obigo。
  • 在Iris中Visual viewport有问题
    • Samsung WebKit在页面应用了<meta viewport>标签的时候会返回正确的值;否则使用<html>元素的尺寸。
    • Firefox返回以设备像素为单位的屏幕尺寸。
    • IE返回1024x768。然而,它把信息存储在document.body.clientWidth/Height中。这和桌面的IE6是一致的。
    • NetFront的值只在100%缩放比例的情况下是正确的。
    • Symbian WebKit 1 (老的S60v3设备)不支持这些属性。
  • BlackBerry不支持。

mobile_client

朝向会对高度产生影响,但对宽度不会产生影响。

mobile_client_la

度量visual viewport

对于visual viewport,它是通过window.innerWidth/Height来进行度量的。很明显当用户缩小或者放大的时候,度量的尺寸会发生变化,因为屏幕上的CSS像素会增加或者减少。

window.innerWidth/Height

  • 意义:Visual viewport的尺寸。
  • 度量单位:CSS像素。
  • 完全支持iPhone,Symbian,BlackBerry。
  • 问题
    • Opera和Firefox返回以设备像素为单位的屏幕宽度。
    • Android,Bolt,MicroB和NetFront返回以CSS像素为单位的layout viewport尺寸。
  • 不支持IE,但是它在document.documentElement.offsetWidth/Height中提供visual viewport的尺寸。
    • Samsung WebKit返回的是layout viewport或者<html>的尺寸,这取决于页面是否应用了<meta viewport>标签。
  • Iris,Skyfire,Obigo根本就是扯淡。

mobile_inner

不幸的是这是浏览器不兼容问题中的一部分;许多浏览器仍然不得不增加对visual viewport度量尺寸的支持。但是没有浏览器把这个度量尺寸存放任何其他的属性对中,所以我猜window.innerWidth/Height是标准,尽管它被支持的很糟。

屏幕

像桌面环境一样,screen.width/height提供了以设备像素为单位的屏幕尺寸。像在桌面环境上一样,做为一个开发者你永远不需要这个信息。你对屏幕的物理尺寸不感兴趣,而是对屏幕上当前有多少CSS像素感兴趣。

screen.width and screen.height

  • 意义:屏幕尺寸
  • 度量单位:设备像素
  • 完全支持Opera Mini,Android,Symbian,Iris,Firefox,MicroB,IE,BlackBerry。
  • 问题:Windows Mobile上的Opera Mobile只提供了风景模式(横屏)的尺寸。S60上的Opera Mobile返回的值是正确的。
    • Samsung WebKit返回layout viewport或者<html>的尺寸,这取决于是否在页面上应用了<meta viewport>标签。
    • iPhone和Obigo只提供了肖像模式(竖屏)的尺寸。
    • NetFront只提供风景模式(横屏)的尺寸。
  • Bolt,Skyfire依旧在扯淡。

mobile_screen

缩放比例 zoom level

直接读出缩放比例是不可能的,但是你可以通过以screen.width除以window.innerWidth来获取它的值。当然这只有在两个属性都被完美支持的情况下才有用。

幸运的是缩放比例并不太重要。你需要知道的是当前屏幕上有多少个CSS像素。你可以通过window.innerWidth来获取这个信息,如果它被正确支持的话。

滚动距离Scrolling offset

你还需知道的是visual viewport当前相对于layout viewport的位置。这是滚动距离,并且就像在桌面一样,它被存储在window.pageX/YOffset之中。

window.pageX/YOffset

  • 意义:滚动距离;与visual viewport相对于layout viewport的距离一样。
  • 度量单位:CSS像素
  • 完全支持iPhone,Android,Symbian,Iris,MicroB,Skyfire,Obigo。
  • 问题:Opera,Bolt,Firefox和NetFront一直返回0。
    • Samsung WebKit只有当<meta viewport>被应用到页面上时候才返回正确的值。
  • 不支持IE,BlackBerry。IE把值存在document.documentElement.scrollLeft/Top之中。

mobile_page

\<html> 元素

就像在桌面上一样,document.documentElement.offsetWidth/Height提供了以CSS像素为单位的<html>元素的整个尺寸。

document.documentElement.offsetWidth/Height

  • 意义:<html>元素的整体尺寸。
  • 度量单位:CSS像素。
  • 完全支持Opera,iPhone,Android,Symbian,Samsung,Iris,Bolt,Firefox,MicroB,Skyfire,BlackBerry,Obigo。
  • 问题:NetFront的值只在100%缩放比例的情况下才正确。
    • IE使用这个属性对来存储visual viewport的尺寸。在IE中,去document.body.clientWidth/Height中获取正确的值。

mobile_offset

媒体查询Media queries

媒体查询和其在桌面环境上的工作方式一样。width/height使用layout viewport做为参照物,并且以CSS像素进行度量,device-width/height使用设备屏幕,并且以设备像素进行度量。

换句话说,width/heightdocument.documentElement.clientWidth/Height值的镜像,同时device-width/heightscreen.width/height值的镜像。(它们在所有浏览器中实际上就是这么做的,即使这个镜像的值不正确。)

媒体查询

  • 意义:度量<html>元素的宽度(CSS像素)或者设备宽度(设备像素)。
  • 完全支持Opera,iPhone,Android,Symbian,Samsung,Iris,Bolt,Firefox,MicroB。
  • 不支持Skyfire,IE,BlackBerry,NetFront,Obigo。
  • 注意我在这里测试的是浏览器是否能从正确的「属性对」获取它们的数据。这些属性对是否提供正确的信息不是这个测试的一部分。

mobile_mediaqueries

现在哪个度量的尺寸对web开发者更有用?我的观点是,不知道。

我开始认为device-width是最重要的那一个,因为它给我们提供了关于我们可能会使用的设备的一些信息。比如,你可以根据设备的宽度来更改你的布局的宽度。不过,你也可以使用<meta viewport>来做这件事情;使用device-width媒体查询并不是绝对必要的。

那么width究竟是不是更重要的媒体查询呢?可能是;它提供了某些线索,这些线索是关于浏览器厂商认为在这个设备上网站应该有的正确宽度。但是这有些模糊不清,并且width媒体查询实际上不提供任何其他信息。

所以我不做选择。目前我认为媒体查询在分辨你是否在使用桌面电脑,平板,或者移动设备方面很重要,但是对于区分各种平板或者移动设备并没有什么用。

或者还有其他用处。

事件坐标

这里的事件坐标与其在桌面环境上的工作方式差不多。不幸的是,在十二个测试过的浏览器中只有Symbian WebKit和Iris这两个浏览器能获取到三个完全正确的值。其他所有浏览器都或多或少有些严重的问题。

pageX/Y仍然是相对于页面,以CSS像素为单位,并且它是目前为止三个属性对中最有用的,就像它在桌面环境上的那样。

Event coordinates

  • 意义:见正文
  • 度量单位:见正文
  • 完全支持Symbian,Iris
  • 问题:Opera Mobile在三个属性对中提供的都是pageX/Y的值,但是当你滚动一段距离后就出问题了。
    • 在iPhone,Firefox和BlackBerry上clientX/Y等于pageX/Y。
    • 在Android和MicroB上screenX/Y等于clientX/Y(换句话说,也就是以CSS像素为单位)。
    • 在Firefox上screenX/Y是错的。
    • IE,BlackBerry和Obigo不支持pageX/Y。
    • 在NetFront上三个属性对的值都等于screenX/Y。
    • 在Obigo上clientX/Y等于screenX/Y。
    • Samsung WebKit一直返回pageX/Y。
  • 没有在Opera Mini,Bolt,Skyfire上测试过。

mobile_pageXY

clientX/Y是相对于visual viewport来计算,以CSS像素为单位的。这有道理的,即使我还不能完全指出这么做的好处。

screenX/Y是相对于屏幕来计算,以设备像素为单位。当然,这和clientX/Y用的参照系是一样的,并且设备像素在这没有用处。所以我们不需要担心screenX/Y;跟在桌面环境上一样没有用处。

mobile_clientXY

viewport meta标签

Meta viewport

  • 意义:设置layout viewport的宽度。
  • 度量单位:CSS像素。
  • 完全支持Opera Mobile,iPhone,Android,Iris,IE,BlackBerry,Obigo。
  • 不支持Opera Mini,Symbian,Bolt,Firefox,MicroB,NetFront。
  • 问题:Skyfire不能处理我的测试页面。
    • 如果在Samsung WebKit中对页面应用<meta viewport>,一些其他属性的意义会发生变化。
    • Opera Mobile,iPhone,Samsung和BlackBerry不允许用户进行缩小。

最后,让我们讨论一下<meta name="viewport" content="width=320">;起初它是苹果做的一个扩展,但是与此同时被更多的浏览器所借鉴。它的意思是调整layout viewport的大小。为了理解为什么这么做是必要的,让我们后退一步。

假设你创建了一个简单的页面,并且没有给你的元素设置「宽度」。那么现在它们会被拉伸来填满layout viewport宽度的100%。大部分浏览器会进行缩放从而在屏幕上展示整个layout viewport,产生下面这样的效果:

mq_none

所有用户将会立刻进行放大操作,这个是工作的,但是大部分浏览器完好无缺的保持元素的宽度,这使得文字很难阅读。

mq_none_zoomed

(值得注意的例外是Android WebKit,它实际上会减小包含文字的元素的大小,所以文字就能适配屏幕。这简直太有才了,我觉得所有其他浏览器应该借鉴这个行为。我过阵子将会完整的写一下这个议题。)

现在你应该尝试设置html {width: 320px}。现在<html>元素收缩了,并且其他元素现在使用的是320px的100%。这在用户进行放大操作的时候有用,但是在初始状态是没用的,当用户面对一个缩小了的页面这几乎不包含任何内容。

mq_html300

为了绕开这个问题苹果发明了viewport meta标签。当你设置<meta name="viewport" content="width=320">的时候,你就设置了layout viewport的宽度为320px。现在页面的初始状态也是正确的。

mq_yes

你可以把layout viewport的宽度设置为任何你想要的尺寸,包括device-widthdevice-width会把screen.width(以设备像素为单位)做为其值,并相应的重置layout viewport的尺寸。

但这里有一个隐情。有时候正规的screen.width不那么明了,因为像素的数量太大了。比如,Nexus One的正规宽度是480px,但是Google的工程师们觉得当使用device-width的时候,layout viewport的宽度为480px,这有些太大了。他们把宽度缩小为三分之二,所以device-width会返回给你一个320px的宽度,就像在iPhone上一样。

如果,像传闻那样,新的iPhone将会炫耀一个更大的像素数量(并不意味着一个更大的屏幕),如果苹果借鉴了这个行为我将不会感到惊讶。也许最终device-width就意味着320px。

相关研究

一些相关的主题不得不需要进行更深一步的研究:

  • position: fixed。一个固定的元素,就像我们知道的那样,是相对于viewport来进行定位的。但是相对于哪个viewport?我正在同时做这个研究。
  • 其他媒体查询:dpi,orientation,aspect-ratio。尤其是dpi,那是一个灾难地区,不仅仅是因为所有浏览器都返回96dpi,通常都是错的,也是因为我完全不确定对于web开发者来说哪个值是他们最感兴趣的。

  • 当一个元素比layout viewport/HTML元素宽的时候会发生什么?比如我把一个拥有width: 1500px属性的元素插入到我的测试页面中的一个?这个元素将会从HTML元素中伸出来(overflow: visible),但这意味着实际的viewport可以变得比layout viewport要宽。除了这个以外,旧Android(Nexus One)还会当这个发生的时候放大HTML元素。这是个好主意吗?

(完)

分享到:
评论

相关推荐

    求两个数组的交集,配合去重方法使用.html

    第二次调用 item = 2 arr2.indexOf(item) !== -1=== arr2.indexOf(2) !== -1 条件成立 会把item添加到新的数组 进行下一次调用 */ // res得到两个数组中交集 [2,2,3,4] var res = arr1.filter(function (item...

    ExtJs 学习笔记 Ext.Panle Ext.TabPanel Ext.Viewport第1/3页

    除了Ext.Panel,ExtJs还提供了其他两个非常有用的组件,即Ext.TabPanel和Ext.Viewport。Ext.TabPanel允许我们在面板内创建选项卡式的界面,每个选项卡可以作为一个容器,加载不同的内容。这种布局方式对于内容分组和...

    C# Nehe OpenGL第二课 绘制多边形

    本教程是"C# Nehe OpenGL第二课 绘制多边形",将介绍如何在C#环境中使用OpenGL绘制基本的多边形。 在OpenGL中,绘制多边形通常涉及以下几个关键概念: 1. **顶点** (Vertices):多边形的基本构建单元,表示图形中...

    EON Studio 快速学习教材

    2. 紧接着会出现名为“Geometry Import”的第二个对话框。同样也请注意红色圈选的部分,确定无误后按下“OK”键。 步骤三:模拟 Living Room 1. 从工具列上按下“Start Simulation”按键开始执行模拟 Living Room...

    extjs 双表头

    第二列则是一个包含两个子列的`column`,显示了邮箱和电话。 在实际项目中,你可以根据需求调整这个示例,例如改变列的数量、宽度,或者添加更多的数据字段。`ux`文件夹可能包含了扩展组件或自定义样式,用于增强双...

    对vue中v-if的常见使用方法详解

    2- 进行两个视图之间的切换 下面我写了两个例子,是Vue官方的简单实例。 第一个实例实现了 type等于不同值,A,B,C 三个元素的展示情况。 第二个例子实现了,点击按钮实现两个视图的切换。 &lt;!DOCTYPE html&gt; &lt...

    pdf.js使用文件流预览pdf

    这两个文件分别包含了主库和工作线程代码,它们是PDF.js运行所必需的。 接着,我们需要一个容器来显示PDF内容。在HTML中创建一个`&lt;div&gt;`元素,并为其设置一个ID,例如`"pdfContainer"`,这样我们可以通过JavaScript...

    前台代发示例

    这里有两个例子: #### 3.1 基础 Panel 示例 ```javascript var obj = { title: "hello", width: 300, height: 200, html: 'Hello, easyjfopensource' }; var panel = new Ext.Panel(obj); panel.render(...

    前端大厂最新面试题-html.docx

    Viewport有两个视口:可见视口和视窗视口。Viewport的属性值包括: * width:设置layout viewport的宽度,为一个正整数,或字符串"width-device"。 * initial-scale:设置页面的初始缩放值,为一个数字,可以带小数...

    第4章+图形变换与裁剪

    视点坐标系和世界坐标系是两个不同的坐标系统。视点坐标系遵循左手法则,而世界坐标系遵循右手法则,这是为了确保在不同坐标系下的变换能够正确无误地执行。在程序中,`glTranslatef()`和`glScalef()`分别实现了视点...

    html5构建触屏网站之网站尺寸探讨

    本文分为两个部分,第一部分讨论跨平台网站的可行性,第二部分讨论viewport是如何设置的 开发跨平台网站? 靠标签的自适应宽高实现多尺寸通用!? 标签宽高可以自适应没错,我们很早就可以这么做了。但是你会发现很...

    计算机图形学 第六章 二维变换及二维观察-2.ppt

    用户坐标系到观察坐标系的变换通常包含平移和旋转两个步骤。平移将观察坐标系的原点移动到用户坐标系的原点,旋转则使两者重合。窗口到视区的变换涉及比例变换和反平移,确保窗口内的点能正确映射到视区内的对应位置...

    工程硕士学位论文 基于Android+HTML5的移动Web项目高效开发探究

    第二章 多窗口类浏览器设计 11 2.1 多窗口类浏览器需求分析 11 2.1.1 Activity简介 11 2.1.2 Fragment简介 11 2.1.3 多窗口类浏览器需求 12 2.2 多窗口浏览器模式的实现机制 12 2.2.1安卓移动端多窗口浏览器框架 12 ...

    WPF 3D基础

    如果我们想要自定义这个顺序,可以设置`TriangleIndices`,如`TriangleIndices="3,0,1"`,这将绘制一个三角形(3, 0, 1),而忽略第2个顶点。 总结来说,WPF 3D提供了丰富的功能来创建和操纵3D对象。通过理解和掌握...

    DirectX_9.0_3D游戏开发编程基础

    - **向量相减**: 类似于相加,只是将第二个向量的分量取反后再相加。 - **标量与向量的乘积**: 标量乘以向量的每个分量。 - **点积**: 两个向量的点积等于它们的模的乘积再乘以两个向量之间的夹角的余弦值。 - **...

    SpringBoot采用pdf.js加载pdf文件

    通常,pdf.js库包括两个主要部分:一个用于前端展示的`build`目录和一个包含源码及构建工具的`src`目录。为了预览PDF,我们主要关注`build`目录中的`pdf.js`和`pdf.worker.js`两个文件。 接下来,我们创建一个...

    API之网络函数---整理网络函数及功能

    CompareFileTime 对比两个文件的时间 CopyFile 复制文件 CreateDirectory 创建一个新目录 CreateFile 打开和创建文件、管道、邮槽、通信服务、设备以及控制台 CreateFileMapping 创建一个新的文件映射对象 ...

    自适应屏幕代码

    我们可以看到两个不同的`&lt;meta name="viewport"&gt;`配置: ##### 2.1 第一条配置 - **代码**: ```html &lt;META name="viewport" content="device-width"&gt; ``` - **解释**: - `width=device-width`: 将视口宽度设置...

Global site tag (gtag.js) - Google Analytics