23

I am working on a product that outputs images from users and the image information is overlayed on top of the aforementioned images. As you might imagine, the images require different text colors due to lightness/darkness. Is there a way to achieve this with JavaScript?

EDIT: I found a similar question to mine and there was a solution given in a jsfiddle (http://jsfiddle.net/xLF38/818). I am using jQuery for my site though. How would I convert the vanilla JavaScript to jQuery?

var rgb = getAverageRGB(document.getElementById('i')); document.body.style.backgroundColor = 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ')'; function getAverageRGB(imgEl) { var blockSize = 5, // only visit every 5 pixels defaultRGB = { r: 0, g: 0, b: 0 }, // for non-supporting envs canvas = document.createElement('canvas'), context = canvas.getContext && canvas.getContext('2d'), data, width, height, i = -4, length, rgb = { r: 0, g: 0, b: 0 }, count = 0; if (!context) { return defaultRGB; } height = canvas.height = imgEl.naturalHeight || imgEl.offsetHeight || imgEl.height; width = canvas.width = imgEl.naturalWidth || imgEl.offsetWidth || imgEl.width; context.drawImage(imgEl, 0, 0); try { data = context.getImageData(0, 0, width, height); } catch (e) { /* security error, img on diff domain */ alert('x'); return defaultRGB; } length = data.data.length; while ((i += blockSize * 4) < length) { ++count; rgb.r += data.data[i]; rgb.g += data.data[i + 1]; rgb.b += data.data[i + 2]; } // ~~ used to floor values rgb.r = ~~ (rgb.r / count); rgb.g = ~~ (rgb.g / count); rgb.b = ~~ (rgb.b / count); return rgb; } 
4
  • How about just using white text with dark text-shadow? Commented Jun 19, 2013 at 15:52
  • Hi Wesley, that's what I'm doing right now. It could still look better though, and I don't want to overdo it with the shadow. Commented Jun 19, 2013 at 16:04
  • did you find any working solution ??? Commented Sep 23, 2013 at 0:15
  • @user4o01, I forget, but I don't think I found anything especially useful that did what I wanted 100% and flawlessly. Commented Sep 27, 2013 at 2:58

3 Answers 3

12

I finally found something to do precisely what I want it to do! Enter Brian Gonzalez's jquery.adaptive-backgrounds.js. Check this out:

$parent.css({ // backgroundColor: data.color color: data.color }); 

I just commented out the backgroundColor rule and made a new one for color. For white text, a text-shadow like:

text-shadow: 0 0 1px rgba($black, 0.3); // using Sass 

should be enough. Thank you to everyone for your answers!

Sign up to request clarification or add additional context in comments.

Comments

6

This is possible using the canvas element. You would have to create a canvas element, draw the image element into the canvas, get the canvas's image data, look at the portion where the text is, convert those values to grayscale, average them, then compare them with a halfway point. Some example code:

var img = document.getElementById('myImage'); var c = document.createElement('canvas'); var ctx = c.getContext('2d'); var w = img.width, h = img.height; c.width = w; c.height = h; ctx.drawImage(img, 0, 0); var data = ctx.getImageData(0, 0, w, h).data; var brightness = 0; var sX = 0, sY = 0, eX = w, eY = h; var start = (w * sY + sX) * 4, end = (w * eY + eX) * 4; for (var i = start, n = end; i < n; i += 4) { var r = data[i], g = data[i + 1], b = data[i + 2]; brightness += 0.34 * r + 0.5 * g + 0.16 * b; if (brightness !== 0) brightness /= 2; } if (brightness > 0.5) var textColor = "#FFFFFF"; else var textColor = "#000000"; 

I haven't tested this code, though it should work. Make sure to change the sX, sY, eX, eY values to only the area where your text is, otherwise you will get unsatisfactory results (it will still work). Good luck!

EDIT: You will not have to display your image in any special way. Just make sure that the color of the overlay text is the variable textColor.

5 Comments

Thanks rvighne, in theory this sounds like it will work. How should I display my image though? Like <canvas><img/></canvas>? Also, these images will all have the same class. Am I right in assuming I can use getElementByClass?
EDIT: Nevermind about getting element by class name, I needed to use document.getElementsByClassName.
Loading the image into canvas is just that, it is loaded into the canvas object. There is no <img> in canvas at that point.
the brightness return NaN as a result. Anybody knows why?
That's quite strange. I will try to diagnose the problem.
-3

you could check the background-image attribute with jQuery then adjust the text color dynamically.

var x = $(body).attr("background-image"); switch(x) { case "something.png": // set color here break; } 

5 Comments

Hi user2501897, this looks good for a small project, but how would this work dynamically?
The image content/color is unknown so this is of no use.
Not dynamic in the sense it would pick up on the appropriate color from the image - but using the above code on form load (or at least after the background image is assigned) you could just declare when the image name is x the text color should be y.
By dynamic, I mean that I wouldn't know what the image looks like ahead of time. This is user-generated content, therefore I would not know which color to set the text as.
This suggestion, as provided, does not achieve the desired results. @rvighne has provided a proposed solution that addresses the question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.