Let's assume that you are working with the following HTML skeleton:
html<div class = 'Background'/> <h2>This a text with high contrast</h2> </div>
The goal is to set a background image using CSS and render the text color in black or white, depending on the average color of the region behind the text.
CSS.Background{ align-items: center; background: url('https://images.unsplash.com/photo-1621868579917-560fc2c68ec0?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=675&q=80'); display: flex; height: 600px; justify-content: center; padding: 20px; width: 600px; }
The steps to follow are:
javascriptconst changeTextColor = () => { // 0. Get text and background let text = document.getElementsByTagName('h2')[0]; let bckg = document.getElementsByClassName('Background')[0]; // 1. Calculate text and background measures let text_measures = text.getBoundingClientRect(); let bckg_measures = bckg.getBoundingClientRect(); // 2. Cut the background image behind the text let img = new Image(); img.crossOrigin = 'anonymous'; img.src = window.getComputedStyle(bckg, false).backgroundImage.slice(5, -2); img.height = bckg_measures.height; img.width = bckg_measures.width; img.onload = () => { // 3. Draw the portion of the background on a new canvas let imgData = paintCanvas(img, bckg, bckg_measures, text, text_measures); // 4. Calculate the YIQ based on the average RGB let yiq = getYiq(imgData); // 5. Decide text color depending on the YIQ text.style.color = yiq >= 128 ? 'black' : 'white'; } }
Additionally, you need to define the 2 functions to paint the canvas paintCanvas()
and to get the YIQ getYiq()
.
To get the average RGB color of the background behind the text, you need to draw the image on a canvas and return the image data:
javascriptconst paintCanvas = (img, bckg, bckg_measures, text, text_measures) => { // Creating the canvas element to draw the image let canvas = document.createElement('canvas'); let ctx = canvas.getContext('2d'); // Setting the size of the canvas canvas.height = bckg_measures.height; canvas.width = bckg_measures.width; // Drawing the final image ctx.drawImage(img, text.offsetLeft, text.offsetTop, text_measures.width, text_measures.height, 0, 0, text_measures.width, text_measures.height); // Getting the image data return ctx.getImageData(0, 0, text_measures.width, text_measures.height); }
Lastly, you can calculate the average RGB color and YIQ by reading each pixel of the canvas:
javascriptconst getYiq = (imgData) => { // Calculating average color of canvas // Counting the total elements let rgb = {r:0, g:0, b: 0}; let count = 0; length = imgData.data.length; for (var i = 0; i < length; i += 4) { rgb.r += imgData.data[i]; rgb.g += imgData.data[i + 1]; rgb.b += imgData.data[i + 2]; count ++; } // Calculating average rgb.r = Math.floor(rgb.r / count); rgb.g = Math.floor(rgb.g / count); rgb.b = Math.floor(rgb.b / count); // Getting the YIQ return yiq = ((rgb.r * 299) + (rgb.g * 587) + (rgb.b * 114)) / 1000; }
You can see a full example here.
Hi, I'm Erik, an engineer from Barcelona. If you like the post or have any comments, say hi.