0

I want to change the font color based on the color of the image. The font color must be black or white, but it should change proportionally to the color of the image as it does in the code below.

So, I have this js function that if you move the mouse it changes the background color and consequently also the text (in the snippet move the pointer under the background). Obviously the part where you move the mouse to change color doesn't interest me, it's just a test. Is there a way to apply the function to my personal div?

In simple terms I want the color of the text to change based on my background and not if you move the mouse. Sorry for the poor explanation, I'm relatively new here, trying to learn how to exercise this function. Currently I don't know how to do it, I appreciate any answer, thanks.

function getTextColor(rgba){
  rgba = rgba.match(/\d+/g);
  if((rgba[0]*0.299)+(rgba[1]*0.587)+(rgba[2]*0.114)>186) {
    return 'black';
  } else {
    return 'white';
  }
}

var div = document.createElement('div');
div.style.height = '100vh';
document.body.appendChild(div);

div.addEventListener('mousemove', (event)=>{
  var x = event.clientX;
  var y = event.clientY;
  div.textContent = `${x} , ${y}`;
  div.style.backgroundColor = `rgba(${x},${y},100,100)`;
  div.style.color = getTextColor(div.style.backgroundColor);
});
.background {
  height: 100vh;
  background: url("https://i.pinimg.com/originals/bc/a3/2d/bca32d5cdfabeaeb4faeb12bff160524.jpg");
}
<div class="background">Background example</div>
Snorlax
  • 183
  • 4
  • 27
  • 1
    To clarify, You would like to take the average color of the background and change the text color to either black or white depending on said average, correct? – Philip Clark Jul 26 '22 at 00:32
  • That's right, I'd like to take the medium color of the background. I think it's the right thing to do. This way the text should turn white if the background is too dark, or turn black if it is too light. Note, in my css the background is determined by background-image and not background-color. – Snorlax Jul 26 '22 at 00:45
  • It seems that this is not a fully supported functionality, but it does the job. [This thread explains it](https://stackoverflow.com/questions/13762864/image-brightness-detection-in-client-side-script) – Philip Clark Jul 26 '22 at 00:55

1 Answers1

2

Explanation


Here is an example showing how to get the average using a canvas rendered to a 1x1-px scale. From there we set the header background and then apply a light or dark class based on the luminance of the aforementioned average.

Notes


Note 1: There is some extra code to allow for easier visualization, this is commented and can be removed.

Note 2: The below code is not guaranteed to run on all browsers. Please see this thread for more specifics.

Note 3: This code only works with local images and CORS-enabled image URLs due to it's reliance on canvas.drawImage. Please see this for more information.

Example 1:

this example shows the functionality. It uses two image URLs in the JS to toggle between.

/* 
     
     THE TOGGLE ACTION
     
     */

/* The urls for toggling between */
const urlLight = 'https://images.unsplash.com/photo-1484503793037-5c9644d6a80a?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=930&q=80';
const urlDark = 'https://images.unsplash.com/photo-1517999144091-3d9dca6d1e43?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=627&q=80';
let currentUrl = urlLight;

const toggle = document.getElementById('toggle');

toggle.addEventListener("click", function() {
  if (currentUrl === urlLight) {
    currentUrl = urlDark;
  } else {
    currentUrl = urlLight;
  }

  getImageBrightness(currentUrl, function(brightness) {

    const header = document.getElementById("header");
    header.style.backgroundImage = `url(${currentUrl}`;

    header.classList.remove("dark");
    header.classList.remove("light");

    console.log(brightness);

    if (brightness > 225 / 2) {
      header.classList.toggle("dark");

    } else {
      header.classList.toggle("light");
    }
  });

});

/* 

Important get brightness code

*/

function getImageBrightness(imageSrc, callback) {
  var img = document.createElement("img");
  img.src = imageSrc;
  img.style.display = "none";
  img.crossOrigin = "anonymous";
  document.body.appendChild(img);

  var colorSum = 0;

  img.onload = function() {
    // create canvas
    var canvas = document.createElement("canvas");
    canvas.width = this.width;
    canvas.height = this.height;

    var ctx = canvas.getContext("2d");
    ctx.drawImage(this, 0, 0);

    var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
    var data = imageData.data;
    var r, g, b, avg;

    for (var x = 0, len = data.length; x < len; x += 4) {
      r = data[x];
      g = data[x + 1];
      b = data[x + 2];

      avg = Math.floor((r + g + b) / 3);
      colorSum += avg;
    }

    var brightness = Math.floor(colorSum / (this.width * this.height));
    callback(brightness);
  }
}



getImageBrightness(currentUrl, function(brightness) {

  const header = document.getElementById("header");
  header.style.backgroundImage = `url(${currentUrl}`;

  header.classList.remove("dark");
  header.classList.remove("light");


  if (brightness > 225 / 2) {
    header.classList.toggle("dark");

  } else {
    header.classList.toggle("light");
  }
});
.dark {
  color: black;
}

.light {
  color: white;
}

* {
  text-align: center;
}

#header {
  padding: 2rem 0;
  font-size: 4rem;
  background-size: cover;
  margin-bottom: 1rem;
  background-position: center center;
}
<div id="header">
  Header
</div>

<button id='toggle'>
Toggle Background
</button>

Example 2:

This example directly answers the question by retrieving the background image of the current div and running the function using said image.

/* 

Important get brightness code

*/

function getImageBrightness(imageSrc, callback) {
  var img = document.createElement("img");
  img.src = imageSrc;
  img.style.display = "none";
  img.crossOrigin = "anonymous";
  document.body.appendChild(img);

  var colorSum = 0;

  img.onload = function() {
    // create canvas
    var canvas = document.createElement("canvas");
    canvas.width = this.width;
    canvas.height = this.height;

    var ctx = canvas.getContext("2d");
    ctx.drawImage(this, 0, 0);

    var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
    var data = imageData.data;
    var r, g, b, avg;

    for (var x = 0, len = data.length; x < len; x += 4) {
      r = data[x];
      g = data[x + 1];
      b = data[x + 2];

      avg = Math.floor((r + g + b) / 3);
      colorSum += avg;
    }

    var brightness = Math.floor(colorSum / (this.width * this.height));
    callback(brightness);
  }
}


/* The element we wish to work on */
const header = document.getElementById("header");

/* To get the background image. Get style and then background image with a slice to remove quotes */
var imageElement = document.getElementById('header'),
  style = imageElement.currentStyle || window.getComputedStyle(imageElement, false),
  backgroundImage = style.backgroundImage.slice(4, -1).replace(/"/g, "");



getImageBrightness(backgroundImage, function(brightness) {
  /* Remove dark and light classes from the element */
  header.classList.remove("dark");
  header.classList.remove("light");

  /* check brightness and apply correct style */
  /* Please modify the below brightness comparison statement to adjust to your needs */
  if (brightness > 225 / 2) {
    header.classList.toggle("dark");

  } else {
    header.classList.toggle("light");
  }
});
.dark {
  color: black;
}

.light {
  color: white;
}

* {
  text-align: center;
}

#header {
  padding: 2rem 0;
  font-size: 4rem;
  background-size: cover;
  margin-bottom: 1rem;
  background-position: center center;
  background-image: url('https://images.unsplash.com/photo-1530128118208-89f6ce02b37b?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1740&q=80')
}
<div id="header">
  <h1>
    Header
  </h1>
</div>
Philip Clark
  • 316
  • 2
  • 7
  • Thanks for your time, that's exactly what I was trying to do. However, I have a concern. How do I apply the function to a personal div? I have several pages with different backgrounds, so instead of toggle the function it should reveal the background image I have in the div. I'm trying with the code you wrote but I can't. – Snorlax Jul 26 '22 at 11:07
  • I see that the url of the images have been put in the js code while my images are in html code or put with css background-image, there is a way to tell the function that it must read the image from my div or from the css and instead of putting them directly in the js code? – Snorlax Jul 26 '22 at 11:09
  • 1
    I have updated the answer with a more direct example of how to implement this. – Philip Clark Jul 26 '22 at 14:23
  • Thanks so much, I am grateful for that. Now that I have several examples, I'll do some practice and practice on these references. Thanks again. – Snorlax Jul 26 '22 at 14:30
  • Thanks again for the valuable suggestions you are giving me. I was able to play with the code to get a box with a product image in the foreground and the same blur image in the background. So I have achieved the goal I originally set for myself. But now I was wondering how can I get the code to work on images that are not CORS-enabled? Obviously with some images it's not working, can this be fixed? However this is the result that I had set myself https://jsfiddle.net/Snorlax93x/93nc8gkd/63/ – Snorlax Jul 26 '22 at 16:35
  • 1
    The easiest way to ensure this works is to use local images from your web server since CORS only deals with external sources. Attempting to use it on CORS disabled URLs is a security threat and should not be tried. – Philip Clark Jul 26 '22 at 17:08