CSS `filter`:通过覆盖白色将图像淡入白色

问题描述 投票:0回答:1

CSS filter 属性具有许多 filter 功能

我希望“洗掉”我正在应用的图像,就好像我在图像顶部覆盖了一个纯白色层并控制其不透明度。它应该从原始图像褪色为全白图像。

乍一看,

brightness()
过滤器似乎是我想要的。然而,这实际上是将所选因子乘以每个像素,使亮像素变亮,但使已经较暗的像素保持黑暗(当给定的值高于 1 时,如果低于 1,则使亮像素变暗)。

令我惊讶的是缺少在图像顶部覆盖半透明白色的滤镜。我怎样才能通过 CSS

filter
属性实现这一目标?

原创
Original image
CSS 过滤器,带有
brightness(1.5)
CSS 过滤器,带有
brightness(1.9)
Image with filter at 1.5 Image with filter at 1.9
所需(叠加 50% 不透明度白色) 所需(覆盖 90% 不透明度白色)
Image with an overlayed 50% opacity white layer Image with an overlayed 90% opacity white layer
css css-filters
1个回答
0
投票

代码

--factor: 0.5; /* Should range only between 0 and 1 */
filter: invert(calc(var(--factor) / 2)) brightness(calc(1 + var(--factor)));

这是通过将

brightness()
函数与
invert()
函数组合来实现的,如下所示。诀窍是当所需亮度为 100% 时,让
invert
达到
0.5
,并让
brightness
达到
2
。结果非常接近所需的滤镜,即使
--factor
参数在 0 到 1 之间进行动画处理,也应该足以产生视觉上预期的结果。

解决方案在
--factor: 0.5
解决方案在
--factor: 0.9
Approximated solution at 0.5 Approximated solution at 0.9

为什么这有效

观察

invert(0.5)
生成全灰色图像,其中每个像素都位于其逆向路径的中间,这意味着它们都在 0.5(中灰色)处相遇。运行以下代码片段以获取
invert(factor / 2)
。在
invert(1 / 2)
处,此操作的结果是将黑色中间色调白色全部映射到中灰色

但我们实际上想要将 blacksmidtoneswhites 映射到 white,这正是双中灰色。由于

brightness()
只是将每个像素乘以其给定值,因此我们可以使用
brightness(2)
将像素加倍。运行以下代码片段以获取
brightness(1 + factor)

将这两个函数组合在一起,我们首先将路径上的每个像素映射到中间灰色,然后将其加倍到白色。

交互式演示进行比较

document.addEventListener("DOMContentLoaded", () => {
    const img = document.querySelector("img");
    const input = document.querySelector("input");

    const update = () => document.body.style.setProperty("--factor", input.value);

    update();
    input.addEventListener("input", update);
});
#invert:checked ~ img {
    filter: invert(calc(var(--factor) / 2));
}

#brightness:checked ~ img  {
    filter: brightness(calc(1 + var(--factor)));
}

#combined:checked ~ div img  {
    filter: invert(calc(var(--factor) / 2)) brightness(calc(1 + var(--factor)));
}

#ground-truth:checked ~ div::after {
    content: "";
    background: rgba(255, 255, 255, var(--factor));
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
}

div {
    display: inline-block;
    position: relative;
}
<label><code>factor</code>:</label>
<input type="range" min="0" max="1" step="any" value="0.5" /><br />

<input type="radio" name="choice" id="invert" value="invert" />
<label for="invert"><code>invert(factor / 2)</code></label><br />

<input type="radio" name="choice" id="brightness" value="brightness" />
<label for="brightness"><code>brightness(1 + factor)</code></label><br />

<input type="radio" name="choice" id="combined" value="combined" checked />
<label for="combined"><code>invert(factor / 2) brightness(1 + factor)</code> (approximate filter)</label><br />

<input type="radio" name="choice" id="ground-truth" value="ground-truth" />
<label for="ground-truth">desired ground truth (correct filter)</label><br />

<div>
<img src="https://i.sstatic.net/k53TSb8R.png" alt="Test image" style="display: block" />
</div>

为了好玩:量化近似误差

为了一些有趣的数学探索,我分析了所需滤波器和近似滤波器之间的误差。

结果通常与图像上白色的线性插值相关,但方程并不完全相同。

所需滤波器的方程为:

f(factor, pixel) = (pixel) - (pixel)(factor) + (factor)

近似滤波器的方程为:

f(factor, pixel) = (pixel) + (0.5 - pixel)(factor)(factor) + (0.5)(factor)

从技术上讲,这种近似确实有一个白色值被剪裁的小区域(如下图灰色天花板平面上方所示)。正确滤波器和近似滤波器的比较(作为所选因子和输入像素通道亮度的函数)可以在此 3D 图中看到:https://www.desmos.com/3d/ldxipd3j1n

  • 蓝色表面代表正确的滤镜
  • 橙色表面代表近似滤波器
    • 一部分穿过灰色天花板平面上方,代表位于
      Z = 1
    • 处的白色剪裁点
  • 紫色表面代表两个滤波器函数之间的误差,对于所有可以想象的目的来说,该误差似乎总体上是平坦的并且是可接受的低值

3D graph visualizing the error between the filter functions

© www.soinside.com 2019 - 2024. All rights reserved.