@Gizmosir
2016-03-16T10:40:14.000000Z
字数 9728
阅读 831
date: 2016-03-16
categories: Computer graphics
tag: [SVG, 矢量图]
博客
这是矢量图的第二篇,如果你没有看过第一篇,可以点击这里。
在第一篇中我们介绍了基础的SVG使用方法,在这一篇中我们将来着重介绍SVG的大杀器——滤镜。
相信大家或多或少也都接触过Photoshop,对图层这个概念也不陌生。图层实际上就是独立的图形,通过覆盖的方式组合成一张图像,如上图所示。
而图层的概念在SVG中也是存在的,其用法与Photoshop的相似,只是覆盖的顺序是后添加的图层(以下称为后图层)将覆盖在前面添加的图层(以下称为前图层)之上。
实际上你可以将不同的图层理解成不同的滤镜效果,将所有的滤镜效果叠加之后生成了最终显示的图像。
就如同我在第一篇中说的一样,几乎所有出色的SVG图像都离不开滤镜。通过将不同的滤镜组件(primitives)叠加在一起生成复合滤镜的方法更是重中之重,那么首先我们来看下都有哪些基本的滤镜组件:
一下子你肯定会觉得这么多滤镜名字已经不同的功能,怎么记得住
?那么下面我们结合图例来从逐个解释下每个滤镜组件,好让你对每个组件有个更好的了解,相信看完你一定都大致记住了。
虽然图像不符合SVG可以无限放大且不产生锯齿的特性,但是有时我们仍然需要使用图像来生成SVG图像,比如名片。这个时候我们就可以使用feImage
。
<!--该例子读取一张图片,并将其作为滤镜填充到矩形中。-->
<svg
<!--使用feImage时一定要添加下面两句来提示外链图片的位置-->
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" >
<defs>
<filter id="feImage_demo">
<feImage xlink:href="smile.jpg" />
</filter>
</defs>
<rect y="0" x="0" height="500" width="500" style="filter:url(#feImage_demo)" />
</svg>
当然feImage
除了导入外链图像,也能导入使用代码生成的SVG图像,如下例:
<!--该例子首先生成一个“太阳”圆,并将其作为滤镜填充到矩形中。-->
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" >
<defs>
<circle id="pretty_circle" cx="300" cy="300" r="200" style="stroke-width:80; stroke:yellow; fill:red" />
<filter id="feImage_demo">
<feImage xlink:href="#pretty_circle" />
</filter>
</defs>
<rect y="0" x="0" height="500" width="500" style="filter:url(#feImage_demo)" />
</svg>
如果我们想要将上下图层叠加,总共有三个指令可以使用,分别是feMerge
,feBlend
和feComposite
。首先我们来看下比较简单的feMerge
。其作用是简单地将图层合并成一张,后图层直接覆盖在前图层之上。所以如果要保证能够通过后图层看到前图层的东西,则必须保证对应位置的透明度(alpha)为0.
<!--本例子中首先读取三张图片,并将其合并成一个滤镜,最后将滤镜应用到一个矩形上。-->
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" >
<defs>
<filter id="layers"
color-interpolation-filters="sRGB"> <!--声明了滤镜的插值方法-->
<feImage xlink:href="liquid_image.jpg" result="background"/>
<feImage xlink:href="small_fish.png" result="layer1" width="500" x="500" y="-180"/>
<feImage xlink:href="big_fish.png" result="layer2" width="800" x="80" y="30"/>
<feMerge>
<feMergeNode in="background"/>
<feMergeNode in="layer1" />
<feMergeNode in="layer2"/>
</feMerge>
</filter>
</defs>
<rect y="0" x="0" height="960" width="1280" style="filter:url(#layers)"/>
</svg>
而另外一个将图层叠加的方法是混合(feBlend)。feBlend
相对于feMerge
,能够根据不同的属性实现稍微复杂一点的效果。属性( normal | multiply | screen | darken | lighten )与其对应的效果如下:
<!--本例子中首先读取两张图片,然后用相乘的方式将两张图片混合并生产滤镜,最后将滤镜应用到矩形上。-->
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" >
<defs>
<filter id="demo_of_feBlend"
color-interpolation-filters="sRGB">
<feImage xlink:href="rainbow.png" result="background_image"/>
<feImage xlink:href="pony.png" result="foreground_image" width="500" x="100"/>
<feBlend mode="multiply"
in="foreground_image" in2="background_image" />
</filter>
</defs>
<rect x="0" y="0" width="100%" height="100%" filter="url(#demo_of_feBlend)"/>
</svg>
最后一个可以将图层添加到一起的命令是复合(feComposite),是应用效果是能够根据属性不同来显示不同图层的不同部分,属性( over | in | out | atop | xor | arithmetic )和其对应效果如下:
由于复合使用的例子需要的滤镜组件我们还没介绍到,所以例子稍后再给。
之前在第一篇的时候我们介绍过使用模式的方式来生成连续不断的图片,实际上更加普遍的做法是使用平铺(feTile)。
<!--本例子中首先读取图片,并平铺生成滤镜,最后将滤镜应用到矩形上。-->
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" >
<defs>
<filter id="carpet"
color-interpolation-filters="sRGB">
<feImage xlink:href="carpet.jpg" width="294" height="374"/>
<feTile />
</filter>
</defs>
<rect x="0" y="0" width="100%" height="100%" filter="url(#carpet)"/>
</svg>
如果平铺的图形十分简单且有规律的话,我们也能够通过自定义形状并使用feTile
来进行平铺。
<!--本例子中首先生成四个矩形,接着将矩阵合并并平铺生成滤镜,最后将滤镜应用到矩形上。-->
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<filter id="chessboard"
color-interpolation-filters="sRGB">
<feFlood x="0" y="0" width="100"
height="100" flood-color="black" result="stage1"/>
<feFlood x="0" y="100" width="100"
height="100" flood-color="white" result="stage2"/>
<feFlood x="100" y="100" width="100"
height="100" flood-color="black" result="stage3"/>
<feFlood x="100" y="0" width="100"
height="100" flood-color="white" result="stage4"/>
<feMerge>
<feMergeNode in="stage1"/>
<feMergeNode in="stage2"/> <feMergeNode in="stage3"/>
<feMergeNode in="stage4"/>
</feMerge>
<feTile />
</filter>
</defs>
<rect x="0" y="0" width="100%" height="100%" filter="url(#chessboard)"/>
</svg>
有时候我们需要对图形/图像的颜色进行调整,那么我们可以使用feComponentTransfer
来调整。feComponentTransfer
可以调整( feFuncR | feFuncG | feFuncB | feFuncA )四路通道值,其调整曲线有( Identity | Linear | Discrete | Table | Gramma )五种。
如果图形/图像整体偏暗,我们可以通过同时提高R、G、B值,能使得图像更加明亮些。
<!--本例子中首先读取图片,使用feComponentTransfer提高R、G、B通道的值并生成滤镜,最后将滤镜应用到图片上,并将修改后的图像与原图像进行对比-->
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" >
<defs>
<filter id="brighter"
color-interpolation-filters="sRGB">
<feComponentTransfer>
<feFuncR type="linear" slope="1.4" intercept="0"/>
<feFuncG type="linear" slope="1.4" intercept="0"/>
<feFuncB type="linear" slope="1.4" intercept="0"/>
</feComponentTransfer>
</filter>
</defs>
<text x="10" y="145">Before:</text>
<image x="70" y="0" width="500" height="751"
xlink:href="Thailand.jpg" />
<text x="610" y="145">After:</text>
<image x="670" y="0"
width="500"
height="751"
xlink:href="before.jpg"
style="filter:url(#brighter)" />
</svg>
有些古老的照片可能褪色,也就是说白色不白了,黑色不黑了,那么我也可以通过色彩调节来改善图片效果。
<!--本例子中首先读取图片,使用Linear曲线调整R、G、B通道的值并生成滤镜,最后将滤镜应用到图片上,并将修改后的图像与原图像进行对比-->
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" >
<defs>
<filter id="incContrast"
color-interpolation-filters="sRGB">
<feComponentTransfer>
<feFuncR type="linear" slope="1.5" intercept="-0.3"/>
<feFuncG type="linear" slope="1.5" intercept="-0.3"/>
<feFuncB type="linear" slope="1.5" intercept="-0.3"/>
</feComponentTransfer>
</filter>
</defs>
<text x="10" y="145">Before:</text>
<image x="70" y="20"
width="465"
height="592"
xlink:href="old_photo_man.png" />
<text x="550" y="145">After:</text>
<image x="600" y="20"
width="465"
height="592"
xlink:href="old_photo_man.png"
style="filter:url(#incContrast)" />
</svg>
最后,我们也能够将色彩值的表示范围进行压缩,甚至“二值化”从而使图形/图像变得不真实。
<!--本例子中首先读取图片,使用Discrete曲线调整R、G、B通道的值并生成滤镜,最后将滤镜应用到图片上,并将修改后的图像与原图像进行对比-->
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" >
<defs>
<filter id="posterization"
color-interpolation-filters="sRGB">
<feComponentTransfer>
<feFuncR type="discrete" tableValues="0 1"/>
<feFuncG type="discrete" tableValues="0 1"/>
<feFuncB type="discrete" tableValues="0 1"/>
</feComponentTransfer>
</filter>
</defs>
<text x="10" y="145">Before:</text>
<image x="70" y="-80"
width="600"
height="600"
xlink:href="pretty_car.jpg" />
<text x="10" y="550">After:</text>
<image x="70" y="350"
width="600"
height="600"
xlink:href="pretty_car.jpg"
style="filter:url(#posterization)" />
</svg>
高斯模糊(feGaussianBlur
)滤镜使用2维高斯函数对图形/图像进行模糊,从而产生一些很棒的效果。
将小协方差的高斯滤镜应用到人物照片上,会产生模糊的效果,从而淡化原本很明显的一些缺点。
<!--本例子首先生成一个协方差为1.5的高斯滤镜,然后将滤镜应用在读取的图片上。-->
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" >
<defs>
<filter id="both_axes_blur"
color-interpolation-filters="sRGB">
<!--高斯滤镜的输入是图片,直接声明SourceGraphic就可以。-->
<feGaussianBlur
in="SourceGraphic"
stdDeviation="1.5 1.5" />
</filter>
</defs>
<image x="550" y="20"
width="400"
height="400"
xlink:href="spotty_face.png"
style="filter:url(#both_axes_blur)" />
</svg>
将只作用在某个方向上的高斯滤镜应用到图片上时,会产生模糊的效果,从而使得图片呈现在作用方向上运动很快的感觉。
<!--本例子首先生成一个X轴方向上的高斯滤镜,然后将滤镜应用在读取的图片上。-->
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
style="background-color: black" >
<defs>
<filter id="xblur"
color-interpolation-filters="sRGB">
<feGaussianBlur
in="SourceGraphic"
stdDeviation="10 0.0001" /><!--需要注意的是SVG并不支持0赋值,所以只能尽可能的小。-->
</filter>
</defs>
<image x="640" y="0"
width="468"
height="351"
xlink:href="superman.jpg"
style="filter:url(#xblur)" />
</svg>
另外,高斯滤镜还常被用来产生阴影效果。结合着feBlend
能够产生一些很棒的文字效果,如:
<!--首先生成一个协方差较大的高斯滤镜,并对文字进行模糊,最后叠加上文字并产生最后的效果。-->
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
style="background-color: black" >
<defs>
<filter id="glowing"
color-interpolation-filters="sRGB">
<feGaussianBlur in="SourceGraphic" result="glow"
stdDeviation="15" />
<feBlend mode="normal" in="glow" in2="SourceGraphic"/>
</filter>
</defs>
<text y="200" x="100" font-size="145"
height="300"
width="300"
style="fill:red;filter:url(#glowing)"> Halloween </text>
</svg>
另外一个常用于产生阴影的命令是feOffset
。其作用就是将图形/图像往X轴/Y轴方向进行移动。所以结合着feGaussianBlur
和feBlend
也能够产生一些不错的文本阴影效果:
<!--本例子中首先生成高斯滤镜,并作用于文本,接着将文本移动一定距离,最后与文本合并并产生最后的效果。需要注意的是由于我们的文本是有颜色的,但是阴影不需要有颜色,所以我们只取文本的alpha值来做高斯模糊。-->
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" >
<defs>
<filter id="shadow" color-interpolation-filters="sRGB">
<feGaussianBlur in="SourceAlpha" stdDeviation="10" result="blur"/>
<feOffset in="blur" dx="20" dy="20" result="offsetBlur"/>
<feBlend mode="normal" in="SourceGraphic" in2="offsetBlur" />
</filter>
</defs>
<text x="0" y="200" font-size="200pt" font-family="Arial" style="filter:url(#shadow)">
<tspan x="20">It's</tspan>
<tspan x="0" dy="230" fill="red">Hot</tspan>
</text>
</svg>
虽然转换并不属于滤镜组件,但是结合着转换能够生成一种超级棒而且也很实用的镜面效果,所以我忍不住还是想要在这里介绍一下。有好几种能够用于SVG转换的命令( Translate | Rotate | Scale )。
<!--本例子中首先生成高斯滤镜,并且定义了线性渐变,接着对文本进行翻转并将滤镜和渐变同时应用与翻转文本上。之所以使用高斯滤镜的原因是加上一点模糊会使得翻转文字看上去更加真实。-->
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
style="background-color: black" >
<defs>
<linearGradient id="vertical_gradient" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0.2" style= "stop-color:black"/>
<stop offset="1" style= "stop-color:grey"/>
</linearGradient>
<filter id="reflection_blur"
color-interpolation-filters="sRGB">
<feGaussianBlur in="SourceGraphic"
stdDeviation="1" />
</filter>
</defs>
<text y="200" x="100" font-size="145" font-family="verdana"
style="fill:lightgrey"> Reflection </text>
<text y="200" x="100" font-size="145" font-family="verdana"
style="fill:url(#vertical_gradient);filter:url(#reflection_blur)"
transform="translate(0,405) scale(1,-1)" > Reflection </text>
</svg>