3 拥抱流式布局
20世纪90年代末,我刚学习做网页的时候,页面布局结构都是基于表格的。通常,屏幕上显示的各部分元素都是百分比宽度。例如,左侧的导航栏可能占据20%的屏幕宽度,主内容区占据80%。那时的浏览器视口还没有像今天这样天差地别,所以表格式布局在有限范围的视口中表现良好且能自由伸缩。没人太在意某些句子在不同大小的屏幕上效果不一样。不过,随着CSS的兴起,网页设计也更加接近印刷设计,很多人(包括我)开始使用固定的基于像素的布局,而基于百分比的布局逐年减少。
所有伟大的设计和思想,都会卷土重来。迷你车、烫发(我喜欢)以及喇叭牛仔裤都在多年沉寂后卷土重来。现在,轮到百分比布局方法上演王者归来了。
本章内容
理解为什么响应式设计需要百分比布局
将元素的固定像素宽度转换为百分比宽度
将文字的固定像素大小转换为等量的相对尺寸
理解如何找到任意元素的上下文
学习如何使图片平滑缩放
学习如何为不同的大小的屏幕提供不同的图片
学习如何让媒体查询与弹性图片及流式布局协同工作
使用CSS网格系统从头创建一个响应式布局
3.1 固定布局经不起未来考验
我前面说过,在“表格布局”的时代落幕之后,很少有需要使用百分比布局。一般情况下,我接到的要求都是让HTML和CSS可以完美匹配950-1000像素显示屏。如果布局使用了百分比宽度(如90%),耳边很快就能听到抱怨:“我的显示器上效果不太一样。”固定像素尺寸的网页是匹配固定像素尺寸显示器的最简单办法。
即使在最近,当我们针对某个特定的流行设备如iPad或者iPhone,使用媒体查询来制作修正版的布局时(类似第2章的例子),仍然是基于已知的视口宽度使用固定像素尺寸。很多人喜欢这种方式,因为每次有新东西出来,只要用户要求修改网站,就可以借机收一次费。但是,这种方法不是一种完全兼容未来的网页制作方法。未来,还会出现更多大小不一的视口,我们需要一些适应未知设备的方法。 3.2 为什么响应式设计需要百分比布局 在认识到媒体查询威力无比的同时,我们也要看到它的局限性。那些仅使用媒体查询来适应不同视口的固定宽度设计,只会从一组CSS媒体查询规则突变到另一组,两者之间没有任何平滑渐变。从我们在第2章的例子来看,当某个视口处于媒体查询设置的固定宽度范围之外(可能是某种未知的未来设备及视口),网页就需要水平滚动才能完整浏览。不过我们想要的是一个灵活的设计,能在所有视口中都完美显示,而不仅仅只针对媒体查询设定的一些固定视口。切入主题吧(看出来我在这抖的包袱了吗?我用了一句电影行话来搭配我们的电影主题的网站……没看懂?好冷啊,我去穿件外套……),我们需要将固定像素布局转换成灵活的百分比布局。这样才能让页面元素根据视口大小在一个又一个媒体查询之间灵活伸缩修正样式。
百分比布局和媒体查询和谐共处
我之前提到过伊桑·马科特在A List Apart上发表的有关响应式网页设计的文章(http://www.alistapart.com/articles/responsive-web-design/)。其实他采用的技术(流动布局、弹性图片和媒体查询)并不新颖,而将这些理念基于一套完整统一的方法论进行应用则很有创造性。对于很多网页设计工作来说,他的文章打开了很多新的思路。事实上,这种网页设计的新方法做到了两全其美:使用百分比布局创建流动的弹性界面,同时使用媒体查询来限制元素的变动范围。将这两者组合到一起构成了响应式设计的核心,基于此可以创造出真正完美的设计。 3.3 将网页从固定布局修改为百分比布局 在可预见的未来,你看到的和制作的网页都还会使用固定尺寸。当前,我们通常是在Photoshop、Fireworks等软件制作的设计图中(基于像素单位)度量元素的大小、外边距,然后将这些尺寸直接写进CSS代码中。文字大小也是这样设置的。我们在图片编辑软件中点击一下文字对象,查看其具体尺寸(一般单位是像素),然后将其写入对应的CSS代码。那我们如何将固定尺寸转换为相对尺寸呢? 3.3.1 需要牢记的公式 作为伊桑·马科特的粉丝,我对他的崇拜可能有点太过了。但此处我有必要再给他行一次三跪九叩之礼。在丹·锡德霍姆(Dan Cederholm)编写的《无懈可击的Web设计》 一书中,伊桑·马科特为其撰写了一章关于流动布局的内容。在书中,他提供了一个简易可行的公式,将固定像素宽度转换对应的百分比宽度:
目标元素宽度÷上下文元素宽度=百分比宽度
看起来有点像方程式?不要害怕,在创建响应式设计的过程中,这个公式很快会成为你的得力助手。不再赘述理论,接下来我们使用该公式将 And the winner isn’t…网站从固定尺寸转换成百分比布局。
如果你还记得,我们在第2章学习使用媒体查询来支持不同视口时,所编写的页面基本标签结构应该是这样的:
页面内容稍后添加,但我们要重点关注的是此处用来设置页面主要结构模块(头部、导航、侧边栏、内容区以及页脚)宽度的CSS。请注意,下面的代码中我省去了很多样式,以便将注意力集中在页面结构上:
上面代码中所有的尺寸值都是像素值。我们从最外层的元素开始,使用“目标元素宽度÷上下文元素宽度=百分比宽度”这个公式将它们改成百分比宽度。
当前所有的页面内容都被包裹在一个ID为#wrapper的div中。在上面所示的CSS中可见,这个div被设置为外边距自适应,宽度960像素。作为最外层的div,我们该把它的宽度定义为相对视口宽度的百分之多少呢? 3.3.2 设置百分比元素的上下文 我们需要一些“hold”场面的元素,让它们作为网页中百分比宽度元素(内容区、侧边栏、页脚)的上下文。我们要为这些元素的宽度设置一个百分比值,而#wrapper元素的宽度应该是相对于视口尺寸而言的。现在我们从头来过,将其宽度设置为96%看看效果如何。#wrapper元素修改后的样式如下:
修改后的效果如下图所示:
看起来还不错!96%的宽度刚刚好,不过也可以选择100%或者90%——这取决于我们的感觉,保证网页在视口内有最美观的视觉效果即可。
将里层元素从固定宽度改为百分比宽度稍微有点复杂。我们先来看看头部。再复习一下公式:目标元素宽度÷上下文元素宽度=百分比宽度。#header(目标元素)被包裹在#wrapper(上下文元素)中。因此,我们用#header的宽度940像素,来除以上下文元素(#wrapper)的宽度960像素,结果是0.979166667。将小数点向右移动两位转换为百分比值,我们就得到了头部的百分比宽度,即97.9166667。将结果设置到CSS中:
navigation和#footer的宽度也是基于像素的,我们使用相同的方法将它们转换为百分比。
最后在浏览器中查看效果之前,我们来说说#content和#sidebar。上下文宽度还是960像素,那我们只需要用目标元素宽度来除这个数字即可。#content的宽度是698像素,除以960结果是0.727083333。移动小数点后结果是72.7083333%——这就是#content的百分比宽度。侧边栏的宽度是220像素,但它有2像素宽的边框。我不想让右侧边框的宽度随着上下文变宽或是变窄,而是始终保持在2像素。所以需要将侧边栏的宽度减去一点。我将本例中的侧边栏宽度减去2像素,然后套用之前的公式,即目标元素宽度(218像素)除以上下文元素宽度(960像素),结果是0.227083333。移动小数点后,侧边栏的百分比宽度是22.7083333%。再将所有像素宽度修改为百分比宽度,最终的CSS代码如下所示:
下图展示了当前页面在视口宽度约为1000像素的Firefox浏览器中的效果:
目前看来效果很不错。接下来我们继续使用“目标元素宽度÷上下文元素宽度=百分比宽度”这个公式,将页面各处10像素的内边距、外边距也替换成等价的百分比值。所有这些间距都基于960像素的上下文,所以替换成对应的百分比值就是1.0416667%(10÷960)。
这些数字能四舍五入吗?
在如前所述的视口中,页面元素表现良好。但是导航区域不太理想。如果将视口尺寸缩小一点,只要一丁点,导航链接就会变成两行:
此外,如果将视口放大,导航链接之间的间距并不会相应地增加。我们来看看与导航关联的CSS代码并试着找出问题的原因:
一眼看上去,就发现好像上面的第三个样式规则#navigation ul li a,还有25px这样的基于像素单位的外边距。让我们用久经考验的公式来修正这个问题。导航区域的宽度是940像素,所以换算结果是2.6595745%。对该样式的修改如下:
够简单吧!我们到浏览器中看看是否万事如意……
哎,等等,这不是我们想要的效果。导航链接没再折成两行,但很明显导航链接之间的间距不对。导航链接看上去像一个字典里查不到的长单词…… 3.3.3 必须时刻牢记上下文 再想想我们的公式(目标元素宽度÷上下文元素宽度=百分比宽度),你就会知道前述问题的症结所在——上下文元素。相关的标签结构如下:
可以看到<a href="#">链接被包裹在各自对应的<li>标签中。它们才是我们要找的外边距的上下文元素。看看<li>标签对应的CSS代码,会发现没有为其设置宽度:
这种情况很常见,不过我们有很多方法解决这个问题。我们可以给<li>标签设置一个明确的宽度值,这个值必须得是一个固定像素宽度或者是一个相对于其包裹元素(#navigation div)的百分比值,但这两种值都无法保证<li>中的文字灵活可变。
此外我们可以修改<li>现在的CSS代码,将inline-block改为inline:
使用display: inline;(这样可以阻止<li>元素渲染为块级元素)还可以使导航在不支持inline-block的老版本Internet Explorer(版本6和7)中水平显示。不过,我是inline-block的粉丝,因为它允许我们在现代浏览器中有效的控制外边距和内边距,所以我会让<li>标签继续保持inline-blocks状态(一会可以给IE6和IE7追加一个覆写样式),然后将(没有明确的上下文的)<a>标签上的百分比外间距挪到<li>上来。修改后的样式如下所示:
下图展示了在视口宽度1200像素的浏览中的显示效果。
导航区域的修改现在告一段落,但是导航链接在视口变小时仍然会折成两行,只有到了视口小于768像素时,第2章编写的媒体查询才能覆写当前的导航样式。在着手修正导航部分的问题之前,我准备先将文字大小从像素尺寸修改为相对单位em。完成这个工作之后,我们就会注意到另外一个被忽略的问题 ,即让图片随着页面的变化缩放。
3.4 用em替换px
过去的几年里,网页设计师使用em替代px主要是为了文字缩放。因为老版本的Internet Explorer无法缩放像素单位的文字。不过现代浏览器很久以前就支持缩放以像素为单位的文字了。那用em替换px还有什么必要性或优越性呢?有两点显而易见的理由:一是那些使用Internet Explorer 6的用户也将能够缩放文字,二是这样做可以使我们设计师和开发者的生活更简单。em的实际大小是相对于其上下文的字体大小而言的。如果我们给<body>标签设置文字大小为100%,给其他文字都使用相对单位em,那这些文字都会受body上的初始声明的影响。这样做的好处就是,如果在完成了所有文字排版后,客户又提出将页面文字统一放大一点,我们就可以只修改body的文字大小,其他所有文字也会相应变大。
同样,“目标元素尺寸÷上下文元素尺寸=百分比尺寸”这个公式也适用于将文字的像素单位转换为相对单位。值得注意的是,现代浏览器的默认文字大小都是16像素(显式声明的除外)。因此一开始给body标签应用下列任何一条规则所产生的效果都一样:
下面举一个例子。示例网站的样式表中,第一段以像素为单位的文字就是页面左上角的网站标题:AND THE WINNER ISN’T…
因为48 ÷ 16 = 3,所以我们将样式修改如下:
这个逻辑在整个网站中通用。如果发现哪出了毛病,那应该是目标元素的上下文发生了变化。以页面中的标签为例:
修改后的相对单位样式如下:
可以看到<span>元素的文字大小(38像素)是相对于其父元素的文字大小(69像素)而言的。而它的行高(40像素)则是相对于其本身的文字大小(38像素)而言。
现在,我们的页面结构可以自动缩放,文字大小也已从像素单位转换成相对单位。但是,我们还必须解决图片大小随视口缩放的问题。那我们接下来看看这个问题。
em究竟是什么?
em是书面形式的大写字母“M”的简称,发音和M相同。以前,“M”常被用来测定某种字体的大小,因为它是英文字母中最大(最宽)的字母。如今,em作为一个测量单位,指的是特定字母的宽度和高度相对于特定字体磅值的比例。 3.5 弹性图片 在现代浏览器(包括IE 7+)中要实现图片随着流动布局相应缩放非常简单。只需在CSS中作如下声明:
这样就可以使图片自动缩放到与其容器100%匹配。更进一步,可以将同样的样式应用到其他多媒体标签上。如:
这些多媒体元素都可以自动缩放了。但是,对于采用的<iframe>显示视频的网站(比如YouTube),这个技术还不行,我们会在第4章解决这个问题。眼下,还是专注于图片的缩放问题吧,因为不论何种多媒体元素,原理都是相通的。
使用这种方法有几个需要斟酌的问题。第一,要提前准备一张足够大的图片,以备大视口使用。但这也就引出了第二个,同时也是非常重要的问题,即无论视口多大,什么设备,都得下载超大图片。可能对某些设备来说,图片大小只要原始图片的25%就好了。另外,在某些情况下,你还不得不因此而考虑带宽限制。我们呆会儿再说第二个问题,先把图片缩放问题说完。 3.5.1 让图片随视口缩放 示例网站的侧边栏有一组电影海报,其中两部是好电影,两部是烂电影(不在此处讨论这个话题)。对应的标签结构如下:
尽管我在CSS文件中给img元素追加了max-width: 100%声明,但是没看到什么变化,图片没有按我预想的方式随着视口变化而变化:
原因是我在标签中指定了图片的宽度和高度:
又犯了一个低级错误!修改对应的图片标签,删除宽度和高度属性:
刷新浏览器看看修改的效果:
修改确实起效了!但却引入了另外一个问题。因为图片被缩放至其包裹元素宽度的100%,所以每张图片都填满了侧边栏。同样,解决这个问题的方法有很多…… 3.5.2 为特定图片指定特定规则 可以像下面代码所示的那样给每个图片追加一个额外的class:
然后,为其宽度设置一个特定的规则。另外,我们还可以保留图片标签不变,利用CSS规则的针对性,用一个更具体的规则覆写侧边栏图片已有的max-width样式:
下图展示了侧边栏当前的显示效果:
这种利用CSS规则针对性的方法,也可以用来控制其他图片或多媒体元素的宽度。第5章会介绍CSS3选择器的强大威力,它能让我们不追加任何多余标签,或是不引入任何JavaScript框架(如jQuery),直接指向页面任意元素,来完成我们的苦活累活。
我给侧边栏图片设置的宽度是45%,因为图片之间需要一点间距,所以两张图片宽度加起来是90%,给我留出了一点余地(10%)。
现在侧边栏图片终于完美显示了,我准备将奥斯卡雕像图片的标签中的宽度和高度也删除掉。但是,除非我为其设置一个百分比宽度,否则它还是不会自动缩放;所以我再次使用我们值得信赖的公式来为其设置一个百分比宽度。
3.5.3 给弹性图片设置阈值
现在图片可以随着视口的伸缩而缩放了。但是如果将视口拉大,直到图片拉伸至超出其原始尺寸,那问题就麻烦了。看看下图所示的1900像素宽的视口中奥斯卡图片的显示效果:
oscar.png图片的实际宽度是202像素。但在超过1900像素宽的视口中,图片也被拉大了,其显示宽度超过了300像素。我们可以通过追加另一个特定样式来为图片设置阈值:
这样就可以保证oscar.png按照通用的图片样式自由缩放,但又绝不会超出max-width属性设置的最大上限。下图展示了给图片设置上限之后的页面效果:
3.5.4 超级全能的max-width属性
另一种限制页面无限制扩张的方法是给最外层的#wrapper div设置max-width属性,如下所示:
这样意味着页面会缩放至视口宽度的96%,但绝不会超过1414像素宽(设置为1414像素,是因为在这个宽度下大多数浏览器恰好能剪切出完整的角旗,而不会留个半边)。下图展示了视口宽度1900像素下的页面效果:
显然这些方法仅是备用选项。但它证明了流动布局的灵活性,也说明了如何通过几个特定声明来控制文档流。 3.6 为不同的屏幕尺寸提供不同的图片 现在我们可以让图片完美缩放,而且也知道了如何限制特定图片的显示尺寸。但在这章的前面我们曾指出图片缩放存在一定的问题。图片尺寸必须比其显示尺寸更大以保证渲染效果,否则的话图片可能看起来很糟糕。基于这个原因,图片文件的体积就永远比实际显示所需的大。
很多人都在研究这个问题,尝试为较小的屏幕尺寸提供较小的图片。第一个著名的例子是Filament Group的“响应式图片”(http://filamentgroup.com/lab/responsive_images_experi_menting_with_context_aware_image_sizing/)。不过最近,我已经转而使用Matt Wilcox的“自适应图片”(http://adaptive-images.com)了。Filament Group的解决方案需要对图片标签做一定修改。Matt Wilcox的解决方案则不需要,而且他的方案会根据标签中已经设定的全尺寸图片自动创建各种尺寸的图片。这种解决方案允许基于一组屏幕尺寸断点,根据用户需要为其提供不同的图片。接下来我们就看一看如何利用该技术实现图片自适应。
设置自适应图片
实现Adaptive Images解决方案需要Apache 2、PHP 5.x和GD库,也就是说需要Web服务器端编程。首先,在其网站上下载.zip文件开始配置:
解压文件,然后将其中的adaptive-images.php和.htaccess文件拷贝到网站的根目录。如果你网站的根目录下已经有一个.htaccess文件了,不要覆盖它。参考下载包中的instructions.htm文件看看怎么做合适。
接着在网站根目录下创建一个名为ai-cache的文件夹。
用你最喜欢的FTP客户端软件设置该文件夹的权限为777。
然后把如下JavaScript代码复制到每个需要自适应图片的网页的头部:
如果你没有使用HTML5(在下一章会改用HTML5),想让页面通过标准验证,则需要追加type属性。所以script标签应如下所示:
切记这段JavaScript代码要放在页面头部(最好作为第一个脚本),因为它需要在页面加载完成之前,而且要在发出图片请求之前运行。下面是我们的示例网站头部加入该脚本后的结果:
把背景图片放在其他地方
过去,我通常将所有图片都放在一个名如images或img的文件夹中,不论是用做CSS背景的图片,还是通过标签插入的图片。但是在使用自适应图片方案时,建议将那些用于CSS的背景图片(或者那些你不想被缩放的图片)放在另一个目录。自适应图片方案默认为此创建的目录是assets。如果你不想缩放某张图片,把它丢进这个文件夹即可。如果你想将这类图片存在其他(或更多)文件夹中,则需要像下面这样修改.htaccess文件。
上面的代码设定了存在assets或bkg文件夹中的图片不会被缩放。反之,如果你想显式声明只允许某个特定文件夹中的图片被缩放,那么将设置规则中的感叹号去掉即可。
例如,如果我只想让网站根目录下名为andthewinnerisnt的文件夹中的图片被缩放,则修改后的代码如下所示:
这就是要设置的全部内容。检查是否能正确生成自适应图片的最简单办法,就是在网页中插入一张大图片,然后用手机访问这个页面。之后用FTP软件检查ai-cache文件夹中的内容,你应该可以看到一堆文件,以及使用屏幕尺寸断点命名的文件夹,如480(如下图):
Adaptive Images方案不仅限于静态网站,它也可以被用于内容管理系统,而且在JavaScript被禁用的情况下依然有效。自适应图片方案给我们提供了一种方法,可以根据屏幕尺寸提供完全不同的图片,为那些没有必要下载全尺寸大图的设备节省带宽。 3.7 流动网格布局和媒体查询的默契配合 在本章的前面,我们的导航链接在特定的视口宽度下回折成两行。我们可以使用媒体查询来修正这个问题。如果导航链接在1060像素下散得太开而在768像素下显示正常(我们之前的媒体查询做好了处理),我们就给这个视口范围设置一下字体样式:
你看到了,我们根据视口宽度来改变文字大小,结果就是这组导航链接在769像素到无穷大的视口中都会显示在一行。这是媒体查询和流动布局和谐共存的又一证据:媒体查询约束流动布局的变动范围,而流动布局则简化了从一组媒体查询样式过渡到另一组的改变过程。 3.8 CSS网格系统 人们对CSS网格系统/框架的看法褒贬不一,有人夸赞它,有人咒骂它。为了不收到满怀怨恨的邮件,我声明我保持中立。我能理解一些开发者认为网络系统是多余的,某些情况下还会产生一些冗余代码,但同时我也很欣赏它们在快速搭建界面布局上的价值。
下面是一些对响应式设计提供了不同程度支持的CSS框架:
Semantic (http://semantic.gs);
Skeleton (http://getskeleton.com);
Less Framework (http://lessframework.com);
1140 CSS Grid (http://cssgrid.net);
Columnal (http://www.columnal.com)。
这些框架中,我个人最喜欢Columnal网格系统,因为它有一套内置的集成了媒体查询的流动网格布局,而且它使用了与960.gs框架类似的CSS命名,960.gs框架是一套广为开发者和设计师所熟知的非常流行的固定宽度网格系统。
Alpha, Omega以及其他常用的网格类名
很多CSS网格系统都使用一些特定的CSS类来完成日常的布局工作。row和container类不言自明,但经常会有很多类需要说明。因此,时常查阅网格系统说明文档中有关这些类的说明,能让我们的工作更加轻松。例如,网格系统中常用的典型CSS类有alpha和omega——分别表示一行中的第一个和最后一个元素(alpha和omega类中会去除内边距或外边距),还有.col_x,其中x指的是该元素横跨合并的列数(如col_6表示横跨6列)。
使用网格系统快速搭建网站
假设我们还没有构建流动布局,也还没有编写任何媒体查询代码。我们手里只有And the winner isn’t…网站首页的PSD分层图,且被要求使用HTML和CSS尽可能快地搭建起网站的基本布局结构。我们来看看Columnal网格系统如何帮助我们快速实现这个目标。在PSD原始设计图中,很容易看出网站是16列布局。但Columnal网格系统最大只支持12列,所以我们将PSD中的16列改为12列。
下载Columnal网格系统的压缩包然后解压,将我们已有的页面复制一份,然后将页面头部的CSS文件链接从main.css改为columnal.css。使用Columnal系统创建页面结构的关键是给div添加正确的class。下面的代码是目前为止页面的完整标签结构:
首先,要将#wrapper div设置为其他所有元素的容器,所以为其追加.container类:
接着往下看,AND THE WINNER ISN'T是页面的第一行文字,所以为头部追加.row类:
网页logo虽然只是文字,嵌在一行里,但横跨12列。因此为其追加.col_12类:
紧接着一行是导航,所以为其追加一个.row类:
然后继续根据需要给元素追加.row和.col_x类。此处我将跳过后续的修改,因为我觉得重复这个过程可能会让你打盹。下面展示的代码就是修改后的完整页面标签。注意,修改中需要将奥斯卡图片挪到单独的一列中。另外还需要给#content和#sidebar外面包裹一层类名为.row的div。
还需要给custom.css文件中追加一些额外的CSS样式,该文件的内容如下:
在这些基本修改完成之后,在浏览器窗口中就能看到页面的基本结构已经搭好,且页面能随着浏览器视口的改变而缩放。
很明显还有很多细节工作要做(我知道,这样说太轻描淡写了),但如果你需要一种快速搭建响应式网站结构的方法,那么Columnal这样的CSS网格系统值得考虑。 3.9 小结 本章,我们学习了如何将死板的像素布局的网页结构改造成灵活的百分比布局的结构,学习了如何使用em来替代px创建更加灵活的文字版式,掌握了如何部署服务器端解决方案为不同尺寸的设备屏幕提供不同的图片,从而保证图片的响应性和流畅缩放。最后,我们试用了一套响应式CSS网格系统,它能让我们以最快的速度搭建起响应式网页结构。不过到目前为止,在我们追求响应式设计梦想的过程中,一直使用的是HTML 4.01标签。第1章我们提到过HTML5带给我们的一些好处。这些好处对响应式设计来说尤为重要和有意义,响应式设计的“移动优先”(mobile first)思想使它很适宜采纳最简洁、最有效和最具语义的代码。下一章,我们将认真学习HTML5,并修改页面标签以便发挥最新最酷的HTML规范的优势。