前言

SVG(Scalable Vector Graphics)意为可缩放矢量图形,通过XML格式定义图像,在HTML开发中有广泛的使用。

其中,SVG filter(SVG 过滤器)可以用来给图形添加一些特殊效果,例如模糊、阴影、色彩转换等等。

相比于CSS的filter和Blend Modes,SVG filter可以提供RGBA通道级别的控制,能够更好地处理图像和得到更多的特殊效果。

而在兼容性方面也可以接受,除非强制要求兼容IE9及以下版本浏览器,因此可以比较放心的使用。

常用的filter有以下几个(fe前缀意为filter effect):

  • feGaussianBlur:模糊过滤器,最为常用的过滤器
  • feColorMatrix:色彩矩阵过滤器,较为常用,常用于调整色彩
  • feOffset:偏移过滤器,常用于创建阴影
  • feMerge:混合过滤器,常用于原图和过滤器效果叠加
  • feImage:图片过滤器,可以使用JPG、PNG、SVG文件或SVG元素作输入源
  • feComponentTransfer
  • feComposite
  • feConvolveMatrix
  • feDiffuseLighting
  • feDisplacementMap
  • feFlood
  • feBlend
  • feMorphology
  • feSpecularLighting
  • feTile
  • feTurbulence
  • feDistantLight
  • fePointLight
  • feSpotLight

 

使用

以下面这个模糊效果为例

<div class="box"></div>
<div class="box" style="filter: url(#blurFilter);" ></div>
<svg>
    <filter id="blurFilter" >
        <feGaussianBlur in="SourceGraphic" stdDeviation="3" ></feGaussianBlur>
    </filter>
</svg>

声明filter后,其他元素可以通过filter: url(#filterID)方式进行调用。其中stdDeviation属性为模糊过滤器的模糊尺寸。

效果如下

 

过滤器的输入与输出

SVG filter在应用过滤效果时需要一个输入源,这个输入源可以是一个图形,或图形的alpha通道,或另一个过滤器的输出值(链接使用)。

输入源可以通过in属性指定,例如:

<feGaussianBlur in="SourceGraphic" stdDeviation="10"/>

SourceGraphic意为使用图形的所有通道作为输入源,也可以使用SourceAlpha只使用图形的Alpha透明通道作为输入源。

将过滤器输出作为另一个过滤器的输入,需要在输出元素上添加result属性,然后另一个元素通过in属性输入(只能在当前filter标签内引用,类似于局部变量)。

当某些过滤器需要多个输入源时,例如feBlend,所需两个输入源分别为in和in2。

下面是一个模拟box-shadow的案例:

<div class="box"></div>
<div class="box" style="filter: url(#shadow);" ></div>

<svg>
    <filter id="shadow">
        <feGaussianBlur in="SourceAlpha" stdDeviation="3" result="blur"></feGaussianBlur>
        <feOffset in="blur" dx="4" dy="4" result="offset"></feOffset>
        <feMerge>
            <feMergeNode in="offset"></feMergeNode>
            <feMergeNode in="SourceGraphic"></feMergeNode>
        </feMerge>
    </filter>
</svg>

feOffset过滤器将通过feGaussianBlur过滤器模糊后的Alpha通道进行偏移(feOffset的偏移量由dx和dy属性控制),然后使用feMerge(注意feMergeNode为顺序叠加)与原图形叠加显示,制作简单地阴影效果。

得到的效果如下

 

过滤器尺寸

一个SVG filter的尺寸由x、y、width、height四个属性决定。

由于过滤器输出的图形通常会比输入图形大(例如模糊效果),因此有时需要对输出图形进行裁剪。

width和height用于指定过滤器的宽度和高度,大多数情况下应该设置为大于输出图像的尺寸,x和y用于指定裁切的位置,通常需要设置为负值。

在未设置尺寸属性时,x和y默认为-10%,width和height默认为120%原始宽高。

以下面的模糊效果为例:

<div class="box"></div>
<div class="box" style="filter: url(#blurFilter1);" ></div>
<div class="box" style="filter: url(#blurFilter2);" ></div>
<div class="box" style="filter: url(#blurFilter3);" ></div>
<svg>
    <filter id="blurFilter1">
        <feGaussianBlur in="SourceGraphic" stdDeviation="25"></feGaussianBlur>
    </filter>
    <filter id="blurFilter2" height="120" width="120">
        <feGaussianBlur in="SourceGraphic" stdDeviation="25"></feGaussianBlur>
    </filter>
    <filter id="blurFilter3" x="-30%" y="-30%" height="120" width="120">
        <feGaussianBlur in="SourceGraphic" stdDeviation="25"></feGaussianBlur>
    </filter>
</svg>

得到的效果如下:

 

feColorMatrix

这个过滤器是比较难以理解但功能十分强大而且常用的色彩转换过滤器,还是以上面的box-shadow为案例,如果想要添加彩色阴影,则可以在feGaussianBlur前面添加feColorMatrix过滤器,代码如下:

<div class="box"></div>
<div class="box" style="filter: url(#shadow);" ></div>

<svg>
    <filter id="shadow">
        <feColorMatrix type="matrix" result="color" values="
        0 0 0 0.9 0
        0 0 0 0.2 0
        0 0 0 0.5 0
        0 0 0 1   0
        "></feColorMatrix>
        <feGaussianBlur in="color" stdDeviation="3" result="blur"></feGaussianBlur>
        <feOffset in="blur" dx="4" dy="4" result="offset"></feOffset>
        <feMerge>
            <feMergeNode in="offset"></feMergeNode>
            <feMergeNode in="SourceGraphic"></feMergeNode>
        </feMerge>
    </filter>
</svg>

效果如下:

当type属性为matrix时,value为4行5列的20个数字矩阵,每一行代表一个方程式,5个数字为原图RGBA通道和常量1,最终计算出的值即为输出结果各通道的值。

另外取值范围不止于0~1,数值小于0时,会得到反相,数值大于1时,会得到更亮的效果。

// R G B A   1
   0 0 0 0.9 0 //outR = 0*R + 0*G + 0*B + 0.9*A + 0*1
   0 0 0 0.2 0 //outG = 0*R + 0*G + 0*B + 0.2*A + 0*1
   0 0 0 0.5 0 //outB = 0*R + 0*G + 0*B + 0.5*A + 0*1
   0 0 0 1   0 //outA = 0*R + 0*G + 0*B + 1*A + 0*1

由于源图形alpha通道是数值为1的不透明,因此最后输出的阴影色彩即为RGBA(0.9,0.2,0.5,1)的纯色,而如果以一张色彩斑斓的图片为源素材,使用RGB通道进行色彩转换,最后得到的输出也会是彩色的,也就是美图滤镜的原理。

type属性除了最常用的matrix值外,其他的使用方式如下:

 

一个水滴融合案例

下面是一个feGaussianBlur和feColorMatrix滤镜配合box-shadow实现的水滴融合效果。

之所以水滴之间会产生粘连以及水滴重合时会放大,原理是利用了模糊的叠加以及锐化,很有创意的思路。

HTML部分

<div class="loader">
    <span style="--i:1"></span>
    <span style="--i:2"></span>
    <span style="--i:3"></span>
    <span style="--i:4"></span>
    <span style="--i:5"></span>
    <span style="--i:6"></span>
    <span style="--i:7"></span>
</div>

<svg>
    <filter id="gooey">
        <feGaussianBlur in="SourceGraphic" stdDeviation="10"/>
        <feColorMatrix values="
            1 0 0 0 0
            0 1 0 0 0
            0 0 1 0 0
            0 0 0 20 -10
        "/>
    </filter>
</svg>

CSS部分

.loader{
    position: relative;
    width: 200px;
    height: 200px;
    filter: url(#gooey);
}
.loader span{
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    display: block;
    animation: spin 4s ease-in-out infinite;
    animation-delay: calc(0.2s * var(--i));
}
.loader span:before{
    content: '';
    position: absolute;
    top: 0;
    left: calc(50% - 20px);
    width: 40px;
    height: 40px;
    background: linear-gradient(#fce4ec,#03a9f4);
    border-radius: 50%;
    box-shadow: 0 0 30px #03a9f4;
}
@keyframes spin{
    0%{
        transform: rotate(0deg);
    }
    50%,100%{
        transform: rotate(360deg);
    }
}

最后得到的效果如下:

 

说实话,就是因为这个水滴案例,对SVG过滤器产生兴趣,研究了下有所收获也就有了这篇文章。没错,为了这点醋,才包了这顿饺子,哈哈哈。

 


离群索居者,
不是神灵,就是野兽。

《政治学》
——亚里士多德