使用SVG滤镜替换一种颜色

5

能否将一张IMG图片中的某种颜色替换为另一种颜色,比如将#fff替换为#000,同时保持其他颜色不变?可能可以使用SVG滤镜中的颜色矩阵来实现。

1个回答

14
使用Canvas很容易实现,但也可以使用SVG,但比较复杂。以下方法适用于常规完全不透明的图像。首先,您需要使用长ComponentTransfer将每个通道中每个非匹配的颜色值转换为零,并将每个匹配的颜色值转换为1(您256个成员tableValues数组中唯一“1”的索引应与r、g和b替换值匹配)。然后,您可以使用颜色矩阵将除结果白色像素之外的所有内容的alpha设置为零。您将结果用作具有目标颜色的feFlood的蒙版,并将其合成到原始图形上方。例如,以下代码将特定颜色 - rgb(87, 78, 29) - 替换为蓝色。

<svg width="600px" height="600px" viewBox="0 0 600 600">
  <defs>
    <filter id="color-replace" color-interpolation-filters="sRGB">
      <!-- Replace rgb(87,78,29) with blue. -->
      <feComponentTransfer >
        <feFuncR type="discrete" tableValues="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0"/>
        <feFuncG type="discrete" tableValues="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0"/>
        <feFuncB type="discrete" tableValues="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0"/>
      </feComponentTransfer>

      <feColorMatrix type="matrix" values="1 0 0 0 0
                                           0 1 0 0 0
                                           0 0 1 0 0
                                           1 1 1 1 -3" result="selectedColor"/>

      <feFlood flood-color="blue"/>
      <feComposite operator="in" in2="selectedColor"/>
      <feComposite operator="over" in2="SourceGraphic"/>
    </filter>
  </defs>
  <g filter="url(#color-replace)">
    <rect  x="50" y="50" height="100" width="100" fill="rgb(86,77,28)"/>
    <rect  x="250" y="50" height="100" width="100" fill="rgb(86,77,29)"/>
    <rect  x="450" y="50" height="100" width="100" fill="rgb(86,78,29)"/>
    <rect  x="50" y="250" height="100" width="100" fill="rgb(87,77,29)"/>
    <rect  x="250" y="250" height="100" width="100" fill="rgb(87,78,29)"/>
    <rect  x="450" y="250" height="100" width="100" fill="rgb(87,78,30)"/>
    <rect  x="50" y="450" height="100" width="100" fill="rgb(88,78,30)"/>
    <rect  x="250" y="450" height="100" width="100" fill="rgb(88,79,29)"/>
    <rect  x="450" y="450" height="100" width="100" fill="rgb(88,79,30)"/>
  </g>
</svg>

更新:

如果你想用一个透明度小于1的颜色替换原来的颜色,那么你需要额外进行一步操作——在替换之前移除所选的颜色。你可以通过扩展这个过滤器来实现这一点。

  <--snipping first half of filter -->

  <feColorMatrix type="matrix" values="1 0 0 0 0
                                       0 1 0 0 0
                                       0 0 1 0 0
                                       1 1 1 1 -3" result="selectedColor"/>

  <feComposite operator="out" in="SourceGraphic" result="notSelectedColor"/>
  <feFlood flood-color="blue" flood-opacity="0.5"/>
  <feComposite operator="in" in2="selectedColor"/>
  <feComposite operator="over" in2="notSelectedColor"/>
</filter>

我刚刚使用了这段代码的变体来替换iframe中的背景颜色。谢谢! - Menno van den Heuvel
这个功能非常好用,但是如果你想让目标颜色的 alpha 值不同,比如 0.5,该怎么办呢?flood-color="blue" flood-opacity="0.5"似乎不起作用,设置 <feFuncA/> 的 tableValues 为 128 "0" 和 128 "1" 也不行。 - Todd Main
添加了一个更新,允许您将替换颜色设置为可变不透明度。 - Michael Mullany
1
这种方法在Chrome(测试版本为77)或任何基于Webkit的浏览器中都不起作用。只有Firefox能够正确处理它。问题似乎出在feComponentTransfer上,其中Chrome以一种相当奇怪的方式解释了tableValues - Greegus
这在我的Chrome 77 / Win10中仍然有效。有几个Chrome GPU标志会破坏过滤器 - 也许您设置了其中之一? - Michael Mullany
我也无法在Chrome 78 / OSX 10.14.5上使其工作,当我尝试在codepad中运行或通过SO运行代码片段时。 - mjkaufer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接