SVG滤镜实现Gooey效果

原文: The Gooey Effect | CSS-Tricks
作者: Lucas Bebber

一段时间前,Chris写了篇文章Shape Blobbing in CSS,效果很酷,使用的技术也很巧妙,但是这种使用CSS滤镜的方法有几个缺点:不支持透明,不能添加内容,很难适用于黑白以外的颜色等。

玩过一段时间svg之后,我发现可以使用svg滤镜来解决纯CSS方法中的大部分问题。可以看几个黏性按钮的栗子:

SVG 滤镜简介

SVG滤镜的功能非常强大,这是一个非常广泛的话题。这里我们只讲一下理解这个效果实现原理的必要的基础知识。

利用锚点指定,我们可以在大部分的浏览器中使用CSS给DOM元素应用SVG滤镜效果(一些不支持filter: url())。

这是定义filter的基础语法:

<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
  <defs>
    <filter id="name-your-filter-here">
      ...          
      <!-- 添加 filters -->
      ...
    </filter>
    ...
  </defs>
</svg>

在DOM元素上应用SVG滤镜效果:

.selector {
  filter: url('#name-of-your-filter-here');

  /* 你也可以使用这种方法加载外部SVG文件的滤镜: */
  filter: url('filters.svg#name-of-your-other-filter-here');
}

可能需要给filter属性添加一些浏览器前缀。

一个<filter>元素包含一个或多个滤镜,比如模糊,颜色转换,阴影等。完整的列表可以参考这里

来看一些例子:

<filter id="blur">
  <feGaussianBlur in="SourceGraphic" stdDeviation="3" />
</filter>

这个滤镜将单纯给对象添加3像素的模糊效果。注意in="SourceGraphic"in属性定义一个滤镜的输入,SourceGraphic关键词返回元素还没有应用滤镜效果的初始图形。这就意味着模糊滤镜的输入是对象的原始图形。非常直观。

现在来看一个常见但是更复杂一些的效果:投影滤镜。这有助于演示如何链式混合滤镜。

<filter id="drop-shadow">
  <feGaussianBlur in="SourceGraphic" stdDeviation="7" result="shadow" />
  <feOffset in="shadow" dx="3" dy="4" result="shadow" />
  <feColorMatrix in="shadow" type="matrix" values="0 0 0 0 0  0 0 0 0 0  0 0 0 0 0  0 0 0 0.6 0" result="shadow" />
  <feBlend in="SourceGraphic" in2="shadow" />
</filter>

注意一下第一个滤镜的result属性和之后一个的in属性。result可以命名一个滤镜的应用结果,然后可以对这个*结果*而不是原始图形添加滤镜。在上面的栗子中,它可以让对象模糊,加深*模糊对象*,并且移动*被加深的模糊对象*。

注意这里的最后一个<feBlend>滤镜。一些滤镜可以有多个输入(使用in2),并且在一个filter中可以多次并且在任意位置使用SourceGraphic关键字。例子中的最后一个滤镜使用了SourceGraphic关键字和shadow,最终把原始图形叠在了我们创建的阴影上。

看完了SVG滤镜基础,接下来看看如何实现gooey效果。

Making Things Stick

Making Things Stick 把效果组合起来?

基础的技术已经在这里有介绍,总结起来就是将两个或多个对象模糊,然后增加对比度。简单而奇妙:

但就像之前提到的那样:

  1. 颜色被混在一起,很难使用黑白以外的颜色
  2. 内容变得模糊不能使用
  3. 容器需要一个背景,所以不支持透明

总而言之,这个效果不是很实用

使用SVG滤镜,可以做一些在CSS滤镜中没法办到的事情:可以只增加alpha通道的对比度,而不改变颜色;使用SroucrGraphic关键字让内容部分依然清晰可用。同时,因为处理了透明通道,不只是它是透明的,一个透明的背景也是必须的,请注意这一点。

基础代码:

<filter id="goo">
  <feGaussianBlur in="SourceGraphic" stdDeviation="10" result="blur" />
  <feColorMatrix in="blur" type="matrix" values="1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 18 -7" result="goo" />
  <feBlend in="SourceGraphic" in2="goo" />
</filter>

代码很短,我们拆开来看:

  1. 首先我们对原始图像SourceGraphic添加了10像素的高斯模糊效果,并命名
  2. 然后对于之前的结果,应用了颜色矩阵滤镜,来增加alpha通道的对比度
  3. 最后,将原始图形叠放在我们创建的效果上面

关于颜色矩阵

如果你之前没有使用过Color Matrix,可能需要一些说明。你可以把它想象成一个四行五列的表格。它看起来像这样:

   | R | G | B | A | +
---|-------------------
 R | 1 | 0 | 0 | 0 | 0
---|-------------------
 G | 0 | 1 | 0 | 0 | 0
---|-------------------
 B | 0 | 0 | 1 | 0 | 0
---|-------------------
 A | 0 | 0 | 0 | 1 | 0
---|-------------------

每一行代表一个通道(红,绿,蓝,透明)并设置通道的值。前四列的每一列也同样代表一个通道,它们返回所代表通道的当前值。单元格中的数字将给它所在行代表的通道,添加它与列通道当前值相乘的结果。比如,R行G列的数字0.5,将给每一个像素的红色通道,添加它原始颜色的绿色通道值*0.5。最后一列不代表任何通道,只做一些值的增减,意味这里的数字将给通道添加它的值乘255的结果。

补充 这里是个矩阵计算,右边RGBA是像素原本的值,左边是计算后得到的值

| R' |    | a00 a01 a02 a03 a04 |   | R |
| G' |    | a10 a11 a12 a13 a14 |   | G |
| B' |  = | a20 a21 a22 a23 a24 | * | B |
| A' |    | a30 a31 a32 a33 a34 |   | A |
                                    | 1 |

例如下面这样(相当于没有改变原图)
1 0 0 0 0 // R = 1*R + 0*G + 0*B + 0*A + 0
0 1 0 0 0 // G = 0*R + 1*G + 0*B + 0*A + 0
0 0 1 0 0 // B = 0*R + 0*G + 1*B + 0*A + 0
0 0 0 1 0 // A = 0*R + 0*G + 0*B + 1*A + 0

这是一个冗长的解释,但是滤镜使用着更简单一些。在我们的例子中,因为我们只需要增加透明通道的对比度,所以我们的矩阵看起来是这样:

   | R | G | B | A | +
---|-------------------
 R | 1 | 0 | 0 | 0 | 0
---|-------------------
 G | 0 | 1 | 0 | 0 | 0
---|-------------------
 B | 0 | 0 | 1 | 0 | 0
---|-------------------
 A | 0 | 0 | 0 |18 |-7
---|-------------------

这里RGB通道没有改变,透明通道乘以18然后减去7*255,只对透明部分增加了对比度。这些值可以根据你的需要调整。

计算结果是将透明度大于某个值的变成了完全不透明,小于他的变成完全透明

把这个矩阵应用到feColorMatrix滤镜上,我们需要做的只是按顺序写上数字:

values="1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 18 -7"

Demo

至此我们已经得到了基本的效果:

你可以按照需要调整,比如添加投影,给每个元素添加不同的颜色,或者其他任何的效果。

注意

  • 滤镜需要添加给元素的容器,而不是元素本身
  • 容器需要一些空余空间,要比它的内容稍大一些,否则可能会影响到边缘

  • 给滤镜添加给有圆角的对象,还要使用稍微复杂点的方法。除了给原始图像叠在滤镜效果上面,还需要使用<feComposite>滤镜的atop运算屏蔽效果外多余的部分。
<filter id="fancy-goo">
  <feGaussianBlur in="SourceGraphic" stdDeviation="10" result="blur" />
  <feColorMatrix in="blur" type="matrix" values="1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 19 -9" result="goo" />
  <feComposite in="SourceGraphic" in2="goo" operator="atop"/>
</filter>

这样我们不止能添加有趣的goo效果,还能创建圆倒角。

兼容性

SVG滤镜支持行比较好,但是并不是所有的浏览器都支持将它应用在DOM元素上,比如Safari。但至少在Firefox和Chrome浏览器,甚至是它们的安卓版本,都能得到支持,并在无效的时候优雅降级。如果一定需要这个效果并且兼容良好,可以考虑使用SVG元素而不是DOM元素。

Comments
Write a Comment