Open gogoend opened 3 years ago
这是我所写的SVG滤镜相关的第二篇文章。本篇将通过讲解几个原语,来将外部资源引入到SVG滤镜搭建流程中。
俗话说,巧妇难为无米之炊。如果说巧妇 === SVG滤镜,那米 === 图像。因此,要想让SVG滤镜发挥作用,我们就需要把要处理的图像传递给SVG滤镜原语中。那么要如何做到这一点呢?
巧妇 === SVG滤镜
米 === 图像
下面我们就来看一些能够为SVG滤镜输入图像的关键字和原语吧
这些关键字可以作为图像处理滤镜原语的in属性的输入值,用来输入图像。
in
SourceGraphic
表示的是元素在应用滤镜之前本来的样子(源图像)
我们来看一个例子
【例2-1】 这是原图,包含有透明像素。
为了方便示意,笔者在此亲手绘制了一下棋盘格,由rgba(181,181,181,1)与rgba(211,211,211,0.3)两种灰度交错而成,应用滤镜效果的部分仅有gogoend的图案。
rgba(181,181,181,1)
rgba(211,211,211,0.3)
gogoend
【例2-2】 我们来提取原图中的SourceGraphic,即源图像
<filter id="filter"> <feTile in="SourceGraphic"/> </filter>
可得到:
默认情况下,若此处in为空,in的值即为SourceGraphic
SourceAlpha
表示的是元素在应用滤镜之前的透明通道(源图像的Alpha),以灰度图表示
【例2-3】
我们来提取【例2-1】原图中的SourceAlpha,即源图像中的Alpha通道
<filter id="filter"> <feTile in="SourceAlpha"/> </filter>
此处全透明像素颜色全白,不透明像素颜色全黑,半透明像素部分为灰色
下方的四个关键字 —— BackgroundImage、BackgroundAlpha、FillPaint、StrokePaint或许可以直接跳过,相关图片仅仅是笔者的猜想而非实际运行结果。
BackgroundImage
BackgroundAlpha
FillPaint
StrokePaint
~表示当前使用滤镜的元素后方的内容~
经过测试这个原语并不生效。~根据规范所描述,这个原语输出的结果应当为:~
~由于这里结果是直接叠加在原图上的,且原图没有通过通过其它滤镜引入进来,因此原图应该会直接消失,仅保留背景;而这里留下的背景又和背景成为一体,因此为方便表述,这里画了一个红框。最终拿到的结果即为红框中所框住部分(原先图片下方)的内容(这里是棋盘格),可将提取出的内容交由下一个滤镜原语进行处理。~
~表示当前使用滤镜的元素后方内容的Alpha通道,以灰度图表示~
~这里实际上是输出了棋盘格的Alpha通道。rgba(181,181,181,1)填充部分展示为全白,rgba(211,211,211,0.3)填充部分展示为较暗的灰色~
~表示当前使用滤镜的元素中的填充,取应用滤镜元素的fill属性~
fill
~即图像中的填充部分~
~表示当前使用滤镜的元素中的描边,取应用滤镜元素的stroke属性~
stroke
~即图像中的描边部分~
很尴尬,经过各种姿势的测试与查阅,BackgroundImage、BackgroundAlpha、FillPaint、StrokePaint四个关键字,在Chrome与Firefox中,似乎都不生效,因此我在此使用删除线划掉了以上描述,与之对应的图片也仅仅是笔者的猜测。虽然规范中规定了这几个值,但浏览器可能并没有对此来进行实现。虽然MDN的这篇文章SVG 1.1 Support in Firefox - SVG: Scalable Vector Graphics“Filter Module”部分大意是说Firefox浏览器实现了这四个关键字中的FillPaint和StrokePaint,不过经过笔者实测似乎并没有效果。
你可以打开CodePen来查看这6个关键字的demo。
这类原语可以从无到有,或通过引入图像,来生成SVG滤镜的图像源(输入源),可在下方图像处理滤镜元素的in属性中引用。这类原语没有in属性,专用于产生图像。在使用这些原语时,若不通过其它原语使用源图像SourceGraphic,那么源图像将会被替换他们生成的图像替换掉。
该原语用于使用某种颜色来对图像进行填充。
flood-color
设置填充色,默认为#000
#000
flood-opacity
设置填充透明度,默认为1
1
【例2-4】
<filter xmlns="http://www.w3.org/2000/svg" id="filter"> <feFlood flood-color="#e32247" flood-opacity="1" result="4CD827A1-2842-4D2D-BD12-FCF306D53C5D"/> </filter>
该原语引入了一个外部图像。
preserveAspectRatio
是否保留图像比例
默认值为xMidYMid meet,即引入的图片将会被放在滤镜元素生效范围中间,且保持比例。
xMidYMid meet
我们可以发现这个属性所接受的值中间有空格来进行分割,因此这里的值由两部分来组成,即:<align> [<meetOrSlice>]。
<align> [<meetOrSlice>]
align表示图像短边如何对齐,默认即为xMidYMid,可选值包括:
align
xMidYMid
none - 表示图像将被拉伸到占满滤镜生效范围。当设置为这个值时,由于图像已经占满了整个滤镜生效范围,因此图像也不必发生裁切,因此第二部分(meetOrSlice)的值将恒为meet
none
meetOrSlice
meet
xMinYMin | xMinYMid | xMinYMax | xMidYMid | xMidYMin | xMidYMax | xMaxYMin | xMaxYMid| xMinYMax - 表示引入的图像将会被等比缩放,但图像会相对滤镜生效范围进行对齐。
xMinYMin
xMinYMid
xMinYMax
xMidYMin
xMidYMax
xMaxYMin
xMaxYMid
虽然这些属性枚举出来有些繁杂,但我们可以来总结出一个表格,看看它们的含义:
meetOrSlice表示引入的图像被等比缩放后,超出图像范围的长边将如何裁切。
slice
xlink:href / href
外部图像的地址。早前这一属性为xlink:href,但根据规范中的定义,xlink:href已被抛弃,目前应当使用href
xlink:href
href
来以近期掘金上活动的logo来做个示例。
如图:
【例2-5】
通过<img>引入SVG,且SVG中的外部图片来自Data URL:
<img>
<filter xmlns="http://www.w3.org/2000/svg" id="filter"> <feImage href="https://img.bagevent.com/resource/20210610/1714587612971445.png" result="7CA83715-60E2-47AE-AD1F-D30D5C3E5B8D"/> </filter>
可能你发现了,此处截图和代码中的href值不一致,截图中为baseURL,代码中为http请求的URL。在此笔者解释一下,在制作这个原语的示例时出现了一个坑,当使用SVG图像通过<img>来在HTML中进行引入时,如果SVG中通过<feImage>原语以及<image>元素引入的图片的URL来自网络,则SVG展示时,并不会发起请求来这些外部图片,这就导致外部图片展示为空白;除非你引入的图片的URL为Data URL,否则仅当SVG内联到HTML中或单独展示时,其中<feImage>及<image>引入的图片才生效。 在这里,左上角缩略图是一张通过<img>标签引入的svg图像,因此发生了这种情况。由于直接在此贴base64 URL太长,因此这里代码中写的是图片的真实URL。下同。 通过<img>SVG引入,且SVG中的外部且图片来自网络请求: 单独打开SVG: <filter xmlns="http://www.w3.org/2000/svg" id="filter"> <feImage href="https://img.bagevent.com/resource/20210610/1714587612971445.png" result="0A0CDDF8-24B3-40A1-B04D-A581C8A5D1C4"/> </filter>
可能你发现了,此处截图和代码中的href值不一致,截图中为baseURL,代码中为http请求的URL。在此笔者解释一下,在制作这个原语的示例时出现了一个坑,当使用SVG图像通过<img>来在HTML中进行引入时,如果SVG中通过<feImage>原语以及<image>元素引入的图片的URL来自网络,则SVG展示时,并不会发起请求来这些外部图片,这就导致外部图片展示为空白;除非你引入的图片的URL为Data URL,否则仅当SVG内联到HTML中或单独展示时,其中<feImage>及<image>引入的图片才生效。
<feImage>
<image>
在这里,左上角缩略图是一张通过<img>标签引入的svg图像,因此发生了这种情况。由于直接在此贴base64 URL太长,因此这里代码中写的是图片的真实URL。下同。
通过<img>SVG引入,且SVG中的外部且图片来自网络请求:
单独打开SVG:
<filter xmlns="http://www.w3.org/2000/svg" id="filter"> <feImage href="https://img.bagevent.com/resource/20210610/1714587612971445.png" result="0A0CDDF8-24B3-40A1-B04D-A581C8A5D1C4"/> </filter>
【例2-6】将preserveAspectiveRatioalign部分(第一个值)设为none:
preserveAspectiveRatio
此时图片被拉伸占满了滤镜生效范围。
<filter xmlns="http://www.w3.org/2000/svg" id="filter"> <feImage href="https://img.bagevent.com/resource/20210610/1714587612971445.png" preserveAspectRatio="none" result="0A0CDDF8-24B3-40A1-B04D-A581C8A5D1C4"/> </filter>
PS:如果足够细致你应该会发现页面中图片内容没展示全。如果你还记得上篇文章我们所提到的滤镜区域的概念,相信你已经知道为什么了吧~
【例2-6】将preserveAspectiveRatioalign部分(第一个值)设为其他值,来看看在保持比例的情况下,此处被引入图像如何对齐。
【例2-7】再将preserveAspectiveRatiomeetOrSlice部分(第二个值)设为slice,来看看此处被引入图像超出滤镜生效范围后将如何对齐。
这一滤镜主要用于产生噪波图像,产生的噪波是包含颜色以及Alpha通道的。
type
噪波类型
可选值为 fractalNoise | turbulence
fractalNoise
turbulence
fractalNoise、turbulence分别表示分型噪波、湍流噪波,主要表示了两种不同的噪波生成算法。
【例2-6】 这是相关代码,下列示例将在这一代码基础上进行修改:
<filter id="filter1"> <feTurbulence type="turbulence" baseFrequency="0.02" numOctaves="10" stitchTiles="stitch" seed="0.05" result="B90FB311-12A2-4CAF-AAC7-044B4D4F8043"/> </filter> <filter id="filter2"> <feTurbulence type="fractalNoise" baseFrequency="0.02" numOctaves="10" stitchTiles="stitch" seed="0.05" result="B55AA3B1-8BAC-4260-944C-07FBEFCE37E3"/> </filter>
baseFrequency
X、Y 方向上噪波的频率
值可以为整数或小数,接受1 ~ 2个值
如果用易于理解但很不准确的方式来表达,这个值可指代噪波的缩放。此处可类比正弦函数有关频率的定义。
X、Y方向上的频率可以分别设置。如果设置的是1个值,则X、Y方向的频率均为这个值;如果设置的是2个值,则X方向取第1个值,Y方向取第2个值。
以湍流噪波turbulence为例,我们来设置不同的baseFrequency值,可以发现左图有种被压扁的感觉 —— 看起来是不是有点点像河流中的水波荡漾呀?这或许就是它被翻译做“湍流噪波”的原因了吧。
numOctaves
噪波的细节
值为整数
浏览了一下网上的其它文章,这里笼统地将这个值概括为噪波的细节也只是一种易于理解但很不准确的表述。如果这个值设置的足够小,噪波会变得很模糊;这个值越大,产生的噪波的细节也就越多;当这个值设置足够大以后,限于图像分辨率,将不会呈现更多细节。如图所示:
seed
用于产生噪波的随机种子。从下图可以看出,种子数量不同,产生的噪波也不相同。
值为一个数值
stitchTiles
产生的噪波图案在向四周平铺时是否是平滑连续的(好像有个专门的名词叫“四方连续图案”)
可选值包括stitch | noStitch,即“连续”和“不连续”,默认值为noStitch
stitch
noStitch
早前笔者尝试多次都没看出这个属性有何效果(想象不出这个图案应当怎样连续排列),除了图像四周可能略有变化。笔者参照了一下张鑫旭大神的demo —— 看起来应该要多个应用此滤镜的元素一起拼起来才能看出效果。
【例2-7】
因此,笔者在CodePen上试着做了一个demo:将滤镜应用到多个使用transform来确定位置的元素上
若stitchTiles="noStitch":
stitchTiles="noStitch"
可以发现元素与元素间噪波图案的衔接并不平滑,可以看到很明显的接缝。因此,我们调整一下这个属性。若stitchTiles="stitch",则:
stitchTiles="stitch"
这就几乎看不到接缝了。
【例2-8】
值得注意的是,这些使用滤镜效果的元素每一个都是使用transform来确定位置的,而非使用x、y。你可以看另一个demo:将滤镜应用到多个使用x、y来确定位置的元素上
transform
x
y
从图中可以发现,这些元素都使用了同一滤镜,且噪波是连续、不中断的。因此大致可以知道噪波总是起始于<svg>元素的左上角,而非起始于应用滤镜的元素的左上角。下方元素在应用滤镜时,则使用滤镜区域对噪波进行裁切,且噪波取的是滤镜区域内。在上方【例2-7】的例子里,由于没有对应用滤镜的元素的x、y属性进行设置,因此它们将保持0,即所有元素都在<svg>左上角,裁切得到的是同一块区域的噪波。而使用transform属性进行位移变换后,噪波的裁切区域仍然保持,因此使用这种方式可以使用多个元素来对同一块噪波区域进行平铺。
<svg>
好了,以上就是图像引入篇的全部内容了~ 本篇主要介绍的内容是SVG滤镜第一步 —— 图像的引入。正如巧妇有了米,方能做出好吃的饭,在我们有了图像源以后,接下来的流程那自然就是对图像来进行处理啦。尽情关注笔者接下来的其它文章。
总目录:
图像引入
俗话说,巧妇难为无米之炊。如果说
巧妇 === SVG滤镜
,那米 === 图像
。因此,要想让SVG滤镜发挥作用,我们就需要把要处理的图像传递给SVG滤镜原语中。那么要如何做到这一点呢?下面我们就来看一些能够为SVG滤镜输入图像的关键字和原语吧
用于对原图像进行引用的关键字
这些关键字可以作为图像处理滤镜原语的
in
属性的输入值,用来输入图像。SourceGraphic
表示的是元素在应用滤镜之前本来的样子(源图像)
我们来看一个例子
【例2-1】 这是原图,包含有透明像素。
为了方便示意,笔者在此亲手绘制了一下棋盘格,由
rgba(181,181,181,1)
与rgba(211,211,211,0.3)
两种灰度交错而成,应用滤镜效果的部分仅有gogoend
的图案。【例2-2】 我们来提取原图中的
SourceGraphic
,即源图像可得到:
默认情况下,若此处
in
为空,in
的值即为SourceGraphic
SourceAlpha
表示的是元素在应用滤镜之前的透明通道(源图像的Alpha),以灰度图表示
【例2-3】
我们来提取【例2-1】原图中的
SourceAlpha
,即源图像中的Alpha通道可得到:
此处全透明像素颜色全白,不透明像素颜色全黑,半透明像素部分为灰色
BackgroundImage
~表示当前使用滤镜的元素后方的内容~
经过测试这个原语并不生效。~根据规范所描述,这个原语输出的结果应当为:~
~由于这里结果是直接叠加在原图上的,且原图没有通过通过其它滤镜引入进来,因此原图应该会直接消失,仅保留背景;而这里留下的背景又和背景成为一体,因此为方便表述,这里画了一个红框。最终拿到的结果即为红框中所框住部分(原先图片下方)的内容(这里是棋盘格),可将提取出的内容交由下一个滤镜原语进行处理。~
BackgroundAlpha
~表示当前使用滤镜的元素后方内容的Alpha通道,以灰度图表示~
经过测试这个原语并不生效。~根据规范所描述,这个原语输出的结果应当为:~
~这里实际上是输出了棋盘格的Alpha通道。
rgba(181,181,181,1)
填充部分展示为全白,rgba(211,211,211,0.3)
填充部分展示为较暗的灰色~FillPaint
~表示当前使用滤镜的元素中的填充,取应用滤镜元素的
fill
属性~经过测试这个原语并不生效。~根据规范所描述,这个原语输出的结果应当为:~
~即图像中的填充部分~
StrokePaint
~表示当前使用滤镜的元素中的描边,取应用滤镜元素的
stroke
属性~经过测试这个原语并不生效。~根据规范所描述,这个原语输出的结果应当为:~
~即图像中的描边部分~
很尴尬,经过各种姿势的测试与查阅,
BackgroundImage
、BackgroundAlpha
、FillPaint
、StrokePaint
四个关键字,在Chrome与Firefox中,似乎都不生效,因此我在此使用删除线划掉了以上描述,与之对应的图片也仅仅是笔者的猜测。虽然规范中规定了这几个值,但浏览器可能并没有对此来进行实现。虽然MDN的这篇文章SVG 1.1 Support in Firefox - SVG: Scalable Vector Graphics“Filter Module”部分大意是说Firefox浏览器实现了这四个关键字中的FillPaint
和StrokePaint
,不过经过笔者实测似乎并没有效果。你可以打开CodePen来查看这6个关键字的demo。
用于生成图像的滤镜原语
这类原语可以从无到有,或通过引入图像,来生成SVG滤镜的图像源(输入源),可在下方图像处理滤镜元素的
in
属性中引用。这类原语没有in
属性,专用于产生图像。在使用这些原语时,若不通过其它原语使用源图像SourceGraphic
,那么源图像将会被替换他们生成的图像替换掉。feFlood - 填充
该原语用于使用某种颜色来对图像进行填充。
flood-color
设置填充色,默认为
#000
flood-opacity
设置填充透明度,默认为
1
【例2-4】
feImage - 引入外部图像
该原语引入了一个外部图像。
preserveAspectRatio
是否保留图像比例
默认值为
xMidYMid meet
,即引入的图片将会被放在滤镜元素生效范围中间,且保持比例。我们可以发现这个属性所接受的值中间有空格来进行分割,因此这里的值由两部分来组成,即:
<align> [<meetOrSlice>]
。align
表示图像短边如何对齐,默认即为xMidYMid
,可选值包括:none
- 表示图像将被拉伸到占满滤镜生效范围。当设置为这个值时,由于图像已经占满了整个滤镜生效范围,因此图像也不必发生裁切,因此第二部分(meetOrSlice
)的值将恒为meet
xMinYMin
|xMinYMid
|xMinYMax
|xMidYMid
|xMidYMin
|xMidYMax
|xMaxYMin
|xMaxYMid
|xMinYMax
- 表示引入的图像将会被等比缩放,但图像会相对滤镜生效范围进行对齐。虽然这些属性枚举出来有些繁杂,但我们可以来总结出一个表格,看看它们的含义:
meetOrSlice
表示引入的图像被等比缩放后,超出图像范围的长边将如何裁切。meet
即引入的图像无论如何缩放,长边将会保持在滤镜生效范围内slice
即引入的图像缩放后,长边将会超出滤镜生效范围xlink:href / href
外部图像的地址。早前这一属性为
xlink:href
,但根据规范中的定义,xlink:href
已被抛弃,目前应当使用href
来以近期掘金上活动的logo来做个示例。
如图:
【例2-5】
通过
<img>
引入SVG,且SVG中的外部图片来自Data URL:【例2-6】将
preserveAspectiveRatio
align部分(第一个值)设为none:此时图片被拉伸占满了滤镜生效范围。
【例2-6】将
preserveAspectiveRatio
align部分(第一个值)设为其他值,来看看在保持比例的情况下,此处被引入图像如何对齐。【例2-7】再将
preserveAspectiveRatio
meetOrSlice部分(第二个值)设为slice
,来看看此处被引入图像超出滤镜生效范围后将如何对齐。feTurbulence - 产生噪波
这一滤镜主要用于产生噪波图像,产生的噪波是包含颜色以及Alpha通道的。
type
噪波类型
可选值为
fractalNoise
|turbulence
fractalNoise
、turbulence
分别表示分型噪波、湍流噪波,主要表示了两种不同的噪波生成算法。【例2-6】 这是相关代码,下列示例将在这一代码基础上进行修改:
baseFrequency
X、Y 方向上噪波的频率
值可以为整数或小数,接受1 ~ 2个值
如果用易于理解但很不准确的方式来表达,这个值可指代噪波的缩放。此处可类比正弦函数有关频率的定义。
X、Y方向上的频率可以分别设置。如果设置的是1个值,则X、Y方向的频率均为这个值;如果设置的是2个值,则X方向取第1个值,Y方向取第2个值。
以湍流噪波
turbulence
为例,我们来设置不同的baseFrequency
值,可以发现左图有种被压扁的感觉 —— 看起来是不是有点点像河流中的水波荡漾呀?这或许就是它被翻译做“湍流噪波”的原因了吧。numOctaves
噪波的细节
值为整数
浏览了一下网上的其它文章,这里笼统地将这个值概括为噪波的细节也只是一种易于理解但很不准确的表述。如果这个值设置的足够小,噪波会变得很模糊;这个值越大,产生的噪波的细节也就越多;当这个值设置足够大以后,限于图像分辨率,将不会呈现更多细节。如图所示:
seed
用于产生噪波的随机种子。从下图可以看出,种子数量不同,产生的噪波也不相同。
值为一个数值
stitchTiles
产生的噪波图案在向四周平铺时是否是平滑连续的(好像有个专门的名词叫“四方连续图案”)
可选值包括
stitch
|noStitch
,即“连续”和“不连续”,默认值为noStitch
早前笔者尝试多次都没看出这个属性有何效果(想象不出这个图案应当怎样连续排列),除了图像四周可能略有变化。笔者参照了一下张鑫旭大神的demo —— 看起来应该要多个应用此滤镜的元素一起拼起来才能看出效果。
【例2-7】
因此,笔者在CodePen上试着做了一个demo:将滤镜应用到多个使用transform来确定位置的元素上
若
stitchTiles="noStitch"
:可以发现元素与元素间噪波图案的衔接并不平滑,可以看到很明显的接缝。因此,我们调整一下这个属性。若
stitchTiles="stitch"
,则:这就几乎看不到接缝了。
【例2-8】
值得注意的是,这些使用滤镜效果的元素每一个都是使用
transform
来确定位置的,而非使用x
、y
。你可以看另一个demo:将滤镜应用到多个使用x、y来确定位置的元素上从图中可以发现,这些元素都使用了同一滤镜,且噪波是连续、不中断的。因此大致可以知道噪波总是起始于
<svg>
元素的左上角,而非起始于应用滤镜的元素的左上角。下方元素在应用滤镜时,则使用滤镜区域对噪波进行裁切,且噪波取的是滤镜区域内。在上方【例2-7】的例子里,由于没有对应用滤镜的元素的x
、y
属性进行设置,因此它们将保持0,即所有元素都在<svg>
左上角,裁切得到的是同一块区域的噪波。而使用transform
属性进行位移变换后,噪波的裁切区域仍然保持,因此使用这种方式可以使用多个元素来对同一块噪波区域进行平铺。好了,以上就是图像引入篇的全部内容了~ 本篇主要介绍的内容是SVG滤镜第一步 —— 图像的引入。正如巧妇有了米,方能做出好吃的饭,在我们有了图像源以后,接下来的流程那自然就是对图像来进行处理啦。尽情关注笔者接下来的其它文章。
总目录: