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.