使用CSS过滤器模拟Photoshop的“颜色叠加”?

时间:2015-04-05 15:06:41

标签: css3 image-manipulation hsv color-space css-filters

我有一个图标,我想用CSS改变颜色。它是在CSS中内联的数据优化的SVG中。

通常情况下,此不可能。这就是图标字体被发明的原因;它们相对于SVG的主要优势是能够从CSS中接收colortext-shadow规则。好吧,CSS filters现在能够同时处理任意图像,they now work in all Blink, Webkit and Gecko browserscan be expected for future IE/Spartan

text-shadow更换很容易;只需使用drop-shadow filter

尽管所有必要的滤镜都在这里,但是,将图像着色为特定的颜色已被证明非常棘手。到目前为止,我的理论如下:

  • 使用contrast(0)将整个图像变为纯灰色,同时保持Alpha通道(Mozilla维基称它变黑了,但在所有浏览器中它变为灰色,必须是拼写错误)。
  • 使用sepia(1),因为我们无法对灰色图像上的色调/饱和度进行操作。这样可以确保整个图像由我们可以进行数学运算的参考颜色组成;具体而言,#AC9977

此时,我们应该能够使用hue-rotatesaturatebrightness将整个图像从现在的实体#AC9977转换为我们想要的任何颜色。

首先,浏览器使用什么颜色坐标?我无法理解the spec是否确定它是否正在使用HSL (Lightness) or HSV (Value),但由于HSB( Brightness )是HSV的另一个名称,我想它正在使用HSV。此外,使用像brightness(999)这样的东西会使颜色饱和(而不是使它们变成白色),这会在HSV中发生,而不会在HSL中发生。

基于这个假设,我们将按如下方式进行:

  • 计算#AC9977与我们想要的颜色之间的色调差异,并使用hue-rotate
  • 计算两者之间的饱和度差异,并使用saturate
  • 计算两者之间的亮度差异,并使用brightness

由于这不是手工完成的事情,我们将使用LESS preprocessor

.colorize(@color) {
    @sepiaGrey: #AC9977;
    @hOffset: (hsvhue(@color) - hsvhue(@sepiaGrey)) * 1deg;
    @sRatio: unit(hsvsaturation(@color) / hsvsaturation(@sepiaGrey));
    @vRatio: unit(hsvvalue(@color) / hsvvalue(@sepiaGrey));
    -webkit-filter: contrast(0) sepia(1) hue-rotate(@hOffset) saturate(@sRatio) brightness(@vRatio);
    filter: contrast(0) sepia(1) hue-rotate(@hOffset) saturate(@sRatio) brightness(@vRatio);
}

根据我的理解,这应该有效。 But it isn't为什么以及如何使其发挥作用?

我正在努力实现的例子

将图标视为图像或元素(背景图像,基于CSS的形状等),使用任何颜色,并使用透明度定义的形状(可以简单重叠的矩形图像。我想让它完全由CSS的特定颜色组成(大概是使用filters)。

example

我计划将其实现为带有颜色参数的LESS mixin,但只需指导HSB函数背后的逻辑即可。

4 个答案:

答案 0 :(得分:3)

我有时试图达到你想要的目标,并且没有成功。

无论如何,您还可以使用混合模式:



div {
    background-color: green;
    mix-blend-mode: color;
    position: absolute;
    width: 200px;
    height: 400px;
}

<div></div>
    <img src="http://upload.wikimedia.org/wikipedia/commons/thumb/a/a0/Hsl-hsv_models.svg/400px-Hsl-hsv_models.svg.png" height="400">
&#13;
&#13;
&#13;

我想念透明度要求。让我们再试一次:-)。缺点:您需要将图像设置2次。

&#13;
&#13;
#test1 {
  background: linear-gradient(red, red), url("http://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png");
  width: 100%;
  height: 500px;
  background-blend-mode: hue;
  -webkit-mask-image: url("http://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png");
}

body {
  background-color: lightblue;
}
&#13;
<div id="test1">
</div>
&#13;
&#13;
&#13;

确定;让我们说想要的结果是:你有一个图像,它将充当面具。您希望使用此蒙版在现有图像上设置彩色叠加,但您希望在CSS样式中指定颜色,以便可以轻松编辑。

如果您可以更改图像,那么要使用的通道是亮度而不是alpha,以下示例可以是您的解决方案 你需要一个灰色和黑色的滤镜,比如这个

mask file

&#13;
&#13;
.test {
        width: 200px;
    height: 200px;
    display: inline-block;
    background-image: url(http://i.stack.imgur.com/kxKXy.png); 
    background-size: cover;
    background-blend-mode: exclusion;
    mix-blend-mode: hard-light;
}

.testred {
    background-color: red;
}

.testblue {
    background-color: blue;
}

body {
    background: repeating-linear-gradient(45deg, lightblue 0px, lightyellow 50px);
}
&#13;
<div class="test testred"></div>
<div class="test testblue"></div>
&#13;
&#13;
&#13;

答案 1 :(得分:1)

您必须创建一个通过CSS过滤器引用的SVG过滤器才能接近这一点。它不是真正的叠加混合 - 需要混合模式。但我认为它实际上可以让你得到你想要的东西。请注意,滤镜中的色调旋转基本上是破坏的 - 它只是RGB空间中的近似值,它会使饱和的颜色非常错误。 (它实际上是使用原始的SVG过滤器数学)。

<svg width="800px" height="600px">
<defs>
  <filter id="fakeOverlay">
    <feColorMatrix type="luminanceToAlpha" result="L2A"/>
    <feFlood flood-color="cyan" result="colorfield"/>
    <feBlend mode="multiply" in="L2A" in2="colorfield"/>
    <feComposite operator="in" in2="SourceGraphic"/>
  </filter>
  </defs>
<image filter="url(#fakeOverlay)" width="800" height="400" xlink:href="http://i.stack.imgur.com/Mboab.png"/>
  
</svg>

答案 2 :(得分:0)

查看有关CSS Masks的这篇文章:http://thenittygritty.co/css-masking

答案 3 :(得分:0)

我在数学方面取得了一些进展,但它们并不漂亮;理想情况下,我相信在以下CSS过滤器中最多可以表示任何颜色:

(-webkit-)filter: contrast(0) sepia(1) hue-rotate(X) saturate(Y) brightness(Z);

换句话说,理想情况下,我们应该能够将任何颜色表示为相对于棕褐色灰色(#AC9977)的色调,饱和度和亮度坐标。

虽然我仍然没有办法做到这一点(也不确定是否可能),但我设法制作了一个接受任何纯色(R,G,B,C,M,Y)的实现或任何中性色(白色,黑色和灰色)。一些是优化的(如黑色只是brightness(0))。此外,如果您指定的颜色具有透明度,则该透明度将添加为opacity过滤器。

这是迄今为止的代码(用LESS编写):

// Filter prefixer.
.filter(@filters) { -webkit-filter+_: @filters; filter+_: @filters; }

// Helper that conditionally adds opacity filter when color calls for it.
._conditional-opacity(@color) when (alpha(@color) < 1) {
  .filter(round(opacity(alpha(@color)), 3));
}

// Helper that adds a brightness filter when necessary.
._conditional-brightness(@channel) when (@channel < 255) {
  .filter(brightness(round(@channel / 255, 3)));
}

// Special case for pure black.
.colorize(@color) when (fade(@color, 100%) = #000) {
  .filter(brightness(0));
  ._conditional-opacity(@color);
}

// Special case for pure grey and off-by-one-grey.
.colorize(@color) when (fade(@color, 100%) = #7F7F7F),
                       (fade(@color, 100%) = #808080) {
  .filter(contrast(0));
  ._conditional-opacity(@color);
}

// Special case for shades of pure red.
.colorize(@color) when (red(@color) > 0)
                   and (green(@color) = 0)
                   and (blue(@color) = 0) {
  .filter(contrast(0) sepia(1) saturate(999));
  ._conditional-brightness(red(@color));
  ._conditional-opacity(@color);
}

// Special case for shades of pure green.
.colorize(@color) when (red(@color) = 0)
                   and (green(@color) > 0)
                   and (blue(@color) = 0) {
  .filter(contrast(0) sepia(1) hue-rotate(99deg) saturate(999));
  ._conditional-brightness(green(@color));
  ._conditional-opacity(@color);
}

// Special case for shades of pure blue.
.colorize(@color) when (red(@color) = 0)
                   and (green(@color) = 0)
                   and (blue(@color) > 0) {
  .filter(contrast(0) sepia(1) hue-rotate(199deg) saturate(999));
  ._conditional-brightness(blue(@color));
  ._conditional-opacity(@color);
}

// Special case for shades of pure cyan.
.colorize(@color) when (red(@color) = 0)
                   and (green(@color) > 0)
                   and (blue(@color) = green(@color)) {
  .filter(contrast(0) sepia(1) invert(1) saturate(999));
  ._conditional-brightness(blue(@color));
  ._conditional-opacity(@color);
}

// Special case for shades of pure magenta.
.colorize(@color) when (red(@color) = blue(@color))
                   and (green(@color) = 0)
                   and (blue(@color) > 0) {
  .filter(contrast(0) sepia(1) hue-rotate(-99deg) saturate(999));
  ._conditional-brightness(red(@color));
  ._conditional-opacity(@color);
}

// Special case for shades of pure yellow.
.colorize(@color) when (red(@color) > 0)
                   and (green(@color) = red(@color))
                   and (blue(@color) = 0) {
  .filter(contrast(0) sepia(1) hue-rotate(199deg) saturate(999) invert(1));
  ._conditional-brightness(green(@color));
  ._conditional-opacity(@color);
}

// Special case for shades of pure grey and white.
.colorize(@color) when (red(@color) = green(@color))
                   and (green(@color) = blue(@color))
                   and not (blue(@color) = 0) // We've optimized these before.
                   and not (blue(@color) = 127)
                   and not (blue(@color) = 128) {
  .filter(contrast(0) brightness(round(blue(@color) / 255 * 2 + .00765, 3)));
  ._conditional-opacity(@color);
}

.colorize(@color) when (default()) {
  // General case not figured out yet.
}

如果你想玩它,here's a codepen(它自动编译LESS)。

请注意,这还不够好,如果您发布更好的答案(包括使用不同的方法来解决问题),我可能会接受您的,并且不接受我自己的答案,除非它可以代表任何给定的颜色(目前它不能;我现在可能已经放弃了它。)