深入浅出CSS3:background-clip,background-origin和border-image教程
已兼容IE!之前不能兼容IE,给您的阅读带来不便十分抱歉,Sorry
一.这篇博客的初衷
最近在准备一个下学期参加比赛的概念网站,希望能用到CSS3的新特性,比如background的background-clip或background-origin或border-image。但仅仅是依靠w3schools上的文档和说明发现很多臆想中的效果都实现不了。于是尝试在百度中搜素,结果资料也是少的可怜,反复转载的只有一篇来自怿飞博客的《background-clip与background-origin 的一则运用》
,但毕竟是几年前的文章,其中的部分内容已经不适用于现在的标准了,其他的搜索结果也不是很理想于是乎只好自己动手丰衣足食。花了一至了两天把这个内容消化。同时把要点和精总结在这篇博客中,给自己和大家做一个参考。
二.从传统的多图像背景技术谈起
在CSS3之前,2.0也好,2.1也罢。要想给一个块级元素添加圆角或是阴影,又或是一个比较复杂的背景图像(比如多边形,多纹路)都是相当困难的一件事。在这里先列举两个例子,有关如何组织复杂的背景和阴影效果。一方面是为了照顾一些入门web的朋友,另一方面也为了和后面这两个技术的应用做一个对比,做一个承上启下的作用。熟练的朋友可以跳过这一节,直接从下一节开始阅读
1.请大家先看这样一个效果:注意文字的背景,大家有没有主意如何实现?一个最普通不过的想法是,我就按我需要的文字的长度,用photoshop做一个包含左右半圆形状的完整一体图片。但问题是,如果我文字的长度需要更改,变得更长或者更短时应该怎么办?为了不影响美观,我们又不得不把图片放回photoshop中返工?虽然不排除这是一种办法。但要是有非常多的页面和框背景需要用到这个图片,那工作量会是非常巨大的。很明显这是得不偿失的。于是我们会想,手动的让图片适应文字麻烦,要是能让图片自动能适应文字,是绝对的事半功倍。那究竟有没有这样一种方法呢?
答案是肯定的(在这里我们简略的谈一下关键思路,具体代码的实现就不深究了),为了实现这个效果,通常我们只需要三幅图:和
和
。具体原理如图所示:
相信看到图之后有朋友应该有不少已经心领神会了:我们在原来唯一一个用来存放文字的div中,又插入了三个div:div#left,div#center,div#right。其中#left和#right分别用来存放半圆形的左右边缘且固定宽度,而中间的#center用来存放文字,背景采用repeat-x定位,且不定位宽度,再通过其它css设置让它能收缩紧贴内容区域(content),这样就能随着文字的长短而自如伸缩了。最后通过position或display属性让#lef,#center,#right实现无缝隙的拼接,同时让最外层的div#content也收缩紧贴它包裹的三个div,然后——大功告成。
这个技术的关键在于如何让存放文字的div#center的伸缩引起外部整个div#content的的伸缩,且保持#left,#center和#right的布局不被破坏(要无缝才能让人感觉一体,感觉真是)……当然这不属于我们今天的讨论范围,有兴趣的朋友可以参考我之前的一篇《深入理解css盒子模型》。这样就完成了自动伸缩功能。精简的代码如下:
< div class =”content” style =”height:32px;” > /*这个是伸缩的*/ < div class =”left” style”width:15px; height:32px;” ></ div > < div class =”center” style =”height:32px;” > 这里存放文字内容 </ div > /*这个也是可以伸缩的*/ < div class =”right” style =”width:15px; height:32px;” ></ div > </ div >
但至于如何才能使最外层的容器内收缩紧贴内部div,使div#center自动伸缩,就不在本课的范围之内,大家可以百度,也可以参照我上一篇文字《深入理解box盒子模型》。
例子2.我们先看一个jQuery插件fancybox的阴影效果:
这是模仿MacOS的效果的一个插件,由于整张图片太大,我只是截取了一个角下来。大家需要注意的重点是白色边框的外边缘的阴影效果(可能不是很明显)——参照上面的例子,您觉得阴影效果是如何实现的?
对,还是用九个盒子拼成的(可能这里有朋友会想到滑动门技术,的确这也是实现阴影的方法之一,但为了承上,这里暂且不介绍),一个盒子装的是中间的照片,另外八个盒子分别装东,西,南,北,东南,西南,东北,西八个方向的阴影。这个不用画图,大家应该可以想象出来吧。但这个例子又有不同——这一次有九个盒子,要是按上一个例子一样,在一容中放九个盒子,再通过浮动,定位属性等来实现他们无缝拼接,也会是相当繁琐的一事。因为要考虑每一个盒子周围的盒子在自己伸缩时也能适当的做一些调整,不至于出现错位和缝隙。所以的这次的做法是将放阴影的八个盒子用绝对定位固定在中间的盒子的八个方向上,这样以中心盒子为中心,无论它怎么放大还是缩小,周围八个盒子都死死的附属着它,就再也不用考虑自己周围的盒子有错位之类的问题。图例如下:
再看看实际的定位是不是这样定位的呢,看看从firebug下的截图:
再看看其中一个fancybox-bg-n的具体css:
三,background-clip,background-origin入门
我想说的是,上面的两个例子总结起来只有两个字:麻烦。背景明明只关一个div的事,结果硬是要n多个不相关的div来辅助完成效果——background-clip,backgrou nd-origin便是我们的救星。要知道CSS3的任何新属性不是W3C人吃饱了撑的没事找事想出来来的,它的出现必然性必定是经过详细验证的且有它必要的价值。相信当您阅读完这一节的内容后,你就会开始学会如何用新技术摆脱那些陈旧繁琐的步有三个属性来就不复杂的效果了。
在开始之前,先做几点说明,请看:
首先我们要引入几个概念(呃,这是我自己翻译的,不知道对不对,可能孤陋寡闻了,请朋友们见谅):内容边缘(content edge),内边距边缘(padding edge),边框边缘(border edge),外边距边缘(margin edge)。为了理解,我们可以把内边距,边框,外边距看成一个环形的容器,并且每个环形容器都只有两条边,一条冲内,一条冲外(内和外也是相对的,好比外边距的外边就是边框的内边),于是我们把冲外(outer)的那条边称为这个环形的边缘(edge)。当然因为内容区域是“实心”的,所以不存在内外的问题。
origin的翻译过来时原始的意思。顾名思义,所以background-origin是用来决定图片的原始起始位置。它有三个可选值content-box,padding-box,border-box(background-origin如果写在css中只有Opera浏览器可以识别,如果希望在火狐或者chrome或Safari中使用,要使用它们浏览器的私有属性-moz-background-origin(Firefox),-webkit-background-origin(chrome,safari),并且对应的值是content,padding,border,省略了-box),即你可以选择背景图片是从内容区域开始显示,还是内边距,还是边框。
举个例子,我们有一个div,关键在定位它的背景图片,为了让演示的效果更明显,我们让它从左上角开始定位,position:left top并且no-repeat。并且内容区于图片大小一致,边框和内边距都设置为30像素宽。OK,
在没有加入background-origin之前的代码为:
.border { background : url("images/qwqw_s.jpg") ; background-repeat : no-repeat ; background-position : left top ; border-width : 30px ; border-style : dashed ; border-color : red ; width : 180px ; height : 254px ; padding : 30px ; margin : 0 auto ; }
设置background-origin之后的效果图:
因为是在火狐下做的测试,所以-moz-background-origin代替background-origin,相应的属性所以也去掉了-box
效果和作用已经一目了然了:我们看到,当background-origin的值为content-box时,首先会让背景图片的左上角和内容边缘左上角对齐,padding-box时,则会让背景图片的左上角和内边距的左上角对齐。以此类推。可见background-origin的值的确是决定背景图片开始从哪个区域开始显示。但话说回来,如果我没有设置任何的background-origin属性的话,它默认的起始位置在哪呢?这里就不演示了,但有必要记住——padding。
有一点要十分的注意:如果背景不是no-repeat的话,这个属性是无效的。它会从border-box区域开始显示,这一点很重要。
background-origin属性就先介绍到这里。应该不难吧。下面继续介绍background-clip。之所以没有把这两个属性分为两节分别介绍,因为实战的经验告诉我这两个属性应该是相互搭配才能相得益彰。
clip原意为裁剪,截取。同样顾名思义,background-origin的作用为将背景图片做适当的裁剪,以适应需要。当然这里并不是真正意义上的把图片给裁剪了,而是根据属性值。把图片的某些部位做适当的隐蔽。background-clip与origin的可选择一样,也是有content-box,padding-box,border-box(要注意在火狐和Chrome和Safari中,需要使用私有属性,加上-mox-和-webkit-,这里就不赘述了,参考解释background-clip的内容)。怎样个剪裁法呢。根据你设置的盒子部位,那么图片在这个部位的外边缘以外的部分都会不可见。举个具体例子,图片起始位置和上面的例子一样,比如是从border-box开始,但我background-clip设置的值是content-box,在content之外,也就是border-box内,padding-box内的图片内容统统不可见。尽管你是让图片从边框开始显示。实例如下
.border { background : url("images/qwqw_s.jpg") black ; background-repeat : no-repeat ; background-position : left top ; border-width : 30px ; border-style : dashed ; border-color : red ; background-clip : content-box ; background-origin : border-box ; -moz-background-clip : content ; -moz-background-origin : border ; -webkit-background-clip : content ; -webkit-background-origin : border ; width : 180px ; height : 254px ; padding : 30px ; margin : 0 auto ; }
![](http://pic002.cnblogs.com/images/2011/138155/2011010918173165.png)
正如以上所说,我们可以看见虽然图片是从顶着边框的左上角进行定位,但是裁剪属性background-clip的属性是设置为content-box,所以只有content区域的内容看得见,也就是只要是在content之外的图片内容都被隐蔽掉了。
我之所以要在不同浏览器下进行测试(Firefox/3.6.3,Google:7.0.517.24,Opera/9.80,Safari:5.0.1),答案也在图上,我们看到在火狐下的结果和其他浏览器结果竟然不一样。明明background-clip设置的属性是content-box,但却没有图片的任何部位被屏蔽。我想说的是:在火狐下-moz-background-clip属性是没有content这一值的(但是padding和border还是有的),在firebug中可以看到,-moz-background-clip的值直接是border,当你强行改为content时,这条属性会直接从bug中消失。当然这只是在3.6.3版本下的结果,至于在4.0版本中表现如何(虽然只是beta版)。可以自行测试
四.实战
在学习了基本background-clip和background-origin用法和原理之后,我们将通过操作一个实际的例子,来加深我们的学习:
这个圆角背景是由三部分组成:和
和
(呃,其实是从Webqq2.0网站上撬下来的,就是上面的工具条……但是作为教学用,应该无伤大雅吧……),思路和开篇的那个黑背景制作过程是一样的,左右固定,中间窄的repeat-x。但是这会不用插入三个div,又要设置float,又要设置display考虑布局那么麻烦。
步骤一:
先搭建一个框架出来,给一个布局。不着急把图片插入进去。主意看以下代码,有几点需要说明的:因为仅需在盒子左右两侧插入背景,且恰好为左右要插入图片的宽度(如果不恰好为那么宽呢?那么不会成功的,因为css3中还没有属性能控制图片在边框中的定位,你会想不是有background-position属性吗?要注意那个是控制图片在整个盒子中的定位。还是不信的话你可以亲自试试);还有就是padding的值也是可以不用设置的,是为了与上面的例子尽力保持一致,方便大家对比学习 margin也是为了让盒子居中而已,可以忽略;最后border-style和border-color也是方便大家理解布局才添加上去的。
.border { background : black ; border-width : 0 11px ; /* 为了要适应左右两个图片的宽度,且只有左右需要,上下的宽度就不需要了 */ border-style : dashed ; border-color : Red ; width : 180px ; height : 90px ; /* 因为要适应图片,所以宽度改小一点 */ padding : 30px ; /* 其实Padding也是可以不需要的,为了方便说明一些问题,还是保留 */ margin : 0 auto ; }
![](https://i-blog.csdnimg.cn/blog_migrate/2c2c4cea89403214edbf3801deaab3ca.png)
![](https://i-blog.csdnimg.cn/blog_migrate/43a13b91a833d0a8ba38af82effa68c2.png)
步骤二:
这一步很简单,就是把中间需要x轴重复的图片添加上去,并且把黑色背景去掉
.border { background : url("images/tool-bar/bg_b_c.png") ; /* 添加背景 */ background-repeat : repeat-x ; background-position : center ; border-width : 0 11px ; /* 为了要适应左右两个图片的宽度,且只有左右需要,上下的宽度就不需要了 */ border-style : dashed ; border-color : Red ; width : 180px ; height : 90px ; /* 因为要适应图片,所以宽度改小一点 */ padding : 30px ; /* 其实Padding也是可以不需要的,为了方便说明一些问题,还是保留 */ margin : 0 auto ; }
步骤三:
这步也很简单,就是把中间重复的背景的左右两端去掉,为了方便我们下一步在左右边框中插入需要的图片。注意这里的
-moz-background-origin的content属性是无效的,实际上这里的值是padding。正如在上一节的最后说道,在火狐中是没有content这个属性的。如果有朋友想要copy这段代码,记得根据浏览器版本做相应的修改,把origin的值content改为padding,或者把盒子的padding去掉。
.border { background : url("images/tool-bar/bg_b_c.png") ; background-repeat : repeat-x ; background-position : center ; -moz-background-clip : padding ; -moz-background-origin : content ; /* firefox中background-origin没有content这个属性,其实现在的值是padding。在其他浏览器中是有效的 */ border-width : 0 11px ; /* 为了要适应左右两个图片的宽度,且只有左右需要,上下的宽度就不需要了 */ border-style : dashed ; border-color : Red ; width : 180px ; height : 90px ; /* 因为要适应图片,所以宽度改小一点 */ padding : 30px ; /* 其实Padding也是可以不需要的,为了方便说明一些问题,还是保留 */ margin : 0 auto ; }
步骤四:
这一步还是很简单,插入边框的左右两张图片,并设置好position,repeat,clip等值。注意当存在多个图片时,设置值的格式,用逗号隔开。
.border { background : url("images/tool-bar/bg_b_c.png"), url("images/tool-bar/bg_b_l.png"), url("images/tool-bar/bg_b_r.png") ; background-repeat : repeat-x,no-repeat,no-repeat ; background-position : center,left center, right center ; -moz-background-clip : padding,border,border ; -moz-background-origin : content,border,border ; border-width : 0 11px ; /* 为了要适应左右两个图片的宽度,且只有左右需要,上下的宽度就不需要了 */ border-style : dashed ; border-color : Red ; width : 180px ; height : 90px ; /* 因为要适应图片,所以宽度改小一点 */ padding : 30px ; /* 其实Padding也是可以不需要的,为了方便说明一些问题,还是保留 */ margin : 0 auto ; }
重要提示!写到这一步,可以从上图看到已经快大功告成了。把左右的红色边框去掉就可以了——那么把border-color改为none或是直接去掉这句话?不行的。如果不设置颜色的话边框就会变成黑色,因为边框有样式,而且样式还有11px宽啊,所以会用默认的黑色来填充。现在你可能又会觉得是边框样式border-style的问题,那咱们把样式去掉,把宽度保留?也不行,因为如果没有样式宽度是无效的,结果会如下图(我们可以从firebug中看到边框的宽度是0,虽然仍然保留border-width)。所以border-style,border-color,border-width缺一不可!这一点要十分注意!
步骤五:
综上所述,你可能会觉得既不能改border-style又不能改border-color岂不是没辙了?正确答案是——还是修改border-color,别忘了,color还有一个值transparent,透明。颜色还让它在,但只要人们看不见就行了。我们实现它:
.button { background : url("images/Fancybox/fancy_title_main.png"), url("images/Fancybox/fancy_title_left.png"), url("images/Fancybox/fancy_title_right.png") ; background-repeat : repeat-x,no-repeat,no-repeat ; background-position : center,left,right ; background-clip : padding-box,border-box,border-box ; background-origin : padding-box,border-box,border-box ; -moz-background-clip : padding,border,border ; -moz-background-origin : content,border,border ; border-width : 0 15px ; border-style : dashed ; border-color : transparent ; width : 80px ; height : 32px ; }
OK,彻底大功告成,很简单不是吗。
四.border-image
写到这里我发现文章的战线貌似拉的有点长了。所以我打算border-image在这里只是作为附加内容说明,并不会深究,尽可能简扼一些。开始(所以废话也简扼一些……):
border-image是定位在边框上的图片——那不是和把background-origin属性设为border差不多一个意思?是的,的确有这层含义。但在接下来的说明和例子中,你将看到它的便利之处。
这个属性说到底还是在捣鼓一张图片。先直接看一张说明图片:
草图左边是举例border-image所使用的图片,右边的草图是比喻一个盒子。border-image有两个值需要设定,头一个是切割属性(border-image-slice),最多有四个值可以设置,命名规则和margin、padding都是一致的,顺序上右下左。四个值对应左边草图的不同区域的宽度,同样上右下左的顺序,也就是2,6,8,4区域的宽度(4、6区域为横向长度,2,8区域为丛向长度)。从图中可知,因为是交集的缘故,只要定义好这四块的宽度,1、3、9、7区域的长宽也就随之确定。在映射到盒子模型上,从1到9(除5)正好连成盒子的边框区域,这样盒子的边框图片就有着落了。
但还有一个疑问。如果实际图片真如左边草图那么小,而实际盒子真有右边草图的那么大,那2、6、8、4区域的图片可能就不够用呀?问得好,所以border-image还提供一种机制来处理这个问题——图片重复属性(border-image-repeat),也叫拉伸值(stretch value)。有四个值可以选择:stretch(拉伸且默认)、repeat(重复)、round(平铺)。是不是听着很耳熟,对,和电脑壁纸的位置属性是一样的。对每一张border-image最多可以设置两个拉伸属性如round round。前一个针对图片侧边(除5区域以外),后一个针对中间部分,即5区域。如果只有一个值的话,则认为所以区域都按照此值布置。若无设置此属性,则按默认值拉伸(stretch)来布置image。
所以对应上图,咱们再来一个例子,就一目了然了:
回到上节的那个例子,这回我们只需要两张图片:和
可以看出后面那种图就是左右两侧拼接而成的,这回咱们用border-image属性完成上面的同样效果,很简单,直接看CSS代码:
.border { background : url("images/tool-bar/bg_b_c.png") ; background-repeat : repeat-x ; background-position : center ; -moz-background-clip : padding ; -moz-background-origin : padding ; -moz-border-image : url("images/bg_b_l_r.png") 0 11 ; border-width : 0 11px ; /* 为了要适应左右两个图片的宽度,且只有左右需要,上下的宽度就不需要了 */ width : 180px ; height : 90px ; /* 因为要适应图片,所以宽度改小一点 */ padding : 0 30px ; /* 其实Padding也是可以不需要的,为了方便说明一些问题,还是保留 */ margin : 0 auto ; }
还是要做几点说明。因为在这个例子中我们只需要左右侧有图片,所以切割属性只要将图片左右切割开去可以了:0 11。在上一节最后一步的重要提示中,我反复强调border-style,border-color,border-width缺一不可。但在这里只需要一个border-width就足够了!这就是它的便利之处。但在使用时要记得不同浏览器之间的差异,判断是否要使用-moz-或-webkit-或-o-前缀。
再给大家看几个惊艳的例子:
----->
还有小的:------->
至于具体CSS代码嘛,其实很简单,这里就给大家卖个关子吧。当做思考题咯
五.再唠叨几句
如果大家在阅读过程中有什么疑惑,或发现什么错误,甚者是错别字,都可以留言给我。我将尽快给大家回复,修正。很乐意与大家交流。
一直觉得博客园永远是.net,c#,MVC之类的后台天下。有关web前端的文章不少,但含金量远远不如那些多。虽然我学的是.net方向,但前端始终是挚爱。因为博客园这里前端的资料的匮乏。所以自己一旦遇到了很多前端的问题都只能google国外的文章,或是去stack overflow(但真的很有效果,回复的速度快,集思广益,而且句句锱铢。不像国内全都是“顶”啊,“标记”之类的废话,向大家强烈推荐)。
前端的冷落是不是真的因为前端是开源,比如html代码,只要把想要网站的图片和样式copy一下改一改就是了。毫无技术可言?还是JavaScript和jQuery这种东西和C#、Java比起来真的简单很多 ?呵,希望有兴趣的朋友能和我留言交流交流。
欢迎转载,转载请注明出处