What is the easiest way to modify the color of a basic PNG image in a web browser?

While working on a website project, I encountered instructions for mouseover styles in the design that got me thinking:

It's straightforward to achieve with javascript or css image swapping... but here's the catch. There will be multiple icon links throughout the site - each paired with text, and some may require different colors or adjustments to the blue shade. It would be a hassle to create new versions of each image for every color variation, and it would be a nightmare if any color changes were needed. Wouldn't it be great if you could manage text and image colors in one place with css?

I did some research, and opinions on changing the color in an image seem divided between using CSS3 masks or utilizing SVG. CSS Masks are tempting due to their simplicity, but they have limited browser support - while SVG is recommended by W3C but requires images to be in .svg format (using .png options can get complicated and have spotty support).

So, does anyone know of a solution that allows me to effectively change the color of a flat .png image with good browser support? Extra points for simplicity/elegance, but I'm open to anything as long as it only needs a path to a .png image and a hex color. Thank you!


Edit 1: Just to clarify, this project excludes IE9-10 - we're focusing on compatibility with IE8 and 11. Thanks!


Edit 2: Other unsuccessful considerations

I thought about creating an inverted "stencil" version of each icon and overlaying it on a background color. However, this presents an issue for users and content editors since these icons will be used elsewhere, and a white stencil on a white background serves no purpose.

I also looked into the answers on this related question, but filters don't seem to accept hex values nor are they fully supported. Using an icon font might be an option, but it wouldn't be easily customizable for non-tech-savvy users - and having an easy "upload-to-cms" feature is crucial here.

Answer №1

Resolution & Example

The issue has been resolved, and here is a JSFiddle demo for reference.

###Advantages

  • User-friendly interface.
  • Available color selection in all JavaScript recognized formats.
  • Customizable icon colors excluding white.

###Drawbacks

  • Dependent on the tinycolor.js library to support various color formats. (Yet, it can be eliminated with self-created conversion functions)

  • Operates solely on IE9+ due to utilization of HTML5 canvas. (However, programming for IE always poses challenges).

  • Requires source images to reside on the same server as canvas cannot fetch from cross-origin sources, using Data URI for demonstration purposes. (There is a workaround available if desired).

##HTML Markup

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

Straightforward structure - an <input> field for selecting the target color, and a <button> for executing the changes. (Initial value mimics the light blue shade present in your default icon).

##JavaScript Logic

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 () {
    updateColor(text.value);
};

updateColor = 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 notTranslucent = 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();
}

##Elucidation

Focusing primarily on the modifyColor() function, while other code segments handle preparatory tasks.

  • Aligning width & height of <img> and <canvas>.
  • Drawing image onto the canvas.
  • Extracting pixel array data from entire canvas.
  • Examining data elements individually to retrieve pixel values.
  • Array[0] = Red
  • Array[1] = Green
  • Array[2] = Blue
  • Array[3] = Alpha.
  • Determining if the pixel is part of the background by checking opacity and presence of white.
  • Applying selected color transformation to pixels.
  • Updating canvas with modified data for rendering.
  • Converting canvas to Data URI, connecting it back to <img>.

#Additional Information

To avoid intensive setup processes, a library named AmazingIcon.js (Github) is provided for achieving similar outcomes:

##Head Section

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

##Body Section

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

Incorporating the data-src attribute allows creation of pseudo-custom attributes.

##Javascript Operations

AmazingIcon.parseDocument();

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

Running AmazingIcon.parseDocument() transforms designated anchors with amazingIcon class into Amazing Icons.

The AmazingIcon.hover(callback) feature enables hover events for all amazing icons, providing both (icon,event) parameters for customized actions. (Further information in documentation).

##Style Sheet Specifications

.AmazingIconObject{
      display: inline-block;
      vertical-align: top;
      text-align: center;
      margin-right: 5px;
      word-wrap: break-word;
      width: 100px;
      font-family: Verdana;
      font-weight: bold;
      color: lightcoral;
      margin-right: 5px;
      text-decoration: none;
}

For detailed insights regarding functionality, modifications or reproduction, refer to the Github repository for complete freedom in adjustments.

##Demonstration Preview

Due to limitations on showcasing with non-crossorigin content, visual depiction is presented for illustrative purpose.

Trusting this elucidates matters effectively. Until next time!

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Styling menus with CSS

I'm struggling with a CSS issue while attempting to add a 1px solid black line after each element in my drop-down table menu. The current appearance of my menu is causing some trouble. What I desire for it to look like involves applying border: 1px s ...

What is causing the 'transitionend' event to trigger multiple times?

Currently, I'm honing my JavaScript skills by taking on the 30 days of JavaScript challenge. I'm puzzled by why the 'transitioned' event is triggered twice in the code snippet below. My CSS only contains one property called transform, ...

Is there a way to showcase the uploaded file contents on the current page without the need to refresh?

Is there a way to display the contents of an uploaded file on the same HTML page without opening a new tab or refreshing it? I have the following HTML and PHP codes for reading an uploaded file in a separate page, but I want to integrate it into the same H ...

Ensure that a div element expands to 100% of its parent's height when its child's height is less than 100%

I am struggling with a flex div that contains 2 elements. I want both elements to take up the full parent height while their content remains at auto height, but I keep getting the inner content height. Here is my code: <html lang="en"> <head&g ...

Getting rid of the upper boundaries and outlines of the Bootstrap table

React code: <div style={{paddingLeft: '8px', paddingRight: '8px'}}> <div className="-subtitle"> <div className="pull-right" style={{marginBottom:'5px' ...

Unleashing the Power of the "OR" Operator in Form Field Validation

The custom JavaScript function FormQuote_Validator is used to validate form fields. It should return the value "TRUE" and display an alert message if all three input fields are submitted without any numbers; otherwise, it should return the value "FALSE". ...

How can I dynamically populate a select menu in HTML without the need to submit any data

Looking for help with an input form in HTML that uses a combination of input and select boxes. My goal is to dynamically populate a select menu based on the data entered into the input boxes. For example: Employee One: Jim Employee Two: John Employee Thre ...

Tips for displaying multiple XML datasets on an HTML webpage

I came across an example on W3schools that I'm trying to replicate: http://www.w3schools.com/xml/tryit.asp?filename=tryxml_app_first However, the example only displays one CD. My goal is to showcase all the data (in this case CDs) in the XML file. H ...

Create engaging text animation by transitioning from horizontally displayed text to vertically displayed text with the letters standing upright using An

I'm trying to create an animation where an acronym is displayed horizontally in the middle of the screen and then flips vertically. The challenge I'm facing is rotating individual letters once they are vertical so that the text reads from top to ...

Concealing objects identified by a specific id

I have two separate div elements var promptContent = $('<div id="errorr">').addClass("formErrorContent").html(promptText).appendTo(prompt); var arrow = $('<div id="errorrarrow">').addClass("formErrorArrow"); I need to refe ...

Troubleshoot: Issue with Navbar Dropdown Expansion on Bootstrap Sass 3.3.6 with JavaScript

Beginner: Bootstrap Sass 3.3.6 - Incorporating Javascript - Issue with Navbar Dropdown Not Expanding application.html.erb Head: <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %> ...

How can I position one DIV relative to another DIV?

Can a DIV be positioned relative to another DIV? My guess is that it involves nesting it inside the reference DIV and utilizing position: relative. However, I'm struggling with how to achieve this without impacting the content of the reference DIV. An ...

Repositioning with Flexbox: shifting the middle element to a new line

I have a flex item that contains three divs. ┌────────────────────────────────────────┐ | WRAPPER | | ┌─────────┬─ ...

Using jQuery to apply the "a" class with the addClass method

The html structure below is giving me trouble: <div class="sub"> <a href="#" id="people">People</a> </div> The CSS for the "a" element is as follows: color:#999999; font-size:31px; I am attempting to use jQuery to change ...

Using jQuery, learn how to successfully call a selector from dynamic content

I am currently facing a challenge with a table that is generated server-side and then appended to the view page (client-side). Since the table is not directly included in the DOM, I am using the StickyTableHeaders jQuery plugin to create a sticky header fo ...

Icons from Material Design Lite are not appearing as expected when integrated into an Angular project

Recently, I integrated MDL into my Angular project. While most of it is functioning properly, the MDL icons seem to be giving me trouble... I've implemented them using <i class="material-icons">share</i>, but instead of displaying as an i ...

What is the best way to ensure the checkbox functions correctly in this specific situation?

I am utilizing PHP, Smarty, and jQuery in the development of my website. Currently, I am working on implementing checkboxes within a Smarty template. Below is a snippet of my Smarty code related to checkboxes: <table cellpadding="5" cellspacing="0" bor ...

What is the method for triggering two actions within a single linked tag?

I have a link tag structured like this: <a href="<?php echo base_url().'dashboard' ?>" class="check_session">Home</a> Upon clicking the "Home" link, it should navigate to the dashboard. At the dashboard page, I want to check i ...

How can validation of input fields be implemented in React Js?

Currently, I am dealing with a variable (in my actual application it is an API) named data, which contains nested items. Starting from the first level, it includes a title that is already displayed and then an array called sublevel, which comprises multip ...

"Utilizing a hidden overflow combined with border-radius and translate3d for a

Is there a way to keep the corners of a block hidden when it has both overflow set to hidden and border radius applied while being translated? HTML <div class="scroller"> <div class="scroller-content"></div> </div> CSS .s ...