# 1. 滤镜工作原理

SVG阅读器处理一个图形对象时,会将对象呈现在位图输出设备上,它可以将对象的描述信息转化为一组对应的像素。在使用滤镜时,SVG阅读器不会直接将图形渲染为最终结果,而是先将像素保存到临时位图中,然后将滤镜指定的操作应用到该临时位图,其结果作为最终图形。

SVG中,使用filter元素指定一组操作(也叫基元),在渲染图形对象时,将该操作应用在最终图形上。

filter标记之间就是我们想要的滤镜基元,每个基元有一个或多个输入,但是只有一个输出,输入可以是原始图形(SourceGraphic)、图形的阿尔法通道(不透明度,SourceAlpha)或者是前一个滤镜基元的输出。

# 2. 创建投影效果

filter元素有一些属性用来描述该滤镜的裁剪区域。通过x,y,width,height属性定义一个滤镜范围,这些属性默认情况是按照对象的边界框计算的,即filterUnits属性的默认值为objectBoundingBox,如果要按照用户单位制定边界,则需要设置该属性值为userSpaceOnUse

还可以用primitiveUnits属性为基元操作指定单位,默认值为userSpaceOnUse,如果设置为objectBoundingBox则会按照图形尺寸的百分比来表示单位。

使用feGuassianBlur元素指定一个高斯投影基元。

<defs>
	<filter id="gaussian" x="0" y="0">
		<feGaussianBlur in="SourceGraphic" stdDeviation="2"></feGaussianBlur>
	</filter>
</defs>
<g transform="translate(10,10)">
	<rect x="10" y="10" width="100" height="100" fill="#ccc" filter="url(#gaussian)"></rect>
</g>
<g>
	<rect x="10" y="10" width="100" height="100" fill="black"></rect>
</g>

在上例中,定义了一个高斯模糊滤镜,然后通过绘制两次矩形产生矩形阴影效果,但是这要求SVG阅读器要绘制两次矩形,更好的方法时添加多个滤镜基元,让SVG阅读器一次性完成渲染。于是就需要对滤镜结果进行存储、链接以及合并。修改后如下:

<defs>
	<filter id="gaussian" x="0" y="0">
		<feGaussianBlur in="SourceAlpha" stdDeviation="5" result="blur"></feGaussianBlur>
	<feOffset in="blur" dx="10" dy="10" result="offsetBlur"></feOffset>
		<feMerge>
			<feMergeNode in="offsetBlur" />
			<feMergeNode in="SourceGraphic" /> 
		</feMerge>
	</filter>
</defs>
<g>
	<rect x="10" y="10" width="100" height="100" fill="black" filter="url(#gaussian)"></rect>
</g>

result属性指定当前元素的结果稍后可以通过blur引用,这个与id不同,只能在包含该基元的filter中有效。

feOffset基元接受它的输入,在这里接受的是feGaussianBlur的结果(in="blur"),偏移由dxdy指定,输出为offsetBlur

feMerge基元包裹一个feMergeNode元素列表,每个元素都指定一个输入。这些输入按照出现的顺序叠加。在这里blur位于原始图形(SourceGraphic)的下面。

# 3. 发光滤镜

feColorMatrix元素用来以一种通用的方式改变颜色值,可以用来创建一个发光的区域。

<defs>
	<filter id="matrix" x="0" y="0">
		<feColorMatrix type="matrix" values="
			0 0 0 0   0
			0 0 0 0.9 0
			0 0 0 0.9 0
			0 0 0 1   0
		"></feColorMatrix>
    </filter>
</defs>
<text x="10" y="100" font-size="40" style="filter:url(#matrix)">发光滤镜</text>

image

feColorMatrix是一个通用的基元,允许修改任意像素点的颜色或阿尔法值,当type="matrix"时,必须指定一个4x5的矩阵。矩阵中每行数字分别乘以输入像素的r,g,b,a的值和常量1,然后加在一起得到输出值。要设置一个变换,将所有不透明区域回执为相同颜色,可以忽略输入颜色和常量。

矩阵模型:

  values = "
    0    0    0    red    0
    0    0    0    green  0
    0    0    0    blue   0
    0    0    0    1      0
  "

red,green,blue的值通常为01之间的十进制数,在上述例子中,red0,greenblue0.9会产生一个明亮的青色。

上述typematrix,除此之外,还有其他三个值:

feColorMatrix基元的type属性 说明
hueRotate 色相旋转,value是一个单一的数字,描述颜色的色相值应该被旋转多少度
saturate 饱和度,values属性指定一个0到1之间的数字,数字越小,颜色越不饱和
luminanceToAlpha 用亮度决定alpha值,这一属性忽略的values属性值

# 4. feImage滤镜

SVGfeImage滤镜允许使用任意的JPG`PNG\SVG文件或带有id属性SVG`元素作为输入源。

<defs>
	<filter id="image" x="0" y="0">
		<feImage xlink:href="10.0.jpg" result="bg" x="10" y="10" width="200" height="200" preserveAspectRatio="none"></feImage>
	</filter>
</defs>
<g style="filter:url(#image)">
	<rect x="10" y="10" width="200" height="300" stroke="black"></rect>
</g>
<g>
	<rect x="10" y="10" width="200" height="300" fill="none" stroke="black"></rect>
</g>

feImage基元可以将一个图片作为背景,在上示例中,第一个g元素使用了滤镜,因此可以看到一个200*200的图片。第二个g中包含了一个与第一个g中相同大小的矩形,feImage中定义了图片的尺寸,因此没有填充满第一个矩形。默认情况下feImage元素上使用userSpaceOnUse设置宽度、高度以及xy,如果要基于滤镜区域,则需要设置filter元素上的primitiveUnits属性为objectBoundingBox

# 5. 光照效果

可以通过滤镜为图形添加光照效果,添加光照效果必须指定以下信息:

  • 反射类型:漫反射(feDiffuseLighting)或镜面反射(feSpecularLighting)

  • 想要照亮的对象

  • 使用的灯光颜色

  • 想要的光源类型:点光源(fePointLight),远光(FeFistantLight)或聚光灯(feSpotLight)

<defs>
	<filter id="diff-light" color-interpolate-filter="sRGB" x="0" y="0">
		<feDiffuseLighting in="SourceGraphic"
			lighting-color="#ffffcc"
			surfaceScale="1"
			diffuseConstant="0.5"
			result="diffuseOutput">
			<fePointLight x="50" y="50" z="20"/>
		</feDiffuseLighting>
		<feComposite in1="diffuseOutput" in2="SourceGraphic" operator="in" result="diffuseOutput"></feComposite>
		<feBlend in1="diffuseOutput" in2="SourceGraphic" mode="screen"></feBlend>
    </filter>
</defs>
<circle cx="50" cy="50" r="50" style="filter:url(#diff-light)"></circle>
<circle cx="170" cy="50" r="50" ></circle>

上述示例中使用feDiffuseLighting定义了一个漫反射元素,设置了光照颜色、计算阿尔法乘积因子(surfaceScale)、RGB值得乘积因子(diffuseConstant)。

fePointLight元素被包裹在feDiffuseLighting中,定义了光源的相关信息(位置)。

feComposite元素接受两个源,并指定两个输入的重叠方式。

feBlend元素也需要两个输入源,还需要一个mode属性指定如何混合输入源。在这里会尝试让图形变亮。

<defs>
	<filter id="spec-light" color-interpolate-filter="sRGB" x="0" y="0">
		<feSpecularLighting in="SourceGraphic"
			lighting-color="#ffffcc"
			surfaceScale="1"
			specularConstant="1"
			specularExponent="4"
			result="specOutput"
			>
				<feDistantLight elevation="25" azimuth="0"/>
		</feSpecularLighting>
		<feComposite in1="specOutput" in2="SourceGraphic" operator="in" result="specOutput"></feComposite>
		<feComposite in1="specOutput" in2="SourceGraphic" operator="arithmetic" k1="0" k2="1" k3="1" k4="0"></feComposite>
	</filter>
</defs>
<circle cx="50" cy="50" r="50" style="fill:#060;filter:url(#spec-light)"></circle>
<circle cx="170" cy="50" r="50" style="fill:#060"></circle>

上述例子使用了镜面反射feSpecularLight元素,在feSpecularLight元素中定义了光源颜色、surfaceScalespecularConstant等属性。与漫反射的光源类型也不同。

# 6. 访问背景

除了SourceGraphicSourceAlpha作为滤镜输入之外,还可以访问以及渲染到画布上的图片的某一部分,这部分内容称为backgroundImagebackgroundAlpha。为了访问这些输入信息,滤镜对象必须位于enable-background属性值为new的容器元素之内。

<defs>
	<filter id="blur-background" color-interpolate-filter="sRGB" x="0" y="0">
		<feGaussianBlur in="BackgroundImage" stdDeviation="10" result="blur" />
		<feComposite in1="blur" in2="SourceGraphic" operator="in"/>
		<feOffset dx="4" dy="4" result="offsetBlur"></feOffset>
    </filter>
</defs>
<g enable-background="new">
	<rect x="20" y="20" width="100" height="100" style="fill:lightblue;stroke:black;stroke-width:10"></rect>
	<circle cx="100" cy="100" r="30" style="fill:#fff;filter:url(#blur-background)"></circle>
</g>

g元素设置enable-background属性,则它所有的子元素都可以利用背景图像和阿尔法信息。

# 8. 滤镜总结

上述所有滤镜之外,还有好多...写到崩溃(无奈脸)

但是这里只写出了几种常见的滤镜。更多的滤镜相关可以查询相关资料。

最后更新: 2021/7/5 下午5:48:01