每天收到不少问题,遇到有代表性的我就多花些时间,给出一个比较详细的回答,希望这样可以帮助更多有类似问题的读者。
问题描述

前几天有一位读者在我们的论坛上发了一个帖子,希望制作这样的一个网页菜单,需要使用CSS来实现。它给出了一个图示:
图中给出了他的切片思路,他的问题是:“自适应高度的 , 上下的1和3两个部分用 img src, 中间部位“2”, 不知道怎样设置渐变色背景了,哪位大侠能给讲解一下呢, 谢谢了。”
问题原因
从他的配图,以及他的文字描述,可以看出,“表格布局”方式已经深深刻进这位读者的脑子里了,使用CSS的时候最重要的一点就是要忘掉表格布局的思路。所以从这个意义上说,有些从零开始的读者学起来反而容易一些。就像一个成年人学外语很难,而小孩则容易得多,因为母语已经深深刻在我们的脑子里面了。英语老师常说的一句话是:“Think in English”。
CSS布局的一个最重要的原则就是,一切以内容为出发点,这个例子显然是一个很典型的列表菜单,那么它的HTML就是一个 ul 列表。我们要做的就是如何让这个ul列表具有设计图中的背景。千万不要上来就把这个图用两刀坎成三块,然后在打算用HTML和CSS拼到一起,这个思路从根本上来说,就是典型的“表格布局后遗症”~~~~。这就等于我们在说:“I’ll give you some color see see ”。
基本方案
那么正确的CSS思路又是如何的呢?我们做任何事情都要有一些基本的思考方法,比如从“简单到复杂”、“从特殊到一般”等等,其实你上中学的时候。这些方法都是渗透在各门课程中的,现在想一想,都会觉得很有收获。
比如具体到这个问题,首先考虑,如果没有渐变色,我们应该怎么办?这里因为我没有原图,就自己绘制了一个简单的,但是道理和原图是完全一样的。第一步,先从最简单的开始起步,如下面图中的左图所示,这里不存在渐变地情况。
下面要解决的就是一个高度自适应的问题了,那么用CSS解决自适应高度或宽度的问题,很显然要用到“滑动门”技术了。要做滑动门就要要准备好两扇“门”了,很简单,如下图中,右边的两个图所示。他们就是左边的图切片得到的,一个去掉顶部,一个去掉底部,这样当它们叠在一起,根据内容高度上下错动时,就可以产生自适应高度的背景框了。
接下来,编写HTML:
1
2
3
4
5
6
7
8
9
10
| <div id="outer">
<div id="inner">
<ul>
<li>Artech</li>
<li>Web Dev</li>
<li>Web disign</li>
此处省略若干菜单项……
</ul>
</div>
</div> |
这里就是把ul列表的外面套了两层div,各自作为滑动门的一扇门。如果有人要较真,非要只套一层div,而把ul作为另一扇门,也是可以的,不过这里为了简单明了,用两层div。
下面设置CSS样式:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| #outer {
width:186px;
background:url(images/bottom.png) no-repeat bottom;
padding:0 0 35px 0;
}
#inner {
padding:35px 0 0 0 ;
margin:0;
background:url(images/top.png) no-repeat ;
}
ul {
margin:0 20px;
} |
解释一下代码:

1:外层div设置一个固定宽度,就是背景图像的宽度,然后设定背景图像,注意最后一个“bottom”,表示从这个盒子的最底端开始放置背景图像。
2:内层div使用另一个背景图像,默认情况下就是从顶端放置背景图像,在内外层盒子重叠的部分,内层的盒子会压住外层的盒子。
3:下面注意,两层盒子的padding设置,外层盒子设置下部的padding为35像素,内层盒子设置上部的padding为35像素,这样就把上下装饰图像部分都让出来,而不会遮挡住他们的了。
4:最后,把最里面的ul列表位置调整一下,就完成了。
效果如右图所示,其高度可以根据ul的高度自适应。当然如果如果太高了,以致于内外两层div的背景图像无法重叠了,就会产生裂缝了,那么一种方法是把背景图像做的高一些即可,另一种方法是外面再再多套一层div,设置一个竖直方向平铺的背景,用它来填补中间的缝隙。
渐变背景

下面考虑背景渐变的情况,例如上面部分(#inner)的背景图像变成右图所示的样子,而下面(#outer)部分的背景图不需要变化,可以发现,代码不需要做任何修改,结果仍然是正确的。
但是要注意的一点是,在这种情况下,这时列表的内容就不能太短了,如果过短的话,渐变还没有完成就直接到了下侧的颜色,如下图所示,接缝处就会不自然了。
增加纹理

这位读者的问题中给出的图还不仅仅是渐变的问题,而且增加了纹理,这就有点麻烦了。如果仍使用上面方法则是的缺陷的,如果纹理很细小,不太明显的话,上面的方法还可以凑合了,而如果纹理比较大,就会比较明显。
在这里为了突出这个问题,我特意做了一个很大的纹理效果,如右图所示。可以发现,在接缝处(红色框里)就很不自然了,上下两层背景图的圆形纹理无法正好对上。按照上面的方法,这个接缝无法避免的,因为高度会自适应,所以无法确定到底它们会在什么位置接缝。
那么是否有更好的办法呢?要实现即有渐变色,又有纹理的背景能够自然衔接,就必须要用到半透明的背景图了,这种半透明学名叫作“alpha透明”。我们知道,GIF格式的图像是不支持alpha透明的,也就是说,GIF图像中的各个像素,要么就彻底不透明,要么就彻地透明,而无法控制透明的程度。但是PNG格式的图像就支持alpha透明,可以控制各个像素的透明度,比如某个像素是30%透

因此我们就来考虑一下,如何使用这个特性来实现具有自然过渡的纹理背景。很显然,我们必须要把“渐变色”和“纹理”这两件事分配到两个图像中,并使渐变色地图像使用alpha透明,这样就可以自然过渡了。具体来说,上面的div#inner的背景图像仅负责渐变色和alpha透明,下面的div#outer负责纹理图案。
因此,首先解决 div#inner 的背景图像,把设计图改成左图所示的样子,灰白小方格交替的部分代表透明背景,在Phostoshop和Fireforks中,都是这样的。上侧的切片图像,取红色虚线以上部分。
注意,在导出的时候,在Fireworks中要选择PNG格式,要注意一点,PNG格式有 8位、24位和32位三种,由于这个图像中存在渐变色,8位格式产生的效果太差,无法使用,此外,24位的无法产生alpha透明,因此,这里选择32位的PNG格式导出(32位:24位=4字节:3字节,多出来的一个字节就是就是记录透明度的)。

接下来,div#outer的背景简单一些了,把渐变层去掉,只保留纹理层,也不需要透明,直接到处即可。
这样,代码不需要调整,产生的效果就很完美了,在IE7、IE8和Firefox中,都可以如图所示显示。

由于 IE 6 尽管支持PNG格式的图像,但是不支持PNG的Alpha属性,因此上面的页面在IE6中,不能正确先显示。要在IE6种也可以正常显示,需要借助于IE6 的 AlphaImageLoader滤镜,也同样可实现,这篇文章已经太长了,这里就不再详细介绍这个细节了,读者可以参考《CSS设计彻底研究》的129页“柔边阴影”效果在IE6中的实现方法。但是要注意,我自己实验的结果是 AlphaImageLoader滤镜只对32位PNG图支持alpha透明,8位的PNG不行。
再给大家一个把alpha透明使用得登峰造极的例子:《你问我答(21)——破解Kai Laborenz的神秘海底世界》,这个例子是一个德国人做的CSS禅意花园的例子,太酷了。
如果你对“CSS滑动门技术”还比较陌生:
1:看一下这篇文章,你问我答(11)——CSS滑动门技术的简单应用 。
2:滑动门技术非常有用,《CSS设计彻底研究》书里面在很多案例中用到了滑动门技术,表面上看起来完全不同的案例,实际上具有相同的技术本质,很有趣,有兴趣的读者可以看一看。
最后给出这个例子的源文件,感兴趣的读者可以点击这里下载。
思考题
1:使用CSS布局,最核心的思想是什么?
2:你能否列举出一些你使用过的CSS滑动门技术的应用案例?
3:你是否能举出一些例子,说明“表面上看起来完全不同,本质上却是相同的”这个道理。
===============================================
[补充说明]
这篇文章贴出来一天之后,我发现我的解决方案还是有些问题的,虽然这个方法可以解决文中所说的读者问题中给出的图的要求,但是我给出的最后一个结果图,实际上和中间我画了一个红色方框的那个图相比,二者并不完全等价。区别在于,前者是在一个渐变色的基础上增加了纹理,后者在固定色上增加纹理后,又覆盖了一层渐变色,这两个效果是不一样的。因此用这种方法能够实现的效果,对设计效果时的操作步骤和顺序是有一定要求的,符合后者操作顺序的才能够拆到两个图像中,再叠加产生最终的效果。文章顶端的那个黑色图,是可以用这种方法做到的,因为渐变色是整个压在纹理上面的,而不是纹理压着渐变色。