10

I am interested in dynamically modifying an image with transparent background, and achieving that using CSS.

What I actually need, is creating a kind of a silhouette, so that all non-transparent pixels, have a color applied to them. In this case, Black.

The before and after should look something like this:

Before enter image description here After

Note both images have a transparent background.

Is there a method that can be used to perform this using CSS?

If not, is there an easy way to generate the silhouette and switch between the two images, on the client side, in a webpage context? Modern browsers can be assumed.

Any kind of help is greatly appreciated.

Selfish
  • 6,023
  • 4
  • 44
  • 63
  • 1
    Use an image editor, or a programming language of some kind. This isn't a job for CSS. –  Sep 24 '15 at 21:00
  • Thanks, though as mentioned, this has to be done dynamically, as I cannot predict the image that's to be used. If this were a single image I wouldn't have bothered. That said, I wouldn't be surprised if there WAS a trick in CSS's sleeve. – Selfish Sep 24 '15 at 21:03
  • This can be done in CSS, give me a few minutes. – knocked loose Sep 24 '15 at 21:05
  • 1
    `filter: grayscale(100%) contrast(0%) brightness(0%);` play around with this, but it won't completely fit your needs, I guess – pkn Sep 24 '15 at 21:06
  • @TinyGiant this is a great job for css, it's much more flexible: you can handle this for any uploaded image, without server image processing overhead, and for internal websites where images might be prepared, you don't need every admin to be able to use photo-shop – Toni Leigh Sep 24 '15 at 21:11
  • @Toni I still wouldn't use CSS for this. –  Sep 24 '15 at 21:12
  • @Toni http://caniuse.com/#feat=css-filters –  Sep 24 '15 at 21:14
  • The only thing I could think of is that IE probably won't play nicely with it. – knocked loose Sep 24 '15 at 21:14
  • it would be something to weigh up, css would be easier on devs / servers / users, but would have quite a large visual impact on IE users (all 8% or so) - could you put up with that? IE does also have a much better update process now, plus there probably are workarounds – Toni Leigh Sep 24 '15 at 21:24

2 Answers2

15

An effect like this can be achieved using webkit filters:

img {
    -webkit-filter: grayscale(100%);
    -webkit-filter: contrast(0%);
    -webkit-filter: brightness(0%);
}

img:hover {
    -webkit-filter: grayscale(0%);
    -webkit-filter: contrast(100%);
    -webkit-filter: brightness(100%);
}

and a jsfiddle to demonstrate: http://jsfiddle.net/7Ljcgj79/

Note that this method won't be support on all browsers. To support IE, you could set this as a background-image and change it on hover.


Using two images for better browser compatibility

If you're willing to use two images, you can achieve the same effect with much wider browser support by simply swapping the image on hover. Something like this:

div {
    height: 400px;
    width: 400px;
    background-image: url('https://i.stack.imgur.com/Pmz7l.png');
    background-repeat: no-repeat;
}

div:hover {
    background-image: url('https://i.stack.imgur.com/gZw5u.png');
}

And an updated fiddle: http://jsfiddle.net/7Ljcgj79/2/


Improved example supporting all colors

I hadn't visited this post in awhile, and I definitely see now that it could have been improved (all I had to do was set brightness to 0%, nothing else was necessary and in fact had no effect). I wanted to give an updated answer, though, in response to a comment. This solution takes a little more work, but supports all colors, not just black! Here are the important bits:

HTML

<svg>
    <filter id="monochrome" color-interpolation-filters="sRGB">
        <!-- change last value of first row to r/255 -->
        <!-- change last value of second row to g/255 -->
        <!-- change last value of third row to b/255 -->
        <feColorMatrix type="matrix" 
            values="0 0 0 0 0.6588 
                    0 0 0 0 0.4745 
                    0 0 0 0 0.1686 
                    0 0 0 1 0" />
    </filter>
</svg>

CSS

img {
    -webkit-filter: url(#monochrome);
    filter:  url(#monochrome);
}

img:hover {
    filter: none;
}

And an updated fiddle: http://jsfiddle.net/7Ljcgj79/16/

This technique takes advantage of the <fecolormatrix>, basically an advanced feature for defining your own filters. What I did here was to turn all color channels down to zero (all zeros for the first four columns), then add to them the constant value that I needed (that's the last column). Make sure in your <filter> tag to have type="matrix" and and in your <fecolormatrix> set color-interpolation-filters="sRGB" or it will interpret your matrix differently.

These posts were super helpful if you want to learn more: https://alistapart.com/article/finessing-fecolormatrix and https://css-tricks.com/color-filters-can-turn-your-gray-skies-blue/

Platte Gruber
  • 2,935
  • 1
  • 20
  • 26
  • nicely done, I knew it was filters, I just haven't used them enough to answer quickly - you ought to link to some browser support stuff, I think this will be important in this example – Toni Leigh Sep 24 '15 at 21:09
  • 1
    No need to confine just to webkit: http://jsfiddle.net/orvn/7Ljcgj79/1/ (No IE compatibility though) – Orun Sep 24 '15 at 21:09
  • If you only want to support half of the users on the internet, this is a great solution. –  Sep 24 '15 at 21:16
  • Works like a charm in chrome. Thanks! As mentioned, this fails on Edge/IE. I hope I can force it to play nice. – Selfish Sep 24 '15 at 21:19
  • @NitaiJ.Perez check out the updated answer if you need further browser support. – Platte Gruber Sep 24 '15 at 21:23
  • Thanks @pgruber, using two images will take me back to looking for an image manipulation solution, and unless I'm missing on some super-easy JS solution, I'm not sure I want to get into it. Let me play with CSS a little. – Selfish Sep 24 '15 at 21:25
  • I have just accepted the answer. Thanks for the effort. Apparently there's no solution for IE, so this is indeed best practice. Thanks. – Selfish Sep 29 '15 at 15:50
  • 1
    @pgruber I am currently doing something similar to this but i wish to have a colored filter for example #a8792b any idea how i could get the desired output? Any help will be greatly appreciated. :) – Dylan Anlezark Oct 25 '17 at 00:22
  • 1
    @DylanAnlezark hey man, definitely more of a challenge! But I think I found the answer, check out the update! – Platte Gruber Oct 25 '17 at 02:41
  • @pgruber Thank you for your help, with what you have provided i will be able to complete my project your the man!! thanks again – Dylan Anlezark Oct 25 '17 at 04:11
1

What you will need is two things:

  1. Insure that the image WILL ALWAYS be a PNG with transparent Background
  2. Use of the filter property.

HTML

<img src="http://www.iceni.com/blog/wp-content/uploads/2013/08/EBay_logo.png">

CSS

img{
    -webkit-filter: drop-shadow(20px 0px 2px rgb(0,0,0));
    filter: url(#drop-shadow);
    -ms-filter: "progid:DXImageTransform.Microsoft.Dropshadow(OffX=12, OffY=12, Color='#000')";
    filter: "progid:DXImageTransform.Microsoft.Dropshadow(OffX=12, OffY=12, Color='#000')";
}

And here is a JSFiddle

knocked loose
  • 3,142
  • 2
  • 25
  • 46