通过前面的学习,你可以用你掌握的知识来绘制任何图形,接下来的目标是给绘制的图形着色。可以使用几种方法来着色,包括指定对象的属性,使用内联CSS样式或者内嵌的CSS样式,或者使用外部的CSS样式文件。大多数的Web网站的SVG使用的是内联样式CSS。但这些方法都有各自的优缺点。
在SVG中要给已绘制好的图形着色,我们主要通过SVG的填充和描边特性来完成。比如在前面的示例中,我们常能看到的fill
属性,就是用来给图形设置填充颜色;stroke
属性设置绘制对象的线条的颜色。但SVG的填充和描边特性不仅仅就这两个属性,那么今天的目标就是来学习这方面的特性。
首先我们要知道,在SVG中要让绘制的图形显示出来,就必须使用以下指定填充和描边特性:
在SVG2的规范中涉及到有关于填充特性主要有上表所列的几个特性,但是在CSS 填充和描边模块(Level3)中除了上表的几个特性之外,还有fill-break
、fill-color
、fill-image
、fill-origin
、fill-position
、fill-size
和fill-repeat
几个,看上去非常的类似于CSS中的background
属性。
但在这篇文章中,只会初步的涉略上表中所列的几个填充特性,其他有关于填充的特性将会在其他的章节中进行介绍。
在这篇文章中,我们主要来了解和学习SVG中的fill
、fill-opacity
和fill-rule
三个属性。
fill
特性在SVG中,可以使用fill
给绘制的图形填充色,而填充色又分为三种:纯色、渐变色和图案。掌握这些技能,直接能决定你绘制的图形是否能引人入胜。
纯色填充其实没有什么特别,只需要在fill
属性中添加颜色值就可以完成。其遵循的规则有:
基本颜色名称,比如:aqua
、black
、blue
这样的颜色关键词
SVG规范中规定的扩展颜色名称
六位十六进制数字#rrggbb
,从前往后,每两位分别表示red
、green
和blue
颜色值
三位十六进制数字#rgb
,三位数字分别表示red
、green
和blue
颜色值。这是六位十六进制数字#rrggbb
的缩写
rgb(r,g,b)
,r
、g
、b
分别是red
、green
和blue
,其每个值的范围是0~255
或0%~100%
currentColor
,来自元素的(当前运用的)color
属性,一般是继承的
CSS3颜色模块规范规定的其他方法
说了这么多,我们来看一个简单的示例:
<svg style="height: 300px;width: 100%;"> <rect x="50" y="50" width="100" height="100" stroke="#000" stroke-width="10" fill="#f40" /> <rect x="170" y="50" width="100" height="100" stroke="#000" stroke-width="10" fill="#0f5" /> <rect x="290" y="50" width="100" height="100" stroke="#000" stroke-width="10" fill="#09f" />
svg>
效果如下:
SVG中还可以使用渐变来填充,但这一部分咱们还没有接触过。而渐变又分为线性渐变、径向渐变和角度渐变。但在SVG中渐变的做法较为特殊,首先要先定义一个渐变层
,接着让需要使用渐变的元件,指向这个已定义的渐变层。由于没有接触过SVG中的渐变相关的知识,但并不要紧,我们继续使用老办法,通过制图软件来绘制,然后导出相应的代码。 比如我们绘制了对应的三种渐变:
在绘制的时候,每一种都可以在对应的面板中选择颜色以及调整颜色的位置,比如:
接下来导出的.svg
文件,查看清理后的代码:
<svg width="543px" height="162px" viewBox="0 0 543 162"> <defs> <linearGradient x1="50%" y1="23.0100236%" x2="50%" y2="100%" id="linearGradient-1"> <stop stop-color="#C33B35" offset="0%">stop> <stop stop-color="#164AFB" offset="100%">stop> linearGradient> <rect id="path-2" x="0" y="0" width="167" height="159">rect> <radialGradient cx="50%" cy="45.1700079%" fx="50%" fy="45.1700079%" r="76.9899764%" gradientTransform="translate(0.500000,0.451700),scale(0.952096,1.000000),rotate(90.000000),translate(-0.500000,-0.451700)" id="radialGradient-3"> <stop stop-color="#C33B35" offset="0%">stop> <stop stop-color="#164AFB" offset="100%">stop> radialGradient> <rect id="path-4" x="187" y="2" width="167" height="159">rect> <rect id="path-5" x="376" y="3" width="167" height="159">rect> defs> <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> <g id="Rectangle"> <use fill="url(#linearGradient-1)" fill-rule="evenodd" xlink:href="#path-2">use> <rect stroke="#000000" stroke-width="6" x="3" y="3" width="161" height="153">rect> g> <g id="Rectangle"> <use fill="url(#radialGradient-3)" fill-rule="evenodd" xlink:href="#path-4">use> <rect stroke="#000000" stroke-width="6" x="190" y="5" width="161" height="153">rect> g> <g id="Rectangle"> <use fill="" fill-rule="evenodd" xlink:href="#path-5">use> <rect stroke="#000000" stroke-width="6" x="379" y="6" width="161" height="153">rect> g> g>
svg>
上面实际效果中只有线性渐变和径向渐变生效,对于角度渐变是无效的:
至于为什么?暂时我也说不清楚。咱们先忽略。从上面的代码中,可以看到
中包含了
(线性渐变)和
(径向渐变)。不管是哪种渐变,都通过
来指定渐变的颜色和位置。把上面的代码简写一下:
<defs> <linearGradient id="lg"> <stop stop-color="#C33B35" offset="0%">stop> <stop stop-color="#164AFB" offset="100%">stop> linearGradient> <radialGradient id="rg"> <stop stop-color="#C33B35" offset="0%">stop> <stop stop-color="#164AFB" offset="100%">stop> radialGradient>
defs>
分别给指定的渐变添加唯一的id
名,这主要是为了让要引用渐变填充的元素可以有一个指向(更好引用)。至于
中的stop-color
和offset
起的作用,大家也许有些许了解,这也不是这里需要详细阐述的内容。
然后使用的时候,可以在fill
中通过url(id)
来引用,比如:
<rect x="50" y="50" width="100" height="100" stroke="#000" stroke-width="10" fill="url(#lg)" /> <rect x="170" y="50" width="100" height="100" stroke="#000" stroke-width="10" fill="url(#rg)" />
有关于SVG中渐变更详细的介绍,我们在后续的文章中会阐述,这里只需要知道,在SVG中可以通过fill
来做渐变填充即可。
看完了渐变填充之后,再来看最后一个图案填充。其实现的方式和渐变有点类似,同要需要先在
中加入
,这个pattern
表示的是一个图案的区域,会用这个区域进行重复排列图案。同样在元素的fill
属性中通过url(id)
来调用
中已声明的
,比如:
<svg width="543px" height="300px" viewBox="0 0 543 300"> <defs> <pattern id="a2" patternUnits="userSpaceOnUse" x="0" y="0" width="30" height="30"> <rect x="0" y="0" width="25" height="25" stroke="#none" fill="#09f">rect> pattern> defs> <rect x="170" y="50" width="135" height="135" stroke="#000" stroke-width="10" fill="url(#a2)" /> <circle cx="420" cy="120" r="75" stroke="#000" stroke-width="5" fill="url(#a2)" />
svg>
你将看到的效果如下:
fill-opacity
特性fill-opacity
特性类似于CSS的opacity
属性。用来设置元素填充的透明度。其值的范围不是0~1
。接近0
的值,元素填充更趋于透明,反之越接近1
,则元素填充趋于完全不透明。其默认值为1
。来看一个简单的示例:
<rect x="50" y="50" width="135" height="135" stroke="#000" stroke-width="10" fill="#f40" fill-opacity="0.3" /><rect x="200" y="50" width="135" height="135" stroke="#000" stroke-width="10" fill="url(#a2)" fill-opacity="0.5"/> <circle cx="430" cy="120" r="75" stroke="#000" stroke-width="10" fill="url(#lg)" fill-opacity="0.9"/>
效果如下:
从上面的示例中,可以看出,不管是纯色、渐变还是图案填充,只要元素上使用了fill-opacity
属性,都可以设置元素的填充透明度。
fill-rule
特性fill-rule
特性相对而言要更复杂一些。其定义了判断点是不是属于填充范围的算法,其值有nonzero
、evenodd
和inherit
。咱们主要来了解nonzero
和evenodd
的算法。
nonzero
这个值采用的算法是:从需要判定的点向任意方向发射线,然后计算图形与线段交点的处的走向;计算结果从0
开始,每有一个交点处的线段是从左到右的,就加1
;每有一个交点处的线段是从右到左的,就减1
;这样计算完所有交点后,如果这个计算的结果不等于0
,则该点在图形内,需要填充;如果该值等于0
,则在图形外,不需要填充。比如下图:
来看一个示例:
<svg width="250px" height="250px" viewBox="0 0 250 250"> <polygon fill="#F9F38C" fill-rule="nonzero" stroke="#E5D50C" stroke-width="5" stroke-linejoin="round" points="47.773,241.534 123.868,8.466 200.427,241.534 7.784,98.208 242.216,98.208 " />svg><svg width="250px" height="250px" viewBox="0 0 250 250"> <path fill="#F4CF84" fill-rule="nonzero" stroke="#D07735" stroke-width="5" d="M124.999,202.856 c-42.93,0-77.855-34.928-77.855-77.858s34.925-77.855,77.855-77.855s77.858,34.925,77.858,77.855S167.929,202.856,124.999,202.856z M125.003,245.385c-7.61,0-13.025-6.921-17.802-13.03c-2.79-3.559-6.259-8.002-8.654-8.638c-0.318-0.085-0.71-0.134-1.159-0.134 c-2.873,0-7.1,1.698-11.188,3.335c-4.929,1.973-10.029,4.021-14.774,4.021c-2.486,0-4.718-0.563-6.633-1.677 c-6.451-3.733-7.618-11.959-8.742-19.919c-0.646-4.571-1.45-10.261-3.292-12.096c-1.84-1.845-7.524-2.646-12.093-3.298 c-7.96-1.119-16.192-2.286-19.927-8.739c-3.682-6.358-0.614-14.005,2.35-21.404c1.829-4.563,3.904-9.735,3.201-12.352 c-0.638-2.392-5.073-5.861-8.64-8.648C11.539,138.025,4.618,132.612,4.618,125c0-7.61,6.921-13.025,13.027-17.802 c3.567-2.79,8.002-6.259,8.64-8.651c0.702-2.614-1.375-7.789-3.201-12.349c-2.961-7.399-6.029-15.046-2.347-21.409 c3.733-6.451,11.962-7.618,19.924-8.742c4.569-0.646,10.253-1.45,12.096-3.292c1.84-1.84,2.646-7.524,3.29-12.093 c1.127-7.96,2.291-16.192,8.745-19.924c1.914-1.111,4.147-1.674,6.633-1.674c4.745,0,9.845,2.045,14.771,4.021 c4.085,1.639,8.312,3.335,11.188,3.335c0.446,0,0.836-0.045,1.161-0.131c2.392-0.641,5.861-5.079,8.654-8.643 c4.782-6.109,10.194-13.03,17.804-13.03c7.612,0,13.025,6.921,17.804,13.027c2.782,3.565,6.259,8.002,8.654,8.643 c0.323,0.085,0.71,0.131,1.161,0.131c2.876,0,7.094-1.696,11.185-3.332c4.932-1.976,10.029-4.021,14.779-4.021 c2.478,0,4.715,0.563,6.627,1.671c6.448,3.733,7.618,11.962,8.739,19.927c0.646,4.569,1.453,10.253,3.292,12.093 c1.84,1.84,7.524,2.646,12.096,3.292c7.96,1.127,16.189,2.291,19.919,8.745c3.687,6.36,0.619,14.007-2.344,21.404 c-1.824,4.563-3.898,9.735-3.201,12.347c0.641,2.395,5.079,5.864,8.643,8.657c6.104,4.774,13.025,10.189,13.025,17.799 c0,7.612-6.921,13.025-13.03,17.804c-3.559,2.788-8.002,6.264-8.638,8.654c-0.702,2.614,1.375,7.783,3.201,12.347 c2.964,7.399,6.032,15.046,2.344,21.409c-3.733,6.448-11.959,7.618-19.924,8.739c-4.566,0.646-10.256,1.453-12.09,3.292 c-1.845,1.84-2.646,7.524-3.298,12.096c-1.119,7.96-2.291,16.189-8.745,19.919c-1.909,1.113-4.147,1.677-6.627,1.677 c-4.745,0-9.839-2.048-14.768-4.021c-4.091-1.637-8.315-3.335-11.19-3.335c-0.446,0-0.836,0.048-1.161,0.134 c-2.392,0.635-5.861,5.073-8.648,8.638C138.027,238.464,132.615,245.385,125.003,245.385z" />
svg>
效果如下:
星星是由一条相交的路径组成的,太阳则是由一条长复合的路径组成。每个形状的内部最初并不清楚,可能根据作者的意图而有所不同。在这些情况下,fill-rule
允许进一步澄清。
在下一个例子中,我们可以看得更清楚些,当nonzero
算法被应用到类似的图形时,究竟发生了什么?
从上图中我们可以理解成,当方向是顺时针时,加1
,逆时针时减1
。相交的值不等于0
则填充,如果等于0
则不填充。
evenodd
这个值采用的算法是,从需要判定的点向任意方向发射线,然后计算图形与线段交点的个数,个数为奇数则该点在图形内,则需要填充;个数为偶数,则该点在图形外,不需要填充。如下图所示:
上面的示例稍作调整:
<svg width="250px" height="250px" viewBox="0 0 250 250"> <polygon fill="#F9F38C" fill-rule="evenodd" stroke="#E5D50C" stroke-width="5" stroke-linejoin="round" points="47.773,241.534 123.868,8.466 200.427,241.534 7.784,98.208 242.216,98.208 " />svg><svg width="250px" height="250px" viewBox="0 0 250 250"> <path fill="#F4CF84" fill-rule="evenodd" stroke="#D07735" stroke-width="5" d="M124.999,202.856 c-42.93,0-77.855-34.928-77.855-77.858s34.925-77.855,77.855-77.855s77.858,34.925,77.858,77.855S167.929,202.856,124.999,202.856z M125.003,245.385c-7.61,0-13.025-6.921-17.802-13.03c-2.79-3.559-6.259-8.002-8.654-8.638c-0.318-0.085-0.71-0.134-1.159-0.134 c-2.873,0-7.1,1.698-11.188,3.335c-4.929,1.973-10.029,4.021-14.774,4.021c-2.486,0-4.718-0.563-6.633-1.677 c-6.451-3.733-7.618-11.959-8.742-19.919c-0.646-4.571-1.45-10.261-3.292-12.096c-1.84-1.845-7.524-2.646-12.093-3.298 c-7.96-1.119-16.192-2.286-19.927-8.739c-3.682-6.358-0.614-14.005,2.35-21.404c1.829-4.563,3.904-9.735,3.201-12.352 c-0.638-2.392-5.073-5.861-8.64-8.648C11.539,138.025,4.618,132.612,4.618,125c0-7.61,6.921-13.025,13.027-17.802 c3.567-2.79,8.002-6.259,8.64-8.651c0.702-2.614-1.375-7.789-3.201-12.349c-2.961-7.399-6.029-15.046-2.347-21.409 c3.733-6.451,11.962-7.618,19.924-8.742c4.569-0.646,10.253-1.45,12.096-3.292c1.84-1.84,2.646-7.524,3.29-12.093 c1.127-7.96,2.291-16.192,8.745-19.924c1.914-1.111,4.147-1.674,6.633-1.674c4.745,0,9.845,2.045,14.771,4.021 c4.085,1.639,8.312,3.335,11.188,3.335c0.446,0,0.836-0.045,1.161-0.131c2.392-0.641,5.861-5.079,8.654-8.643 c4.782-6.109,10.194-13.03,17.804-13.03c7.612,0,13.025,6.921,17.804,13.027c2.782,3.565,6.259,8.002,8.654,8.643 c0.323,0.085,0.71,0.131,1.161,0.131c2.876,0,7.094-1.696,11.185-3.332c4.932-1.976,10.029-4.021,14.779-4.021 c2.478,0,4.715,0.563,6.627,1.671c6.448,3.733,7.618,11.962,8.739,19.927c0.646,4.569,1.453,10.253,3.292,12.093 c1.84,1.84,7.524,2.646,12.096,3.292c7.96,1.127,16.189,2.291,19.919,8.745c3.687,6.36,0.619,14.007-2.344,21.404 c-1.824,4.563-3.898,9.735-3.201,12.347c0.641,2.395,5.079,5.864,8.643,8.657c6.104,4.774,13.025,10.189,13.025,17.799 c0,7.612-6.921,13.025-13.03,17.804c-3.559,2.788-8.002,6.264-8.638,8.654c-0.702,2.614,1.375,7.783,3.201,12.347 c2.964,7.399,6.032,15.046,2.344,21.409c-3.733,6.448-11.959,7.618-19.924,8.739c-4.566,0.646-10.256,1.453-12.09,3.292 c-1.845,1.84-2.646,7.524-3.298,12.096c-1.119,7.96-2.291,16.189-8.745,19.919c-1.909,1.113-4.147,1.677-6.627,1.677 c-4.745,0-9.839-2.048-14.768-4.021c-4.091-1.637-8.315-3.335-11.19-3.335c-0.446,0-0.836,0.048-1.161,0.134 c-2.392,0.635-5.861,5.073-8.648,8.638C138.027,238.464,132.615,245.385,125.003,245.385z" />
svg>
运用fill-rule="evenodd"
的星星和太阳的效果就和刚才的不一样了:
同样用一张图来描述,可能更易于理解:
evenodd
规则是特定的算法,与nonzero
情况不同,其算法和内部形状绘制的方向不相关,因为只是简单地计算它们穿过直线的路径数是不是奇偶数。
上面我们了解了SVG中的填充特性,除此之外,还有描边特性:
在SVG2的规范中,除了上表所列之外的特性,还有stroke-dashoffset
。但在CSS 填充和描边模块(Level3)中除了上述所列的几个特性之外,还有以下一些特性:stroke-break
、stroke-dash-corner
、stroke-dash-justify
、stroke-color
、stroke-image
、stroke-origin
、stroke-position
、stroke-size
、stroke-repeat
等特性。
由于担心篇幅过长,将SVG的描边特性放到下一节中来阐述。
除了定义对象的属性外,你也可以通过CSS来样式化填充(当然后面的描边也可以)。语法和在HTML中使用CSS一样,只不过你要把background-color
换成fill
。
需要特别注意的是,在SVG中不是所有的属性都能用CSS来设置。上色和填充的部分一般是可以用CSS来设置的,比如fill
,但是不包括上面提到的渐变和图案等功能。另外,width
、height
以及path
的命令等,都不能用CSS来设置。判断它们能不能用CSS设置还是比较容易的。
SVG规范将属性区分成
properties
和其他attributes
,前者是可以用CSS来设置,后者则不能。
用简单的示例来展示,更为明显:
<defs> <style type="text/css">
style>
defs>
<rect x="10" height="180" y="10" width="180" id="MyRect"/>
或者:
<rect x="10" height="180" y="10" width="180" style="stroke: black; fill: red;"/>
你也还可以在单独的.css
文件中使用:
#MyRect { fill: red;}
前面的内容我们是学习了在SVG中怎么绘制图形。事实上,在SVG中绘制的图形要着色的话是需要通过其他的特性来完成的,比如填充就需要fill
特性。而在这篇文章中,主要学习了如何给绘制的元素填充(纯色、渐变和图案)。主要涉及到三个属性:fill
用来填充颜色(或图案),fill-opacity
用来设置填充的透明度和fill-rule
用来指定判断填充的算法。
在SVG中绘制的图形除了填充之外,还有描边着色之类的。比如文章列表中提到的描边特性。那么在下一篇文章中,将主要来学习这方面的知识。如果你感兴趣的话,欢迎持续观注本系列的相关更新。
由于本人是SVG的初学者,如果文章中有不对之处,烦请各路大婶拍正。如果你有更好的经验或想法,欢迎在下面的评论中与我们一起分享。
文章涉及到图片和代码,如果展示不全给您带来不好的阅读体验,欢迎点击文章底部的 阅读全文。如果您觉得小站的内容对您的工作或学习有所帮助,欢迎关注此公众号。
W3cplus.com
————————————
记述前端那些事,引领web前沿
长按二维码,关注W3cplus
▼