【问题标题】:Revert a filter invert() CSS rule恢复过滤器 invert() CSS 规则
【发布时间】:2018-02-11 11:45:58
【问题描述】:

我有一整段代码,其中应用了 CSS 规则 filter: invert(0.85)

在这个块里面,我有一张图片,当然也遵循这个 CSS 规则,并且是倒置的。

我需要为所述图片还原此invert()

有可能吗?我试过invert(1),但图像并不完全像以前一样,它仍然有点倒置(由于第一个invert只有0.85而不是1

看这个例子:

body{
  font-size: 0;
}

div{
  display: inline-block;
  width: 50%;
}

.filter{
  filter: invert(0.85);
}

.filter img{
  filter: invert(1);
}
<div class="initial">
  <img src="https://s7d1.scene7.com/is/image/PETCO/cat-category-090617-369w-269h-hero-cutout-d?fmt=png-alpha" alt="">
</div>
<div class="filter">
  <img src="https://s7d1.scene7.com/is/image/PETCO/cat-category-090617-369w-269h-hero-cutout-d?fmt=png-alpha" alt="">
</div>

【问题讨论】:

  • 不像其他过滤器或属性我猜反转不能被反转
  • @TemaniAfif 一定有办法实现这一点,不知何故:x
  • 我想您需要结合其他过滤器才能获得初始结果。好吧,我对过滤器没有经验 :) .. 但这就像您应用矩阵变换...您不能简单地反转数字,您需要反转整个矩阵,这可能有点棘手
  • 有什么进展吗? :) ...从我这边我研究了另一个过滤器,但找不到准确的东西。
  • @TemaniAfif 我仍在努力理解所有关于过滤器的 W3C 文档 ^^ 这非常困难。不过我不会放弃!

标签: css css-filters


【解决方案1】:

TL;DR

您无法通过应用另一个 invert() 或其他过滤器的组合来还原 invert() 过滤器。


首先,我将从一个基本示例和解释开始:invert() 过滤器用于通过指定反转百分比(或从01 的值)来反转颜色。如果我们使用值1,我们会完全反转颜色,因此很容易回到初始状态,因为我们只需再次应用相同的反转:

.container {
 display:flex;
 justify-content:space-around;
}

.inner {
  height: 200px;
  width: 200px;
  background: 
  linear-gradient(to right, rgb(255,0,0) 50%, rgb(0,255,255) 0) 0 0/100% 50% no-repeat, 
  
  linear-gradient(to right, rgb(50,0,60) 50%, rgb(205,255,195) 0) 0 100%/100% 50% no-repeat;
}
<div class="container" style="filter:invert(1)">
  <div class="inner"></div>
  <div class="inner" style="filter:invert(1)"></div>
</div>

从这个例子中,我们还可以了解反转是如何处理颜色的。我们只需为 RGB 的每个值执行(255 - x)

现在让我们考虑另一个值的反转,让我们采用0.85

.container {
  display: inline-block;
}

.inner {
  display: inline-block;
  height: 200px;
  width: 200px;
  background: linear-gradient(to right, rgb(255, 0, 0) 50%, rgb(0, 255, 255) 0) 0 0/100% 50% no-repeat, linear-gradient(to right, rgb(50, 0, 60) 50%, rgb(205, 255, 195) 0) 0 100%/100% 50% no-repeat;
}
<div class="container" style="filter:invert(0.85)">
  <div class="inner"></div>
</div>
<div class="inner"></div>

它是如何工作的?

对于第一种颜色(rgb(255,0,0)),我们得到这个(rgb(38, 217, 217)),所以计算如下:

255 - [255*(1-0.85) + x*(2*0.85-1)]

所以我们的目标是反转这个公式:

f(x) = 255 - [255*(1-p) + x*(2*p-1)] , p a value in the range [0,1]

您可能清楚地注意到,当p=0 时我们将拥有f(x)=xp=1 时我们将拥有f(x)=255-x,这非常容易。现在让我们用 f(x) 来表达x 的值(我这里用y

x = 1/(2*p-1) * [255 - (255*(1-p) +y)]

让我们试着让它类似于一个反转函数。让我们拥有y=(2*p-1)*y',我们将获得:

x = 1/(2*p-1) * [255 - (255*(1-p) +(2*p-1)*y')]

相当于

x = 1/(2*p-1) * f(y') ---> x = K * f(K*y) with K = 1/(2*p-1)

这里f 是使用p 相同值的反转函数,K 是基于值p 计算的常数。我们可以区分三种情况:

  1. p 的值等于0.5 时,函数没有定义,因此不能反转(你可以顺便尝试应用 invert(0.5) 来查看结果)
  2. 当p的值大于0.5时,K是[1,+infinity]范围内的值。
  3. 当p的值小于0.5时,K是[-infinity,-1]范围内的值。

所以如果我们省略第一种情况,K 可以这样设置K=1/K',其中 K' 是[-1,1]/{0} 范围内的值,abs(K')]0,1] 范围内的值。那么我们的函数可以写成如下:

x = (1/K') * f(y/K') where K' in the range [-1,1] define by (2*p - 1)

此时,我们使用invert 函数和乘法/除法来表达我们的函数,该值具有我们可以轻松计算的值。

现在我们需要找出哪个过滤器应用乘法/除法。我知道有使用线性变换的brightnesscontrast 过滤器。

如果我们使用brightness(p),论坛如下:

f(x) = x*p;

如果我们使用contrast(p),公式将如下所示:

f(x) = p*(x - 255/2) + 255/2

这将在这里结束......

正如最初所说,我们无法使用其他过滤器还原invert()。对于某些值,我们可能可以近似此值,但对于其他值则不可能(例如 0.5)。换句话说,当应用invert() 时,我们会丢失一些我们无法找回的信息。就像,例如,当您将彩色图像转换为黑白版本时。你无法恢复最初的颜色。

更新

这里有一些JS代码来证明上面的计算是正确的,并看看一些结果。在应用我的功能时,我使用画布再次绘制图像:

不幸的是,我不能在 sn-p 中使用图像来解决安全性和跨浏览器来源问题,所以我考虑了渐变

var canvas = document.querySelector('canvas');
var img = document.querySelector('img');
var ctx = canvas.getContext('2d');
//we draw the same image on the canvas (here I will draw the same gradient )
//canvas.getContext('2d').drawImage(img, 0, 0, img.width, img.height);
var grd = ctx.createLinearGradient(0, 0, 150, 0);
grd.addColorStop(0, "blue");
grd.addColorStop(1, "red");
ctx.fillStyle = grd;
ctx.fillRect(0, 0, 150, 150);

//we get the data of the image
var imgData = canvas.getContext('2d').getImageData(0, 0, 150, 150);
var pix = imgData.data;
var p = 0.85;
// Loop over each pixel and apply the function
for (var i = 0, n = pix.length; i < n; i += 4) {
  pix[i + 0] = Math.round((1 / (2 * p - 1)) * (255 - (255 * (1 - p) + pix[i + 0])));
  pix[i + 1] = Math.round((1 / (2 * p - 1)) * (255 - (255 * (1 - p) + pix[i + 1])))
  pix[i + 2] = Math.round((1 / (2 * p - 1)) * (255 - (255 * (1 - p) + pix[i + 2])))
  // i+3 is alpha (the fourth element)
}
//Draw the image again
imgData.data = pix;
canvas.getContext('2d').putImageData(imgData, 0, 0);
body {
  font-size: 0;
}

div,
canvas {
  display: inline-block;
}

.grad {
  height: 150px;
  width: 150px;
  background: linear-gradient(to right, blue, red);
}

.filter {
  filter: invert(0.85);
}
<div class="grad"></div>
<div class="filter">
  <div class="grad"></div>
  <canvas></canvas>
</div>

如我们所见,我们有 3 张图像:原始图像倒置的图像以及我们应用函数使其变回原始图像的图像.

我们在这里取得了不错的结果,因为该值接近1。如果我们使用更接近0.5 的另一个值,则会产生不好的结果,因为我们已经接近定义函数的位置:

var canvas = document.querySelector('canvas');
var img = document.querySelector('img');
var ctx = canvas.getContext('2d');
//we draw the same image on the canvas (here I will draw the same gradient )
//canvas.getContext('2d').drawImage(img, 0, 0, img.width, img.height);
var grd = ctx.createLinearGradient(0, 0, 150, 0);
grd.addColorStop(0, "blue");
grd.addColorStop(1, "red");
ctx.fillStyle = grd;
ctx.fillRect(0, 0, 150, 150);

//we get the data of the image
var imgData = canvas.getContext('2d').getImageData(0, 0, 150, 150);
var pix = imgData.data;
var p = 0.6;
// Loop over each pixel and apply the function
for (var i = 0, n = pix.length; i < n; i += 4) {
  pix[i + 0] = Math.round((1 / (2 * p - 1)) * (255 - (255 * (1 - p) + pix[i + 0])));
  pix[i + 1] = Math.round((1 / (2 * p - 1)) * (255 - (255 * (1 - p) + pix[i + 1])))
  pix[i + 2] = Math.round((1 / (2 * p - 1)) * (255 - (255 * (1 - p) + pix[i + 2])))
  // i+3 is alpha (the fourth element)
}
//Draw the image again
imgData.data = pix;
canvas.getContext('2d').putImageData(imgData, 0, 0);
body {
  font-size: 0;
}

div,
canvas {
  display: inline-block;
}

.grad {
  height: 150px;
  width: 150px;
  background: linear-gradient(to right, blue, red);
}

.filter {
  filter: invert(0.6);
}
<div class="grad"></div>
<div class="filter">
  <div class="grad"></div>
  <canvas></canvas>
</div>

您可以使用此代码创建一个通用函数,让您部分恢复invert()过滤器:

  1. 使用一些 JS 可以很容易地找到过滤器中使用的p 的值。
  2. 您可以使用特定的选择器仅针对要应用此逻辑的特定图像。
  3. 如您所见,我创建了一个 canvas,因此我们的想法是创建它,然后隐藏图像以仅保留画布。

这是一个更具交互性的演示:

var canvas = document.querySelector('canvas');
var ctx = canvas.getContext('2d');
var init = function() {
  var grd = ctx.createLinearGradient(0, 0, 150, 0);
  grd.addColorStop(0, "blue");
  grd.addColorStop(1, "red");
  ctx.fillStyle = grd;
  ctx.fillRect(0, 0, 150, 100);
  var grd2 = ctx.createLinearGradient(0, 0, 0, 150);
  grd2.addColorStop(0, "green");
  grd2.addColorStop(1, "yellow");
  ctx.fillStyle = grd2;
  ctx.fillRect(40, 0, 70, 100);
}
var invert = function(p) {
  //we get the data of the image
  var imgData = canvas.getContext('2d').getImageData(0, 0, 150, 100);
  var pix = imgData.data;
  // Loop over each pixel and apply the function
  for (var i = 0, n = pix.length; i < n; i += 4) {
    pix[i + 0] = Math.round((1 / (2 * p - 1)) * (255 - (255 * (1 - p) + pix[i + 0])));
    pix[i + 1] = Math.round((1 / (2 * p - 1)) * (255 - (255 * (1 - p) + pix[i + 1])))
    pix[i + 2] = Math.round((1 / (2 * p - 1)) * (255 - (255 * (1 - p) + pix[i + 2])))
    // i+3 is alpha (the fourth element)
  }
  //Draw the image again
  imgData.data = pix;
  canvas.getContext('2d').putImageData(imgData, 0, 0);
}
init();
$('input').change(function() {
  var v = $(this).val();
  $('.filter').css('filter', 'invert(' + v + ')');
  init();
  invert(v);
})
p {
  margin: 0;
}

div,
canvas {
  display: inline-block;
}

.grad {
  height: 100px;
  width: 150px;
  background: linear-gradient(to bottom, green, yellow)40px 0/70px 100% no-repeat, linear-gradient(to right, blue, red);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="grad"></div>
<div class="filter">
  <div class="grad"></div>
  <canvas height="100" width="150"></canvas>
</div>
<p>Adjust the value of the invert filter</p>
<input type="range" value="0" min=0 max=1 step=0.05>

从这个演示中我们还可以注意到,当我们接近值 0.5 时,我们不会得到一个好的结果,这仅仅是因为过滤器不能在 0.5 处反转,如果我们参考之前的计算,值的K 变得非常大,因此K' 太小了。

【讨论】:

  • 哇,这是一个深入的答案^^。我仍在努力解决255 - [255*(1-0.85) + x*(2*0.85-1)] 部分的问题,我不明白你是如何得到这个公式的。
  • @Zenoo 好吧,我在一些互联网搜索方面得到了帮助 :) 我以前从事图像处理工作,所以我不得不通过阅读几篇文章来了解计算是如何完成的,并在简化后结束了我的记忆用这个公式;)
  • 我查看了 W3C 定义的 invert() 过滤器,但我必须说我仍然不知道你是如何实现的 ^^
  • @Zenoo 很好,如果你坚持 :) 你可以看到 invert 使用了feComponentTransfer 并且你可以在 [这里] (w3.org/TR/filter-effects/#feComponentTransferElement) 阅读这个,如果你继续阅读你会结束通过这个feFunc* 使用 [table 类型] (w3.org/TR/filter-effects/#valdef-type-table) 应用于 3 种颜色 --> 有了这个你可以有一些线索 ;)
  • @Zenoo 如果你想要更复杂的东西,这里有一个处理许多过滤器和一些计算的问题,你可以在其中看到一些关于反转和一些公式的东西stackoverflow.com/a/43960991/8620333 ...然后玩围绕和测试我们最终得到一个我们可以使用的公式:)
【解决方案2】:

您需要结合使用其他 CSS filter()s 来还原更改。根据最初应用的filter(),这可能非常困难。

在这种特殊情况下,仅应用了invert(0.85),我们可以尝试其他几个filter()s 以使图像恢复正常。

通过一些实验,看来以下方法可行:

filter: invert(1) contrast(1.3) saturate(1.4);

这是一个粗略的猜测,但视觉上似乎可以解决问题。

.flex {
  width: 100%;
  display: flex;
}

img {
  width: 100%;
  display: block;
}

.flex > div{
  flex: 1;
}

.filter {
  filter: invert(0.85);
}

.filter img {
  filter: invert(1) contrast(1.3) saturate(1.4);
}

p {
  color: red;
}
<div class="flex">
  <div class="initial">
    <p>Some red text</p>
    <img src="https://s7d1.scene7.com/is/image/PETCO/cat-category-090617-369w-269h-hero-cutout-d?fmt=png-alpha" alt="">
  </div>
  <div class="filter">
    <p>Some red text</p>
    <img src="https://s7d1.scene7.com/is/image/PETCO/cat-category-090617-369w-269h-hero-cutout-d?fmt=png-alpha" alt="">
  </div>
</div>

【讨论】:

  • 我的问题不是关于如何避免过滤图像,而是 revert 已经应用于块的过滤器,仅适用于该块的某些元素。 .filter 已应用 filter: invert() 规则。我需要为里面的一些元素恢复这个。
  • 正如所指出的,无法撤消invert。我能想象到的最接近的方法是使用一系列其他filters 来使图像颜色恢复到初始状态,但这并不精确。例如,saturateinvert 等的某种组合。
  • 不,.filter 类必须直接应用filter,因为其中的DOM 非常复杂,在.filter * 上应用invert 会搞砸一切。如果@TemaniAfif 所说的是正确的,那么必须有其他过滤器的组合可以准确地呈现为初始图像。
  • “在 .filter * 上应用反转会搞砸一切” - 为什么?
  • 感谢您的近似!有没有办法找到这一切背后的确切数学?如果没有,我会去找你的答案,但我真的想要一个完全匹配的。
【解决方案3】:

其他答案显示了使用 CSS 实现这一点的困难。这是您可能会发现有用的基本 javascript 方法。您检测.filter 是否包含图像,如果包含,则删除类filter

var filteredDiv = document.querySelectorAll(".filter"),
  len = filteredDiv.length,
  i;

for (i = 0; i < len; i++) {
  if (filteredDiv[i].getElementsByTagName('img').length > 0) {
    filteredDiv[i].classList.remove('filter');
  }
}
body {
  font-size: 0;
}

.filter {
  filter: invert(.85);
}

div {
  display: inline-block;
  width: 200px;
}

img {
  width: 100%;
  height: auto;
}

div>div {
  background: yellow;
  font-size: 2rem;
}
<div class="filter">
  <img src="https://s7d1.scene7.com/is/image/PETCO/cat-category-090617-369w-269h-hero-cutout-d?fmt=png-alpha" alt="">
</div>
<div class="filter">
  <div>Sample text</div>
</div>

【讨论】:

  • 不确定这是否是一个解决方案,因为正如在其他答案中评论的那样,他不想删除过滤器..过滤器应该保持应用于整个块,并且在这个块内他想要恢复仅用于某些元素。
  • 谢谢,但我不能使用 Javascript 来解决这个问题,另外,您的代码超出了问题的目的。过滤器必须留在块上。
  • @Zenoo 够公平的。如果您找到一种使用 CSS 正确执行此操作的方法,我会感到惊讶。祝你好运
猜你喜欢
  • 2015-02-20
  • 2017-12-03
  • 2019-01-06
  • 1970-01-01
  • 1970-01-01
  • 2016-12-05
  • 1970-01-01
  • 1970-01-01
  • 2016-08-07
相关资源
最近更新 更多