Reputation: 3025
During a site build, I came across this direction for mouseover styles in the design (paraphrased by me):
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.
Upvotes: 1
Views: 601
Reputation: 4135
I've solved the problem, here is a JSFiddle.
###Pros
###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.
<img>
's width & height with the <canvas>
.<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;
/* Separation 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.
Hope this helps, see ya!
Upvotes: 1