1

During a site build, I came across this direction for mouseover styles in the design (paraphrased by me):

paraphrased comp style

This is simple enough to do with js (or even css) image swapping... but it made me ponder. There are going to be a few dozen such icon links across the site - each paired with text, and each potentially needing additional colors, or to have the blue color adjusted. It would be a headache to create a new version of each image for any additional colors, and it would be a nightmare if any colors needed to be adjusted. Wouldn't it be nice if you could control the text and image colors in one place with css?

I've done some research, and popular opinion on affecting the color in an image seems to be split between working with CSS3 masks or putting the whole works in SVG. CSS Masks are a very attractive option for their simplicity, but have one glaring support gap - whereas SVG is recommended by W3C but seems to rely on the images being in .svg format (the .png solutions get complicated and spotty support).

So, is anyone aware of a solution that would enable me to effectively change the color of a flat .png image, that provides adequate browser support? Bonus points for simplicity/elegance, but I'll accept anything that only requires a path to a .png image and a hex color. Thanks!


Edit 1: For the record, IE9-10 are out of scope here - we're only looking at 8 and 11. Thanks!


Edit 2: Other (failed) considerations

I've considered simply creating an inverted "stencil" of each icon and overlaying it on a background color. This, however, poses a problem for our users and content editors, since these icons will be used elsewhere and a white stencil on a white background is useless.

Also read the answers on this related question, but filters don't seem to accept a hex value nor are they fully supported. The icon font might work, but would not be easily extensible/modifiable to a non-savvy user - the "upload-to-cms" functionality is very desired here.

Community
  • 1
  • 1
CodeMoose
  • 2,964
  • 4
  • 31
  • 56
  • 3
    Have you tried font-icons? [**here**](http://fortawesome.github.io/Font-Awesome/) and [**here**](http://css-tricks.com/examples/IconFont/). You can control size, color, shadow, etc. really easily. If the icons are customized, you can also create your own font – undefined Jan 19 '15 at 21:37
  • @Rou just made an edit addressing that haha. The problem is that it would be difficult to edit/extend that font as more icons were needed. – CodeMoose Jan 19 '15 at 21:39
  • Rou is exactly right. There are dozens of sites with thousands of free font icons available for download. This is by far the easiest solution. There are also many sites and programs that will convert .png images to svg format. – Jake Taylor Jan 19 '15 at 21:42
  • @JakeTaylor I appreciate the suggestion, but these icons are pretty much all custom and need to be configurable by a non-savvy client - a font icon really won't work. – CodeMoose Jan 19 '15 at 21:44
  • 1
    Ahhhh non-savvy client editable....Good Luck :) – Jake Taylor Jan 19 '15 at 21:45
  • @JakeTaylor you begin to see my pain ;) – CodeMoose Jan 19 '15 at 21:46
  • I'm working on a fiddle, sit tight! – undefined Jan 19 '15 at 21:50
  • I have what you need, I'm afraid my internet provider is not working (i'm using my cellphone data and it isn't reliable), I'll post an answer as soon as I can. – undefined Jan 20 '15 at 03:35
  • I'm confused by the statement "these icons will be used elsewhere and a white stencil on a white background". Did you mean "white stencil on a NON-white background"? – Paul LeBeau Jan 20 '15 at 05:58
  • @PaulLeBeau not quite. The site has a white background - so if I use a white stencil on a colored background it will produce the desired effect. However, if one of my users places the image (read stencil) somewhere else on the site via a WYSIWYG, it will be placed on a white background and be rendered invisible. – CodeMoose Jan 20 '15 at 14:27
  • Hmm, my solution uses canvas, which is not [**supported in IE 8**](http://caniuse.com/#feat=canvas), are you still interested? – undefined Jan 21 '15 at 16:48
  • @Rou - hit me. Even if it ends up not fitting into scope, I'm definitely academically interested in such a solution. – CodeMoose Jan 21 '15 at 18:43

1 Answers1

1

Answer & Demo

I've solved the problem, here is a JSFiddle.

Pros

  • It's very easy to use.
  • You can select any color in any format known to JavaScript.
  • Your icon can have any color except white.

Cons

  • In order to be compatible with all the color formats, it needs the tinycolor.js library as a dependency. (However: this can be removed if you create your own color convertion functions)

  • It only works in IE9+ as it uses the HTML5 canvas. (Although to be honest coding for IE is always a pain).

  • The source images have to be in the same server, as canvas can't get tainted from cross-origin sources, and you can't get data from them, this is why the source image in the demo is in Data URI. (However: there is a solution if you are interested).

HTML

<input type="text" value="#669dbb">
<button>Change color</button>
<img src="data:image/png;base64,(..)">

Simple enough, an <input> for the color you want the image to change to, and a <button> to apply the changes. (By the way, the original value is the lightblueish you have on your default icon).

JavaScript

var img = document.querySelector("img");
var button = document.querySelector("button");
var text = document.querySelector("input");
var canvas = document.createElement("canvas");
var context = canvas.getContext("2d");

button.onclick = function () {
    changeColor(text.value);
};

changeColor = function (inputColor) {
    canvas.setAttribute("width", img.width);
    canvas.setAttribute("height", img.height);
    context.drawImage(img, 0, 0);
    var dataObject = context.getImageData(0, 0, canvas.width, canvas.height);
    var data = dataObject.data;
    for (var i = 0; i < data.length; i += 4) {
        var r = data[i],
            g = data[i + 1],
            b = data[i + 2],
            a = data[i + 3];
        var notTransparent = a != 0;
        var nonBackground = ( notTransparent && r != 255 && g != 255 && b != 255 );
        if ( nonBackground ) {
            var input = tinycolor(inputColor);
            var rgb = input.toRgb();
            data[i] = rgb.r;
            data[i + 1] = rgb.g;
            data[i + 2] = rgb.b;
            data[i + 3] = a;
        }
    }
    context.putImageData(dataObject, 0, 0);
    img.src = canvas.toDataURL();
}

Explanation

Okey, what's going on here, I'm going to center my explanation on the changeColor() function as the other code blocks are just background work.

  • First, we match the <img>'s width & height with the <canvas>.
  • Then, we draw the image to the canvas.
  • Then, we get the pixel array data from the whole canvas. (More on canvas pixel manipulation.
  • Then we read the data (wich is a loooong array) and get each pixel value.
    • Array[0] = Red
    • Array[1] = Green
    • Array[2] = Blue
    • Array[3] = Alpha.
  • Then, we check if the pixel is not a background pixel, by making sure is not transparent nor white.
  • If it isn't, we get the input color and make it RGB.
  • Then, we modify the pixel data and update the canvas.
  • Finally, we get an <img> source from the canvas, by transforming it to Data URI, and we update the <img>.

Just one more thing

Now, while that may be cool and all, it would take a lot of grunt-work to get going, so I've made it easier for you, here is a library that allows you to do what you want: AmazingIcon.js (Github)

It's not nearly polished, but it will do the job, here is how it works.

HTML(head)

<script src=tinycolor.js></script>
<script src=amazingicon.js></script>

HTML(body)

<a class="amazingIcon" href="some-url" data-src="icon-image-url">icon-label</a>

Fairly simple, we use the data-src to create a pseudo-custom attribute.

JavaScript

AmazingIcon.parseDocument();

AmazingIcon.hover(function(icon,ev){
    icon.setColor("lightblue");
});

The AmazingIcon.parseDocument() function will convert any anchor with the class amazingIcon to an Amazing Icon.

The AmazingIcon.hover(callback) function applies the hover event to all the amazing icons, the hover event function will recieve both (icon,event) arguments, which you can manipulate accordingly. (More on the documentation)

CSS

.AmazingIconObject{
    /* Display icons in the same row */
      display: inline-block;
    /* Aligning icons vertically inside parent */
      vertical-align: top;
    /* Centering labels */
      text-align: center;
    /* Sepparation between the icons */
      margin-right: 5px;
    /* Break label to fit icon width */
      word-wrap: break-word;
    /* Sets the width of the anchor (not the icon) */
      width: 100px;
    /* The following properties are design-subjective */
      font-family: Verdana;
      font-weight: bold;
      color: lightcoral;
      margin-right: 5px;
      text-decoration: none;
}

Very simple, if you'd like to know more of how it works of course it's all in the github, feel free to reproduce and modify the code as you and any other readers please.

Demostration

As it's difficult to use demonstrate with non-crossorigin sources, I'll just show you an image of what that would look like.

Demonstration of the AmazingIcon.js library.

Hope this helps, see ya!

undefined
  • 3,949
  • 4
  • 26
  • 38