CSS 滤镜:使用色相旋转仅更改某些颜色(如 Photoshop 色相/饱和度)

Mar*_*uto 2 html css hsl css-filters

我想知道是否可以使用 css/svg 滤镜执行色相/饱和度颜色转换,就像 Photoshop 色相/饱和度的工作方式一样。

本质上,根据我所读到的内容,Photoshop 在内部将所有像素从 RGB 表示转换为 HSL 表示,并根据用户使用定义的值基本上增加色调 (H)、饱和度 (S) 或亮度 (L) 值。滑块,如下图所示: Photoshop 色调/饱和度(默认)

在这里,我选择了默认范围 ( Master),这意味着将考虑所有色调值。

使用 CSShue-rotate过滤器,我们可以得到一个近似结果: 色调比较

由于另一个问题的答案中指出的问题,某些颜色并不完全相同:/sf/answers/1352779221/。(这对我来说没关系,我不需要它像 Photoshop 一样准确。)

因此,本质上,这两种方法的内部程序似乎大致相同。

现在,Photoshop 还允许我定义要考虑调整的颜色范围,如下图所示: 根据颜色范围调整色调

本质上,这个值范围意味着任何值hue超出范围限制的颜色都将被忽略。因此,在我的示例中,颜色 #1、#2 和 #5 保持不变。

我正在尝试使用 CSS os SVG 过滤器做同样的事情,但我找不到方法。我正在阅读滤镜效果文档(https://drafts.fxtf.org/filter-effects/),看看是否有任何东西可以用来定义范围,但我找不到任何东西。

有谁知道是否有办法做我想做的事?也许有 CSS 过滤器的有效替代品吗?

编辑:此片段显示了我得到的内容filter: hue-rotate(45deg)以及我想要获得的结果。

.block-wrapper {
  width: 100%;
  height: 50px;
  display: flex;
  margin-bottom: 10px;
}


.block {
  width: 20%;
  height: 100%;
}


.b1 {
  background-color: rgb(29 85 34);
}

.b1-goal {
  background-color: rgb(29 85 34);
}

.b2 {
  background-color: rgb(32 53 79);
}

.b2-goal {
  background-color: rgb(32 53 79);
}

.b3 {
  background-color: rgb(175 43 52);
}

.b3-goal {
  background-color: rgb(173 75 51);
}

.b4 {
  background-color: rgb(172 94 50);
}

.b4-goal {
  background-color: rgb(166 160 44);
}

.b5 {
  background-color: rgb(96 230 33);
}

.b5-goal {
  background-color: rgb(96 230 33);
}


.hue-45 {
  filter: hue-rotate(45deg);
}
Run Code Online (Sandbox Code Playgroud)
<h3>Original</h3>
<div class="block-wrapper">
  <div class="block b1"></div>
  <div class="block b2"></div>
  <div class="block b3"></div>
  <div class="block b4"></div>
  <div class="block b5"></div>
</div>

<h3>With <code>hue-rotate: 45deg;</code></h3>
<div class="block-wrapper hue-45">
  <div class="block b1"></div>
  <div class="block b2"></div>
  <div class="block b3"></div>
  <div class="block b4"></div>
  <div class="block b5"></div>
</div>

<h3>What I want: update hue only for red colors</h3>
<div class="block-wrapper">
  <div class="block b1-goal"></div>
  <div class="block b2-goal"></div>
  <div class="block b3-goal"></div>
  <div class="block b4-goal"></div>
  <div class="block b5-goal"></div>
</div>
Run Code Online (Sandbox Code Playgroud)

Phi*_*lip 5

这实际上很难立即完成,因为feColorMatrix处理 RGB 中的颜色,而在 HSL 中还没有办法做到这一点。(如我错了请纠正我。)

所以我找到了一个接近您想要的解决方案。这个想法是首先掩盖掉你不想进行色调旋转的颜色。然后对剩余部分进行色调旋转并将其粘贴到原始部分的顶部。

带过滤器的 SVG 代码如下所示:

<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
  <filter id="partial-hue-rotation">
    <!--
      1) Mask away the colors that shouldn't be hue-rotated.
        This is done based on the R-channel value only.
        The R-channel value comes in at [0-1],
        so multiply it by 255 to get the original value (as in rgb()).
        Then subtract (lowest R-channel value of color range - 1)
        to leave all color with a R-channel value higher than that.
    -->
    <feColorMatrix
      color-interpolation-filters="sRGB"
      type="matrix"
      in="SourceGraphic"
      result="only-red-visible"
      values="1 0 0 0 0
              0 1 0 0 0
              0 0 1 0 0
              255 0 0 0 -171"
    /><!-- Colors with R-channel > 171 will be left (and thus effected). -->

    <!--
      2) Apply hue rotation to remaining colors.
    -->
    <feColorMatrix
      type="hueRotate"
      values="45"
      in="only-red-visible"
      result="rotated-part"   
    />
    
    <!--
      3) Now paste the rotated part on top of the original.
    -->
    <feMerge>
      <feMergeNode in="SourceGraphic" />
      <feMergeNode in="rotated-part" />
    </feMerge>
  </filter>
  
  <!--
    This filter is to check if the right range is hue-rotated.
    All white areas will be rotated.
    The bottom row of values can be copied over the bottom row
    of the filter above.
  -->
  <filter id="test-partial-hue-rotation">
    <feColorMatrix
      color-interpolation-filters="sRGB"
      type="matrix"
      in="SourceGraphic"
      result="marked-range"
      values="0 0 0 0 1
              0 0 0 0 1
              0 0 0 0 1
            255 0 0 0 -171"
    /><!-- Colors with R-channel > 171 will be white. -->
    <feMerge>
      <feMergeNode in="marked-range" />
    </feMerge>
  </filter>
</svg>
Run Code Online (Sandbox Code Playgroud)

要应用过滤器,只需添加filter: url(#partial-hue-rotation)到元素的 CSS 中即可。

要测试是否实现了正确的颜色/部分,您可以添加filter: url(#test-partial-hue-rotation);到元素的 CSS。所有白色部分都将进行色调旋转。(您可能需要将父级的背景颜色设置为黑色才能看到它。)

注意事项和限制:

  1. 仅当您想要进行色调旋转的颜色可以由单个 RGB 通道分隔时,此方法才有效。例如:R 值 > X 的所有颜色。然后将 放在矩阵中 alpha 行的255第一列和-X最后一列中。(255第二列用于 B 值选择等)
  2. 这不是最终的解决方案,因为所有较浅的颜色(具有可能高于阈值的 R、G 和 B 值)也将进行色调旋转。
  3. 显然,由于 Alpha 通道用于遮罩,因此这只适用于不透明的颜色/内容。
  4. 此外,该滤镜针对特定颜色范围和旋转值进行了硬编码。因此,不太可扩展,但可能对个别实例有用。
  5. hue-rotate显然 CSS和feColorMatix's 的hueRotate计算方式也存在差异(来源)。这可以通过添加color-interpolation-filters="sRGB"hueRotate feColorMatrix标签来消除(不确定)。

不管怎样,这是第一次尝试,也许这种方法可以帮助你。:)

在这里工作 JSFiddle

更多信息:

feColorMatrix文档

有关如何计算色调旋转的颜色矩阵的更多信息,请参阅Chrome 浏览器的 C++ 实现

另请参见速记过滤器的矩阵等价

还有这个帖子

更新:版本 2

因此,经过一番阅读和思考后,我想到了使用混合模式difference为过滤器提供有关哪些颜色在“范围内”并且应该受到影响的信息。其工作原理如下:

  1. 用您的范围的中间颜色(例如红色)填充整个图像(区域)。
  2. <feBlend>在与原始模式和洪水模式不同的情况下使用。(最暗的部分与中间颜色的重叠最多,例如在色轮上最接近它。)
  3. 反转差异并将其转换为平均灰度。(由于我们需要的是纯数值平均值,因此所有通道均被取 0.3333 次。)
  4. 现在,我们使用 afeColorMatrix将此灰度转换为 alpha 值,同时将这些值映射到最低的 2/3 是透明的(将被删除)。
  5. 用于feComposite遮盖原始图像并将效果(色调旋转)仅应用于该部分。
  6. 然后将受影响的部分粘贴到原件的顶部。
  7. 完毕!

可以选择要影响的颜色范围的中点和宽度:

  • 中间颜色设置为flood-colorfeFlood。(使用完全饱和和 50% 亮度的颜色以获得最佳效果,所以#ff0000#00ff00等)
  • 宽度由 alpha 通道在feColorMatrixwith中的偏移量选择result="alpha-mask"。示例:保留色轮的 1/3 给出偏移值(2/3) * -255

在这里更新了工作中的 JSFiddle。(底部的一个,过滤#partial-hue-rotation。)

注意: 色调旋转效果做得很糟糕,所以不确定那里出了什么问题,但生成的颜色与CSS的过滤器相同hue-rotate()..所以,是的..

更新:版本 3

不幸的是,上面的过滤器并不适用于所有颜色SourceGraphic对于正确将 转换为灰度色调值(其中0deg = black和)的 SVG 滤镜360deg = white,请查看#hue-values我在此 JSFiddle中制作的滤镜。

如果您只想对所有红色/绿色/蓝色/青色/洋红色/黄色应用滤镜效果,则可以使用#tonegroup-select同一JSFiddle中的滤镜。

该过滤器的代码为:

<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
    <defs>
        <filter id="tonegroup-select"
            x="0%" y="0%"
            width="100%" height="100%"
            primitiveUnits="objectBoundingBox"
            color-interpolation-filters="sRGB"
        >
            <!-- Compare RGB channel values -->
            <feColorMatrix type="matrix" in="SourceGraphic" result="test-r-gte-g"
                values="0 0 0 0 0  0 0 0 0 0  0 0 0 0 0  255 -255 0 0 1"
            />
            <feColorMatrix type="matrix" in="SourceGraphic" result="test-r-gte-b"
                values="0 0 0 0 0  0 0 0 0 0  0 0 0 0 0  255 0 -255 0 1"
            />

            <feColorMatrix type="matrix" in="SourceGraphic" result="test-g-gte-r"
                values="0 0 0 0 0  0 0 0 0 0  0 0 0 0 0  -255 255 0 0 1"
            />
            <feColorMatrix type="matrix" in="SourceGraphic" result="test-g-gte-b"
                values="0 0 0 0 0  0 0 0 0 0  0 0 0 0 0  0 255 -255 0 1"
            />

            <feColorMatrix type="matrix" in="SourceGraphic" result="test-b-gte-r"
                values="0 0 0 0 0  0 0 0 0 0  0 0 0 0 0  -255 0 255 0 1"
            />
            <feColorMatrix type="matrix" in="SourceGraphic" result="test-b-gte-g"
                values="0 0 0 0 0  0 0 0 0 0  0 0 0 0 0  0 -255 255 0 1"
            />

            <!-- Logic masks for tone groups -->
            <!-- For example: all red colors have red channel values greater than or equal to the green and blue values -->
            <feComposite operator="in" in="test-r-gte-g" in2="test-r-gte-b" result="red-mask" />
            <feComposite operator="in" in="test-g-gte-r" in2="test-g-gte-b" result="green-mask" />
            <feComposite operator="in" in="test-b-gte-r" in2="test-b-gte-g" result="blue-mask" />
            <feComposite operator="in" in="test-g-gte-r" in2="test-b-gte-r" result="cyan-mask" />
            <feComposite operator="in" in="test-b-gte-g" in2="test-r-gte-g" result="magenta-mask" />
            <feComposite operator="in" in="test-r-gte-b" in2="test-g-gte-b" result="yellow-mask" />

            <!-- Select all colors in tone group -->
            <!-- Note: uncomment the right tone group selection here -->
            <!-- Note: greyscale colors will always be selected -->
            <feComposite operator="in" in="SourceGraphic" in2="red-mask" result="selection" />
            <!-- <feComposite operator="in" in="SourceGraphic" in2="green-mask" result="selection" /> -->
            <!-- <feComposite operator="in" in="SourceGraphic" in2="blue-mask" result="selection" /> -->
            <!-- <feComposite operator="in" in="SourceGraphic" in2="cyan-mask" result="selection" /> -->
            <!-- <feComposite operator="in" in="SourceGraphic" in2="magenta-mask" result="selection" /> -->
            <!-- <feComposite operator="in" in="SourceGraphic" in2="yellow-mask" result="selection" /> -->

            <!-- Cut selection from original image -->
            <!-- Note: use same mask for `in2` attribute as with selection -->
            <feComposite operator="out" in="SourceGraphic" in2="red-mask" result="not-selected-source" />

            <!-- Apply effects to `selection` only -->
            <feColorMatrix
                type="saturate"
                values="0"
                in="selection"
                result="edited-selection"   
            />
            <!-- After all effects, adjustments, etc -->
            <!-- the last `result` output name should be "edited-selection" -->

            <!-- Bring it all together -->
            <feMerge>
                <!-- <feMergeNode in="selection" /> --><!-- Uncomment to check selection -->
                <feMergeNode in="not-selected-source" />
                <feMergeNode in="edited-selection" />
            </feMerge>
        </filter>
    </defs>
</svg>
Run Code Online (Sandbox Code Playgroud)

在代码内的注释中,您可以找到有关工作原理的更多信息以及如何使用它的说明。

欲了解更多信息和参考,请查看: