转自:http://www.liquidroc.name/post/my_view_on_css_sprites.html
《CSS Sprites: Image Slicing’s Kiss of Death 》是一篇旧文,于2004年3月5日发表于alistapart的第173期 ,作者是大名鼎鼎的CSS禅意花园 的园主Dave Shea 。之所以把它翻出来,是因为最近在看的关于页面优化的一些文章,都不约而同地提到了这项技术,温故知新~
有趣的是,关于CSS Sprites这个名称的中文翻译一直没有定论,如果依照词典直译成样式表精灵 ,恐怕没有几个人会同意这样的叫法。就好像有人把其后的image slicing直译成图像限制一样,会让人摸不着头脑~
和 直译不同,也有人把它意译为CSS图像拼合,的确,整个css sprites的图像制作过程就是一种图像拼合,将页面所需的修饰性图像全部打包在一张图像中。但是对于如何用css层面上的positio定位来使用这 个拼合的图像,这个名称就显得名不副实了。我倒是认为,这种css控制背景图像位置的技术和3D软件中的UVW贴图 有神似的地方,暂且就叫它样式表贴图定位吧。说到底,叫什么不重要,了解它才是王道……
---------------------------------------- 开始正文的分割线 ----------------------------------------
原文如下:
回到电视游戏盛行的年代(我们这里讲的是8位游戏机的辉煌时期),那时的图像非常的简单,位图制成的二维形象和背景场景独立分开,这一点很像现在的像素艺术。成百上千的小图像成为构建整个游戏的视觉基石。
当游戏慢慢变得复杂,技术的提升使得管理众多的小图片成为可能,游戏画面变得流畅自然起来。其中一点就是,贴图定位技术的引入,在代码控制下抽出所需的图片,这些图片在主图层中被映射于各自位置的之上,并有选择性地将其呈现于屏幕上。
这和Web有什么关系?
三十年河东三十年河西,虽然3D游戏的兴起让贴图技术日落西山,但同时兴起的2D手机游戏让贴图技术有了焕发新生的可能。现在,通过一点点的数学计算以及完备的css技术,我们将这个基本概念应用于整个web设计之中。
确切地说,我们将用CSS方案取代陈旧的图像切片(加上JavaScript)技术。更进一步的阐述是:建立一种网格图像,采用一个方法将网格中的每一个独立小单元抽出。我们可以在这个主图层文件中放置按钮、导航以及一切元素,连同它们“初始”和“之后”的链接状态。
样式表贴图定位如何运作?
在css应用上我们只需要一点创造性思维,这就是全部了。
让我们从主图层开始。将其分成四等分矩形,在主图层图像 中 你将看到“初始”图像在上,“之后”的悬停状态在其下。现在4个链接之间并没有清晰的界线,现在想象一下每一块文字对应一个链接。(为了简单起见,在其后 的文章中,我们依然把链接图像叫做“初始”图像,悬停状态下叫“之后”。对诸如:active,:focus以及:visited的链接状态,这里不展开 讨论。)
熟悉Petr Stanicek(Pixy)快速翻转图像 方法的朋友也许已经猜出我们将要做什么。感谢Pixy的这个样例,我们接下来的讨论都是基于它的基础功能。但是别高兴的太早了~
先来看HTML部分。每一个优秀的css技巧都力图在最简洁的代码块上添加视觉效果。本方法也不例外。
<ul id="skyline">
<li id="panel1b"><a href="#1"></a></li>
<li id="panel2b"><a href="#2"></a></li>
<li id="panel3b"><a href="#3"></a></li>
<li id="panel4b"><a href="#4"></a></li>
</ul>
以上的代码将是我们这个例子的基础。简单的标记将使得代码在老旧的、对css支持差的浏览器中也得以运行,这也是行业中的一个良好趋势。对于我们力争要达到的目的,这是个很不错的主意。(我们在这里忽略了链接中的文字,你可以使用你所钟爱的图像替代法 来隐藏最后添加的文字。)
CSS的运用
有 了这些编写好的块元素,我们可以开始着手css。在开始之前有一点需要注意——由于IE的一个bug,我们需要让“之后”图像紧贴在“初始”图像之后,而 不是一张取代另一张的做法。尽管当我们把它们排成一行时,并没有什么真正的视觉差别,但这样会避免一个我们不想见到的加载“闪烁(flicker)”。
#skyline {
width: 400px; height: 200px;
background: url(test-3.jpg);
margin: 10px auto; padding: 0;
position: relative;
}
#skyline li {
margin: 0; padding: 0; list-style: none;
position: absolute; top: 0;
}
#skyline li, #skyline a {
height: 200px; display: block;
}
和想象中不同,我们没有把“初始”图像指定给这些链接,而是用<ul>代替,后面你将看到这是为什么。
css的其他部分,分别给#skyline和列表项定义了尺寸、范围,给列表项定位,以及去除了不想要看到的默认列表样式中的圆点。
我们将链接作为一个透明的空层,通过父层级<li>来给它们定位。如果我们跳过<li>,直接定位这些链接元素,在老的浏览器上将会出现错误,所以我们不能这么做。
定位链接
<li> 元素被绝对定位,为什么它们没有出现在浏览器窗口的顶端?那是因为对于绝对定位元素有一个奇异(quirky),但却很使用的特性(roc:我不觉得有什 么奇异的地方),绝对定位元素会以离它们最近的父级定位元素为基点,而不是浏览器窗口这个根元素。因为我们给#skyline定义了position: relative;属性,所以我们可以将<li>元素绝对定位于#skyline的左上角。
#panel1b {left: 0; width: 95px;}
#panel2b {left: 96px; width: 75px;}
#panel3b {left: 172px; width: 110px;}
#panel4b {left: 283px; width: 117px;}
因 此#panel1不再独占水平位置,#panel2b基于#skyline水平向左96px,其他的也类似。我们将链接定义为display: block;使其高度和<li>相同,这样我们就可以填充这些<li>了。这正是我们想要得到的效果。
现在我们有了一个链接效果的基础图片,但悬停状态还没有设置。查看样例 ,加上边框 也许可以看得更清楚些。
悬停
在过去,为悬停状态翻转一张新图片我们需要使用一些JavaScript。现在我们将所有的集中在一张图片上,我们需要一种有选择的抽出各种状态的方法。
如果我们在:hover状态下应用整张贴图,不附加其他值,我们只能看到左上角——这不是我们想要得到的,虽然我们的确希望依照链接的范围裁剪。我们需要以某种方式移动这个贴图的位置。
#panel1b a:hover {
background: transparent url(test-3.jpg)
0 -200px no-repeat;
}
#panel2b a:hover {
background: transparent url(test-3.jpg)
-96px -200px no-repeat;
}
#panel3b a:hover {
background: transparent url(test-3.jpg)
-172px -200px no-repeat;
}
#panel4b a:hover {
background: transparent url(test-3.jpg)
-283px -200px no-repeat;
}
这些像素位置值要如何放置?我来解释一下:第一个值用来设置水平位置,第二个用来设置垂直位置。
每 一个垂直位置都是相同的;由于整张贴图的高度是400px,初始和悬停各占据上下一半,所以我们将很容易的计算出高度值。想要将整个背景贴图向上移动 200px,我们需要给一个数量相同的负值。以这个链接的顶端或者说0高度作为原点,背景图将在这个原点200px之上,也就相当于说,我们将原点设在了 -200px上。
同样的,如果每一个链接的左边界都是0,我们需依据<li>的宽度,给背景贴图一个偏移值,就像前面我们做过 的那样。所以,第一个链接不需要偏移,因为它就是水平位置的原点。第二个链接需要的偏移值是第一个链接的宽度,第三个链接的偏移值是前两个宽度相加,而最 后一个链接的偏移值则是之前三个宽度的综合。
这个过程解析起来有点绕,不是吗?但如果亲手去调试一下,你将很快理解它们的工作原理,一旦你对此熟悉起来,它们看上去就不那么难搞了。
看看我们得到了什么 ,一个单一图片翻转转化成为了一个无序列表。
按钮
在上面的例子里,并不是出于特别的原因,我们将链接彼此相邻。图像地图也许在有些地方比较合适,但把它们分开作为各自独立的按钮又会如何?我们可以给其添加边框和边距,
实 际上,积木已经搭好了。我们其实不比对现有的代码大动干戈;最大的变动就是需要创建一个新的背景图片,而不是像上个例子中让链接彼此相连。由于我们不能 给<ul>应用一个整体的原始背景图,我们以<li>代替,并且像上面讨论过的那样设置悬停状态下的偏移值。
使用一张合适的图片 以及给<li>之间添加些许边距,我们就得到了这些按钮 。
在这个例子里,我们添加了1px的边框,链接的最终宽度增加,这会影响到偏移值;我们需要增加2px的偏移值以抵消这一影响。
不规则形状
到目前为止我们都把精力放在不重叠的矩形形状上。如何将大量的复杂图像切片处理的就像是用Fireworks和ImageReady输出的那样简单呢?别紧张,我们同样可以做到。
开始的部分,我们采用第一个例子中同样的方法。给<ul>设置背景图,然后取消列表项的项目符号,以及宽度等其他定义。显著的区别在于我们要给这些<li>定位;目的就是让这张图片 上每一个图形被包裹住。
因为我们相对于<ul>的左上角应用绝对定位,我们得以随心所欲地将链接精确放置。现在只剩下要处理悬停状态 了。
值得注意的是,在这个案例中,仅仅设置初始和悬停状态图片是不够的。因为它们彼此重叠,所以在只是设置了一个悬停状态之后,块和块之间在悬停下会彼此露馅。事实上,它显示的块就是这个链接的边框范围。(这里 可以看得很清楚)
那么如何避免呢?解决方法是添加第二种悬停状态图片背景,并且仔细的选择哪些图形需要出现。重新制作的贴图背景 被分割成在第一个悬停状态下紫色和蓝色图形出现,绿色、橙色和黄色图形在第二个悬停状态下出现。这样就避免了每一个图形在悬停状态下露出旁边的图形。大功告成 。
优缺点
最后的几点关键。我们新的样式表贴图定位法在绝大部分的现代浏览器运作的很好。(Roc:这里的观点都是对的,但文本诞生的时间和现在——相对于浏览器升级速度——相去甚远,Opera6和7的版本就像是石器时代那么遥远了,所以这一段可以跳过 )Opera 6是个例外,在悬停状态下,它无法应用背景图片。为什么?我们还不能确定,但它意味着我们的悬停不起作用了。链接依然有效,如果它们都标记正确的话,最终 的结果就是在Opera 6下它呈现出一个静态的,但可用的图片地图。我们寄希望于即将推出的Opera 7。
另一个困扰是FIR 。在极少数情况下,用户禁止了下载图片,但依然保持CSS样式,那么在页面上原来由图片占据的地方就会出现一片空白。链接虽然仍在,但却是不可见的。截至目前,还没有很好的解决之道。
还 有就是文件的大小。有一种观点就是认为双倍尺寸的图片文件一定大过这些单一切片的集合,因为图片涵盖的尺寸比较大。所有的图片格式都有一个极限值(例如 1x1px的gif图片总是在50bytes左右),切片越多,这种极限值就越快的堆积。此外,一张大的gif贴图只需要一个颜色表(color table),如果是切片,每一个切片图都需要附加一个自己的颜色表。
最后,不要忘了我们的标记是干净漂亮的,这是其优势所在。HTML列表完美,并且图片替代文本链接技术对于屏幕浏览者可用性更高。替换贴图里图形更是简单无比,因为我们只用一个css文件就可以控制尺寸大小和定位,所有的图形也都在一张大图之中。
---------------------------------------- 翻译结束的分割线 ----------------------------------------
感谢耐心看完我拙劣的翻译。
在 css这个领域,04年的文章完全可以称得上是古董级,但这篇依然没有失去它的价值,尽管现在看来CSS Sprites在技术并没有太多的超前性,但是完美应用这项技术的网站并没有许多。因为,CSS Sprites这项css技术的优势根本在于减少了服务器请求次数,压缩了带宽开支,越是大型的网站越是看重这一点。
另外有一点对于使用Dreamweaver CS4 Beta的朋友需要注意,这个测试版本很显然它的可视化引擎不能正确处理背景位置,在可视化下编辑CSS Sprites,你会看到背景图片到处乱飞。CS3的正式版中没有这个问题。
最后,有一个好消息,Dave Shea 时隔4年多推出了他的又一篇力作:CSS Sprites2 - It's JavaScript Time
关注样式表贴图定位技术的朋友一定不能错过。