CSS Sprites
What is CSS Sprites
Web 是个很神奇的地方,很多时候会给你“OMG! The old things is new again!”的错觉。
比如初衷是数据表格的 <table/> 标签,会被用来取得更有弹性的布局效果;
比如 <h1/> 还是 <p class="title"/> 作为网站的标题,都可以成为争辩的话题;
比如 <canvas/> 和 Flash 的比较。
CSS Sprites 也是如此。
我们先把 CSS Sprites 这名字拆分一下,CSS 和 Sprites,CSS 么大家都知道,Cascading Styles Sheets,层叠样式表。 只不过它的层叠算法有的时候很奇怪,代码结构看着也不想表。但这不妨碍你把它去繁取简, 理解为样式(Style)。事实上在 Flex 里头,Adobe 就是这么做的。
而 Sprites 呢?查下字典,意为小精灵。在计算机图形的世界,
这个词的意思
是一张二维的图片,用来嵌入到场景当中。通常它是由多张小图拼成的。同样,在 ActionScript 里面,
有个 Class 唤作
Sprite。
采取这种做法的原因,跟现在一样,是
速度。
把一张大图载入内存,每次重绘的时候只改变它的显示位置,比每次重绘都把图片数据搞来搞去要经济很多。
而两个词结合起来,就是本文要说的,使用 CSS 应用古老的 Sprites 技术。在开始之前,我们先了解一下为什么。
在 YUI 的 Yslow 网站优化工具评分标准里头,有一项重要的评分标准是
减少资源请求次数。
网页上各种菜单的图标,布局里的背景图片,
按钮等等,可以将这些图片弄到一张大图上,再在 CSS 中通过 background-position 制定需要显示的图片位置。
也就是让浏览器自己去调整显示结果,取得“抠”图的效果。它是一项网页优化的技术。
是否要使用这项技术已经不是个问题了,它无处不在。
【】有趣的是,在笔者写本文的时候, CSS-Tricks 也发了篇博文。在“为什么”的部分说的比较清楚。
CSS
CSS 的中文意思,前面介绍过了。对于它的意义、简单语法等,可以参考我的 HTML & CSS。
先说基本,background 属性的语法。示例声明(来自鹿木)。
这是一个简写。在 CSS 中这种简写很多,使用最多的就是 padding 和 margin 的语法。
background: #000 url("/picture/3003_m.jpg") no-repeat scroll center top;
接着说背景声明。详细属性分为:
background-color各种颜色;background-image背景图,可以是url(),也可以是 BASE64 编码的图片;background-repeat重复模式,no-repeat, repeat, repeat-x, repeat-ybackground-attachment是否跟随页面滚动滚动,scroll, fixedbackground-position
上述简写便是以这个顺序。详细语法可以参考 w3school。
我们要讲的重头是 background-position。它的值的模式为 xpos ypos。
xpos 可以是 left, center, right, x% 或者实际的像素单位的坐标;
ypos 则是 top, middle, bottom, y% 或者实际坐标。
了解这个之后,我们就可以做个示例了。
Demo 1
这个示例来自于 A List Apart。 我们要做的事情就是,把上一节中的图片作 Sprites,做一个看起来还不错的导航栏。最终效果如右图所示:
先把 HTML 搞起来。通常写 HTML 的原则有两个:简介其一、语义化其二。把语义化作为重要准则的原因也有两个, SEO 和可用性。因为搜索引擎的爬虫和屏幕阅读器都不会识别通过 CSS 后加的图片、文字,它们只认 HTML。 代码如右图下方所示。
接下来,配上 CSS。首先,将这个菜单的高宽设置为背景图大小,配上背景图;把 <li/> 的默认属性重置掉。
关于 position 的部分说起来容易话长,
对这方面不熟的童鞋建议看看《十步走学会 CSS Position》。
简单地说这里的代码就是让每个菜单条目使用绝对定位,方便布置鼠标悬停的效果。
#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;
}
#panel1b {left: 0; width: 95px;}
#panel2b {left: 96px; width: 75px;}
#panel3b {left: 172px; width: 110px;}
#panel4b {left: 283px; width: 117px;}
布置好菜单之后,通过 a: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;
}
这个例子应用 CSS Sprites 的方式有点特别,经典应用方式是背景图片只指定一次,
需要的时候再去改 background-position。这个做法是个 Workaround,而非 Fix,主要原因是
IE6 里头的一个 bug:如果 IE 的缓存控制设置为不缓存,每次都去服务器取资源的话,
背景图会闪(解决方式)。
下面,我们通过求是设计的博客,来做一个更为纯粹的示例。
Demo 2 - Blog of Qiushi Design
求是博客的导航栏,是一个比上例更为经典的 CSS Sprites 应用。 它的背景图片 是一张包括常态、鼠标悬停之后、disable 之后三个效果的图片,并对应求是设计各个模块。
HTML 如前文所述,遵循简明扼要、语义化的规则。注意这里虽然最终效果里头并不需要文字,
但是依然写明,并通过 text-indent 将它设为不可见。实际的代码中,还含有 title、target
等属性。title 用来鼠标悬停时提示说明,target="_blank" 则告诉浏览器要新开一个窗口或者标签。
此处为了简明,略去了。有 Firebug 的童鞋,可以查看右侧的示例元素。
关于 #menu li a 的 CSS 声明:
#menu li a {
background:url("http://blog.qiushid.com/wp-content/themes/qiushid/images/navitems.gif") no-repeat scroll 0 -468px transparent;
display:block;
float:left;
height:26px;
text-indent:-999em;
width:85px;
}
因为每个模块对应的图标都不一样,所以实际位置通过 #menu li a.class 指定。以首页(home)为例:
#menu li.home a {
background-position: 0 -182px;
}
#menu li.home a:hover {
background-position: 0 0;
}
其余代码可以参考求是博客的 CSS 文件。
Demo 3 - (Blink) With JavaScript
A List Apart 在几年后又发一文,
加入了 JavaScript 的部分。
使用 jQuery 的 .fadeIn(), slideDown() 等 UI 方面的方法,来模拟淡入淡出等效果。
原文的示例代码不很清爽,就不再转用。下面是参考
hover 文档
做的求是博客导航栏的基础上制作的示例。
有没有 jQuery is the new <blink/> 的错觉?廉价的特效,廉价的代码:
$("#qiushid-menu").find("a").hover(function() {
$(this).fadeOut(500, function() {
$(this).fadeIn(500);
});
}, function() {
$(this).fadeIn(100);
});
这么做的问题也很明显。如果用户玩导航栏,在那上面划来划去,之前的淡入淡出效果并不会消失,
而是慢慢做完。后果就是,鼠标甩过去之后导航栏闪个不停。修正的方法是每次
mouseenter、mouseleave 的时候判断是否正在特效进行中,如果是就清除。
下面的例子思路跟这个是一样的,所以这个例子的方法我就不再说了。
Demo 4 - (Marquee) With JavaScript
下面,我们做个高级一点的,jQuery 版本的 <marquee/>。
我们知道,求是设计的导航栏的背景图是在鼠标悬停(hover)的时候,通过调整
background-position 得到效果。现在呢,就要用 JavaScript 让这个过程慢一点。
实际执行代码请查看本页面源代码末尾的 JavaScript 部分,这里就伪代码介绍一下。
var yPosMap = { // 最独立的办法自然是从 CSS 里头取,但是感觉费力
home: [-182, 0],
camp: [-260, -78],
bookstore: [-286, -104],
microblog: [-338, -156],
network: [-312, -130]
};
var timerMap = {}; // 每个动画的定时器应该是独立的
$marqueemenu.find('a').hover(function() {
animate(this, 'show');
}, function() {
animate(this, 'hide');
});
function animate(block, mode) {
clearTimers(clsName); // 清除正在进行的动画
if (reachTargetPosition()) {
reset();
return; // 已经到了
}
timerMap[clsName] = setInterval(function() {
pos = getYPos($a);
/*
* 最关键的语句,CSS Sprites 就是这么回事:
*/
$a.css('background-position', '0 '+(pos+delta)+'px');
if (reachTargetPostion()) {
clearInterval(timerMap[clsName]);
reset();
}
}, 10);
}
Demo 5 - jQuery.animate()
我们的示例到这还没结束。
jQuery 自带了个 .animate(),但是对个别 CSS
属性的动画支持并不好。有达人做了个
jQuery Bg Pos 补丁
让 jQuery 可以以 background-position 做动画。实际效果可以看右方示例。
有没有觉得示例四很有重复发明轮子的嫌疑?
详细内容,不妨移步 Snook 的博文 和他的示例。这里简单讲述一下用法。
首先要在页面上引入 jQuery 与 jQuery.bgPos 插件。后者的内容并不多, 不妨把它并入你的网站的主 JavaScript 文件里头去。
lt;script type="text/javascript" src="jquery-1.4.2.min.js">lt;/script> lt;script type="text/javascript" src="jquery.bgpos.js">lt;/script>
然后是使用:
$('#snook-menu').find('a')
.css( {backgroundPosition: "-20px 35px"} )
.mouseover(function(){
$(this).stop().animate({backgroundPosition:"(-20px 94px)"},
{duration:500})
})
.mouseout(function(){
$(this).stop().animate({backgroundPosition:"(40px 35px)"},
{duration:200, complete:function(){
$(this).css({backgroundPosition: "-20px 35px"})
}})
});
为需要特效的空间注册鼠标移入移出的响应函数,也可以使用前面示例的 .hover() 简写。
重点代码是 .animate() 的参数部分。第一个就是特效的结束值,.animate()
会根据这个值计算应该如何动画,详细的过程与示例四的 animate() 函数相仿。
第二个({duration:200, complete:function(){}})是其他参数,前者是动画的持续时间,
后者是当动画完成之后的回调。示例中的回调将控件重置,主要是针对动画结束背景图位置有偏差的情况。
Summary
看完本文,了解了这四个例子之后,我想你对 CSS Sprites 应该有很清楚的认知了。
最后广告一下:做前端开发,目前为止最好用的工具依然是 Firefox + Firebug 组合。 Google Chrome 的开发者工具也已经越来越完善,但是小细节总有不愉处,可能是我习惯了前者的缘故。 IE9 如今成了 Underdog,但是也在慢慢赶上。此外,最好整个 IE Tester。 当然,如果你的机器上还有 IE6,直接测也行。不过,
你的电脑上还有 IE6?