Currently, I’ve been using this tutorial. But it is just not as clean As I’d have hoped, and I’m sure there is a better alternative.
I know I can do this:
.img-wrap{
position: relative;
}
.img-wrap img{
filter: grayscale(1);
width: 100%;
z-index: 0;
}
.img-wrap::before{
content:'';
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background: red;
z-index: 1;
mix-blend-mode: screen;
}
<div class="img-wrap">
<img src="https://picsum.photos/200/100/">
</div>
But I want to accomplish this without having to use a wrapper.
>Solution :
You can use SVG filters to do this.
img{
width: 100%;
filter: url(#duotone);
}
<img src="https://picsum.photos/200/100"/>
<svg xmlns="http://www.w3.org/2000/svg">
<filter id="duotone">
<feColorMatrix type="matrix" result="grayscale"
values="1 0 0 0 0
1 0 0 0 0
1 0 0 0 0
0 0 0 1 0" >
</feColorMatrix>
<feComponentTransfer color-interpolation-filters="sRGB" result="duotone">
<feFuncR type="table" tableValues="1 1"></feFuncR>
<feFuncG type="table" tableValues="0 1"></feFuncG>
<feFuncB type="table" tableValues="0 1"></feFuncB>
<feFuncA type="table" tableValues="0 1"></feFuncA>
</feComponentTransfer>
</filter>
</svg>
The way this works is:
- We define the filter as
#duotonein our SVG. - We convert the image to grayscale feColorMatrix.
- Finally, we apply a RGBA filter on top of that image. These values are set via the tableValues. The first of two values being the amount (from 0 → 1). A common way to understand this is by taking the RGBA values you are used to and dividing them by 255 (since thats the max). For example, a red value of 100 would be
100/255=0.3921OR<feFuncR type="table" tableValues="0.3921 1"></feFuncR>.