如何确定包含图像的 div 的最佳背景颜色?

Jor*_*rdy 5 html javascript css image colors

我的图像资源中有一些徽标。这些标志将被导入到本网站中。每个标志都有随机颜色(可以是白色、黑色、灰色、红色、蓝色、绿色等)并具有透明背景。例如:

白色标志 黑色标志 灰色标志

以下代码将用于在网站页面上显示徽标:

.magic-box {
    width: 200px;
    height: 100px;
    border: 1px solid black;
    position: relative;
    border-radius: 20px;
    background-color: white; /* can be changed */
}

.magic-image {
    max-height: 100%;
    max-width: 100%;
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    margin: auto;
}

.images {
  display: flex;
}
Run Code Online (Sandbox Code Playgroud)
<div class="images">
  <div class="magic-box">
    <img src="https://i.stack.imgur.com/b7yHv.png" class="magic-image" />
  </div>
  <div class="magic-box">
    <img src="https://i.stack.imgur.com/IhCH1.png" class="magic-image" />
  </div>
  <div class="magic-box">
    <img src="https://i.stack.imgur.com/tYjdM.png" class="magic-image" />
  </div>
</div>
Run Code Online (Sandbox Code Playgroud)

问题是当我将背景颜色设置为.magic-box白色时,白色徽标不会显示。当背景颜色设置为黑色时,黑色标志不可见,等等。

.magic-box {
    width: 200px;
    height: 100px;
    border: 1px solid black;
    position: relative;
    border-radius: 20px;
    background-color: black; /* can be changed */ 
}

.magic-image {
    max-height: 100%;
    max-width: 100%;
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    margin: auto;
}

.images {
  display: flex;
}
Run Code Online (Sandbox Code Playgroud)
<div class="images">
  <div class="magic-box">
    <img src="https://i.stack.imgur.com/b7yHv.png" class="magic-image" />
  </div>
  <div class="magic-box">
    <img src="https://i.stack.imgur.com/IhCH1.png" class="magic-image" />
  </div>
  <div class="magic-box">
    <img src="https://i.stack.imgur.com/tYjdM.png" class="magic-image" />
  </div>
</div>
Run Code Online (Sandbox Code Playgroud)

如何以编程方式确定最适合background-color应用于每个.magic-box元素的?

注意:每个图像的背景颜色可以不同

her*_*zel 4

1. JS:根据平均亮度添加背景颜色

此方法将图像渲染为 1x1 像素<canvas>,以检索平均亮度/亮度,从而找到合适的背景颜色。

function adjustBG(image, grayscale = true) {
    // try to fix CORS issues
    image.crossOrigin = "anonymous";

    // draw image on canvas
    let canvas = document.createElement("canvas");
    let ctx = canvas.getContext("2d");
    let img = new Image();

    img.src = image.src;
    img.crossOrigin = "anonymous"; // ADDED
    img.onload = function () {
        canvas.width = 1;
        canvas.height = 1;
        ctx.imageSmoothingEnabled = true;
        ctx.drawImage(img, 0, 0, 1, 1);

        // calculate average color form 1x1 px canvas
        let color = ctx.getImageData(0, 0, 1, 1).data;
        let [r, g, b, a] = [color[0], color[1], color[2], color[3]];

        // calculate relative luminance
        let luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b;
        let contrast = +((255 - luminance) / 255).toFixed(2);

        let bg = [255 - luminance, 255 - luminance, 255 - luminance].map(
            (val) => {
                return +val.toFixed(0);
            }
        );
        let filters = [];

        // optional convert all to grayscale
        if (grayscale) {
            filters.push(`grayscale(1)`);
        }

        // add background color if image is very bright
        if (luminance > 160 && contrast < 0.5) {
            //console.log(bg, luminance)
            image.style.backgroundColor = `rgb(${bg.join(",")})`;
        } else {
            image.style.backgroundColor = `rgb(255,255,255)`;
        }

        // enhance contrast
        if (contrast < 0.5) {
            let newContrast = contrast ? 1/contrast : 1;
            filters.push(`contrast(${newContrast })`);
        }

        image.style.filter = filters.join(" ");
    };
Run Code Online (Sandbox Code Playgroud)

function adjustBG(image, grayscale = true) {
    // try to fix CORS issues
    image.crossOrigin = "anonymous";

    // draw image on canvas
    let canvas = document.createElement("canvas");
    let ctx = canvas.getContext("2d");
    let img = new Image();

    img.src = image.src;
    img.crossOrigin = "anonymous"; // ADDED
    img.onload = function () {
        canvas.width = 1;
        canvas.height = 1;
        ctx.imageSmoothingEnabled = true;
        ctx.drawImage(img, 0, 0, 1, 1);

        // calculate average color form 1x1 px canvas
        let color = ctx.getImageData(0, 0, 1, 1).data;
        let [r, g, b, a] = [color[0], color[1], color[2], color[3]];

        // calculate relative luminance
        let luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b;
        let contrast = +((255 - luminance) / 255).toFixed(2);

        let bg = [255 - luminance, 255 - luminance, 255 - luminance].map(
            (val) => {
                return +val.toFixed(0);
            }
        );
        let filters = [];

        // optional convert all to grayscale
        if (grayscale) {
            filters.push(`grayscale(1)`);
        }

        // add background color if image is very bright
        if (luminance > 160 && contrast < 0.5) {
            //console.log(bg, luminance)
            image.style.backgroundColor = `rgb(${bg.join(",")})`;
        } else {
            image.style.backgroundColor = `rgb(255,255,255)`;
        }

        // enhance contrast
        if (contrast < 0.5) {
            let newContrast = contrast ? 1/contrast : 1;
            filters.push(`contrast(${newContrast })`);
        }

        image.style.filter = filters.join(" ");
    };
Run Code Online (Sandbox Code Playgroud)
let images = document.querySelectorAll("img");

addBGColor(true);

function revert() {
  images.forEach((img) => {
    img.style.removeProperty("background-color");
    img.style.removeProperty("filter");
  });
}

function addBGColor(grayscale = true) {
  images.forEach((img) => {
    adjustBG(img, grayscale);
  });
}

function adjustBG(image, grayscale = true) {
  // try to fix CORS issues
  image.crossOrigin = "anonymous";

  // draw image on canvas
  let canvas = document.createElement("canvas");
  let ctx = canvas.getContext("2d");
  let img = new Image();

  img.src = image.src;
  img.crossOrigin = "anonymous"; // ADDED
  img.onload = function() {
    canvas.width = 1;
    canvas.height = 1;
    ctx.imageSmoothingEnabled = true;
    ctx.drawImage(img, 0, 0, 1, 1);

    // calculate average color form 1x1 px canvas
    let color = ctx.getImageData(0, 0, 1, 1).data;
    let [r, g, b, a] = [color[0], color[1], color[2], color[3]];

    // calculate relative luminance
    let luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b;
    let contrast = +((255 - luminance) / 255).toFixed(2);

    let bg = [255 - luminance, 255 - luminance, 255 - luminance].map(
      (val) => {
        return +val.toFixed(0);
      }
    );
    let filters = [];

    // optional convert all to grayscale
    if (grayscale) {
      filters.push(`grayscale(1)`);
    }

    // add background color if image is very bright
    if (luminance > 160 && contrast < 0.5) {
      //console.log(bg, luminance)
      image.style.backgroundColor = `rgb(${bg.join(",")})`;
    } else {
      image.style.backgroundColor = `rgb(255,255,255)`;
    }

    // enhance contrast
    if (contrast < 0.5) {
      let newContrast = contrast ? 1 / contrast : 1;
      filters.push(`contrast(${newContrast })`);
    }

    image.style.filter = filters.join(" ");
  };
}
Run Code Online (Sandbox Code Playgroud)
body {
  background: #eee;
}

img {
  width: 10em;
  margin: 1em;
}
Run Code Online (Sandbox Code Playgroud)

Rene van der Lende 在评论中提出了一些重要观点:

  • 您可能会遇到CORS问题,因此您应该添加image.crossOrigin = 'anonymous'
  • 之前的版本在对比度计算中存在错误
  • 上面的示例提供了非常不准确的平均颜色计算

1.2 高级平均颜色计算

根据Rene van der Lende 的评论,我修改了averagecolor.js脚本并添加了一些功能。

以下代码片段还将检查图像的透明度以及仅灰度的颜色范围。

然而,该脚本对于更详细的颜色检测速度明显较慢。

<p><button onclick="revert()">revert</button> <button onclick="addBGColor(true)">Add BG color (grayscale)</button> <button onclick="addBGColor(false)">Add BG color (no change)</button></p>


<div class="logos">

  <img src="data:image/svg+xml, <svg width='200' height='100' viewBox='0 0 200 100' xmlns='http://www.w3.org/2000/svg'><text x='100' y='50' font-size='60' dy='20' text-anchor='middle' font-family='sans-serif' font-weight='bold' fill='rgb(0, 0, 0)'>LO<tspan fill='rgb(128, 128, 255)'>GO</tspan><tspan fill='rgba(255, 255, 255, 0.4)'>*</tspan></text></svg>">

  <img src="data:image/svg+xml, <svg width='200' height='100' viewBox='0 0 200 100' xmlns='http://www.w3.org/2000/svg'><text x='100' y='50' font-size='60' dy='5' text-anchor='middle' dominant-baseline='middle' font-family='sans-serif' font-weight='bold' fill='rgb(255, 0, 0)'>LO<tspan fill='rgb(255, 0, 0)'>GO</tspan><tspan fill='rgba(255, 255, 255, 0.4)'>*</tspan></text></svg>">

  <img src="data:image/svg+xml, <svg width='200' height='100' viewBox='0 0 200 100' xmlns='http://www.w3.org/2000/svg'><text x='100' y='50' font-size='60' dy='5' text-anchor='middle' dominant-baseline='middle' font-family='sans-serif' font-weight='bold' fill='rgb(0, 255, 0)'>LO<tspan fill='rgb(255, 128, 255)'>GO</tspan><tspan fill='rgba(255, 255, 255, 0.5)'>*</tspan></text></svg>">

  <img src="data:image/svg+xml, <svg width='200' height='100' viewBox='0 0 200 100' xmlns='http://www.w3.org/2000/svg'><text x='100' y='50' font-size='60' dy='5' text-anchor='middle' dominant-baseline='middle' font-family='sans-serif' font-weight='bold' fill='rgb(0, 0, 255)'>LO<tspan fill='rgb(128, 128, 128)'>GO</tspan><tspan fill='rgba(0, 255, 255, 0.4)'>*</tspan></text></svg>">

  <img src="data:image/svg+xml, <svg width='200' height='100' viewBox='0 0 200 100' xmlns='http://www.w3.org/2000/svg'><text x='100' y='50' font-size='60' dy='5' text-anchor='middle' dominant-baseline='middle' font-family='sans-serif' font-weight='bold' fill='rgb(255, 255, 0)'>LO<tspan fill='rgb(255, 255, 255)'>GO</tspan><tspan fill='rgba(255, 255, 255, 1)'>*</tspan></text></svg>">


  <img src="data:image/svg+xml, <svg width='200' height='100' viewBox='0 0 200 100' xmlns='http://www.w3.org/2000/svg'><text x='100' y='50' font-size='60' dy='5' text-anchor='middle' dominant-baseline='middle' font-family='sans-serif' font-weight='bold' fill='rgb(255, 255, 255)'>LO<tspan fill='rgb(128, 128, 255)'>GO</tspan><tspan fill='rgba(255, 255, 255, 0.5)'>*</tspan></text></svg>">


  <img src="data:image/svg+xml, <svg width='200' height='100' viewBox='0 0 200 100' xmlns='http://www.w3.org/2000/svg'><text x='100' y='50' font-size='60' dy='5' text-anchor='middle' dominant-baseline='middle' font-family='sans-serif' font-weight='bold' fill='rgb(0, 255, 255)'>LO<tspan fill='rgb(255, 200, 255)'>GO</tspan><tspan fill='rgba(255, 255, 255, 0.75)'>*</tspan></text></svg>">


  <img src="https://i.imgur.com/qO6ZdET.png" crossorigin="anonymous" data-contrast="0.69" data-bg="175,175,175" style="background-color: rgb(175, 175, 175);">



</div>
Run Code Online (Sandbox Code Playgroud)
let images = document.querySelectorAll("img");

function revert() {
    images.forEach((img) => {
        img.style.removeProperty('background-color');
        img.style.removeProperty('filter');
    });
}



function addBGColor(options = { grayscale: true, complementraryColor: false, enhanceContrast: true }) {
    images.forEach((img) => {
        adjustBG(img, options);
    });
}


function adjustBG(image, options = { grayscale: true, complementraryColor: false, enhanceContrast: true }) {


    let grayscale = options.grayscale;
    let complementraryColor = options.complementraryColor;
    let enhanceContrast = options.enhanceContrast;

    // try to fix CORS issues 
    image.crossOrigin = 'anonymous';

    image.addEventListener('load', e=>{

        // check transparency
        let hasTransparency = checkImgTransparency(image);
        let isGray = checkImgGrayScale(image);

        // skip opaque images
        if (!hasTransparency) {
            console.log('nothing to do', image.src);
            return false
        }

        // get average color based on flattened transparency
        let { r, g, b, a, colors } = getAverageColor(image, 24, 24, false);

        // calculate relative luminance
        let luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b;
        let contrast = +((255 - luminance) / 255).toFixed(2);
        let bg = [255 - luminance, 255 - luminance, 255 - luminance];

        // calculate complementary color
        let colComplemantary = [255 - r, 255 - g, 255 - b];
        let filters = [];

        // optional convert all to grayscale
        if (grayscale && !isGray) {
            filters.push(`grayscale(1)`)
        }

        // add background color if image is very bright
        let contrastAdjust = 1 + (luminance / 255);
        let colorBG = !complementraryColor ? 'rgb(255,255,255)' : `rgb(${colComplemantary.join(',')})`;

        image.setAttribute('data-contrast', contrast);
        image.setAttribute('data-bg', bg.join(','));

        // almost white
        if (luminance > 170 && contrast < 0.5) {
            colorBG = `rgb(${bg.join(',')})`;
        }

        // enhance contrast
        if (enhanceContrast && contrast < 0.5) {
            let newContrast = contrast ? 1/contrast : 1;
            filters.push(`contrast(${newContrast })`);
        }

        // apply styles
        image.style.backgroundColor = colorBG;
        image.style.filter = filters.join(' ');

    })


    // if image is ready loaded
    let isloaded = image.complete;
    if (isloaded) {
        image.dispatchEvent(new Event('load'));
    } 

}




/**
 * based on 
 * https://matkl.github.io/average-color/average-color.js
 */


function getAverageColor(img, width=24, height=24, flattenTransparency = false) {

  let canvas = document.createElement('canvas');
  let ctx = canvas.getContext('2d');
  ctx.imageSmoothingEnabled = true;
  canvas.width = width;
  canvas.height = height;

  //document.body.appendChild(canvas)

  // flatten transparency
  if (flattenTransparency) {
    //add rect
    ctx.fillStyle = "rgb(255,255, 255)";
    ctx.fillRect(0, 0, width, height);
  }

  ctx.drawImage(img, 0, 0, width, height);
  let imageData = ctx.getImageData(0, 0, width, height);
  let data = imageData.data;
  let [rT, gT, bT, aT] = [0, 0, 0, 0];
  let colLength = data.length/4;

  // get colors
  let colors = [];
  for (let i = 0; i < data.length; i += 4) {

    r = data[i];
    g = data[i + 1];
    b = data[i + 2];
    a = data[i + 3];

    // exclude transparent colors
    if(a>128){
      rT += r;
      gT += g;
      bT += b;
      aT += a;
    } else{
      colLength--;
    }

    // count colors
    let colStr = [r, g, b].join('_');
   colors.push(colStr)
    
  }

  // calculate average color
  rT = Math.floor(rT / colLength);
  gT = Math.floor(gT / colLength);
  bT = Math.floor(bT / colLength);
  aT = Math.floor(aT / colLength);

  // remove duplicates
  colors = [...new Set(colors)];

  return { r: rT, g: gT, b: bT, a: aT , colors: colors.length};
}



function colorIsgrayscale(r, g, b, tolerance = 0.25) {
  let isGray = false;
  let rT = +(r * tolerance).toFixed(0);
  let gT = +(g * tolerance).toFixed(0);
  let bT = +(b * tolerance).toFixed(0);

  let colorAverage = (rT + gT + bT) / 3;
  if (colorAverage == rT && colorAverage == gT && colorAverage == bT) {
    isGray = true;
  }
  return isGray;
}


function checkImgGrayScale(img, tolerance = 0.9) {
  let isGrayscale = false;
  let canvas = document.createElement('canvas');
  let ctx = canvas.getContext('2d');
  ctx.imageSmoothingEnabled = true;
  let [w, h] = [8, 8];

  ctx.drawImage(img, 0, 0, w, h);
  let imageData = ctx.getImageData(0, 0, w, h);
  let data = imageData.data;
  let gray = 0;

  for (let i = 0; i < data.length; i += 4) {
    let r = data[i];
    let g = data[i + 1];
    let b = data[i + 2];
    let isGray = colorIsgrayscale(r, g, b);
    if(isGray){
      gray++;
    }
  }

  if(gray===data.length/4){
    isGrayscale = true;
  }
  return isGrayscale;
}


function checkImgTransparency(img) {
  let canvas = document.createElement('canvas');
  let ctx = canvas.getContext('2d');
  ctx.imageSmoothingEnabled = true;

  ctx.drawImage(img, 0, 0, 3, 3);
  let imageData = ctx.getImageData(0, 0, 2, 2);
  let data = imageData.data;
  let hasAlpha = data[3] < 255 ? true : false;

  return hasAlpha;
}
Run Code Online (Sandbox Code Playgroud)
body {
  background: #eee;
}

img {
  width: 10em;
  margin: 1em;
}
Run Code Online (Sandbox Code Playgroud)

2. JS:通过应用CSS滤镜增强对比度

与第一种方法类似,我们通过渲染元素来<canvas>计算适当的滤镜属性值来分析图像的对比度和亮度,以增强contrast(2)或反转非常明亮的颜色invert(1)

<p><button onclick="revert()">revert</button>
   <button onclick="addBGColor({grayscale: true, complementraryColor: false, enhanceContrast: false})">Add BG color (grayscale)</button> <button onclick="addBGColor({grayscale: false, complementraryColor: true, enhanceContrast: false})">Add BG color (complementary color)</button></p>

<div class="logos">

  
  <img alt="logo white" src="https://i.postimg.cc/FzFQ3n3D/b7yHv.png" class="magic-image" />

  <img alt="logo black" src="https://i.postimg.cc/J0TCqcQm/IhCH1.png" class="magic-image" />

  
  <img src="https://i.imgur.com/qO6ZdET.png" >
  
  

  <img src="data:image/svg+xml, <svg width='200' height='100' viewBox='0 0 200 100' xmlns='http://www.w3.org/2000/svg'><text x='100' y='50' font-size='60' dy='20' text-anchor='middle' font-family='sans-serif' font-weight='bold' fill='rgb(0, 0, 0)'>LO<tspan fill='rgb(128, 128, 255)'>GO</tspan><tspan fill='rgba(255, 255, 255, 0.4)'>*</tspan></text></svg>">

  <img src="data:image/svg+xml, <svg width='200' height='100' viewBox='0 0 200 100' xmlns='http://www.w3.org/2000/svg'><text x='100' y='50' font-size='60' dy='5' text-anchor='middle' dominant-baseline='middle' font-family='sans-serif' font-weight='bold' fill='rgb(255, 0, 0)'>LO<tspan fill='rgb(255, 0, 0)'>GO</tspan><tspan fill='rgba(255, 255, 255, 0.4)'>*</tspan></text></svg>">

  <img src="data:image/svg+xml, <svg width='200' height='100' viewBox='0 0 200 100' xmlns='http://www.w3.org/2000/svg'><text x='100' y='50' font-size='60' dy='5' text-anchor='middle' dominant-baseline='middle' font-family='sans-serif' font-weight='bold' fill='rgb(0, 255, 0)'>LO<tspan fill='rgb(255, 128, 255)'>GO</tspan><tspan fill='rgba(255, 255, 255, 0.5)'>*</tspan></text></svg>">

  <img src="data:image/svg+xml, <svg width='200' height='100' viewBox='0 0 200 100' xmlns='http://www.w3.org/2000/svg'><text x='100' y='50' font-size='60' dy='5' text-anchor='middle' dominant-baseline='middle' font-family='sans-serif' font-weight='bold' fill='rgb(0, 0, 255)'>LO<tspan fill='rgb(128, 128, 128)'>GO</tspan><tspan fill='rgba(0, 255, 255, 0.4)'>*</tspan></text></svg>">

  <img src="data:image/svg+xml, <svg width='200' height='100' viewBox='0 0 200 100' xmlns='http://www.w3.org/2000/svg'><text x='100' y='50' font-size='60' dy='5' text-anchor='middle' dominant-baseline='middle' font-family='sans-serif' font-weight='bold' fill='rgb(255, 255, 0)'>LO<tspan fill='rgb(255, 255, 255)'>GO</tspan><tspan fill='rgba(255, 255, 255, 1)'>*</tspan></text></svg>">


  <img src="data:image/svg+xml, <svg width='200' height='100' viewBox='0 0 200 100' xmlns='http://www.w3.org/2000/svg'><text x='100' y='50' font-size='60' dy='5' text-anchor='middle' dominant-baseline='middle' font-family='sans-serif' font-weight='bold' fill='rgb(255, 255, 255)'>LO<tspan fill='rgb(128, 128, 255)'>GO</tspan><tspan fill='rgba(255, 255, 255, 0.5)'>*</tspan></text></svg>">


  <img src="data:image/svg+xml, <svg width='200' height='100' viewBox='0 0 200 100' xmlns='http://www.w3.org/2000/svg'><text x='100' y='50' font-size='60' dy='5' text-anchor='middle' dominant-baseline='middle' font-family='sans-serif' font-weight='bold' fill='rgb(0, 255, 255)'>LO<tspan fill='rgb(255, 200, 255)'>GO</tspan><tspan fill='rgba(255, 255, 255, 0.75)'>*</tspan></text></svg>">

</div>
Run Code Online (Sandbox Code Playgroud)
let images = document.querySelectorAll("img");

function fixImgColors() {
  images.forEach((img) => {
    adjustLightColors(img);
  });
}

function revert() {
    images.forEach((img) => {
        img.style.removeProperty('background-color');
        img.style.removeProperty('filter');
    });
}

function adjustLightColors(image) {
  // draw image on canvas 
  let canvas = document.createElement("canvas");
  let ctx = canvas.getContext("2d");
  let img = new Image();
  img.src = image.src;
  img.onload = function() {
    canvas.width = 1;
    canvas.height = 1;
    ctx.imageSmoothingEnabled = true;
    ctx.drawImage(img, 0, 0, 1, 1);

    // calculate average color form 1x1 px canvas
    let color = ctx.getImageData(0, 0, 1, 1).data;
    let [r, g, b] = [color[0], color[1], color[2]];

    // calculate relative luminance
    let luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b;

    // invert if image is very bright
    let filterInvert = luminance > 128 ? 1 : 0;
    let contrast = Math.ceil(1 + (luminance / 255));
    image.style.filter = `invert(${filterInvert}) grayscale(1) contrast(${contrast}`;
  };
}
Run Code Online (Sandbox Code Playgroud)
<p><button onclick="revert()">revert</button>   <button onclick="fixImgColors()">Adjust colors</button></p>

<div class="logos">

  <img src="data:image/svg+xml, <svg width='200' height='100' viewBox='0 0 200 100' xmlns='http://www.w3.org/2000/svg'><text x='100' y='50' font-size='60' dy='20' text-anchor='middle' font-family='sans-serif' font-weight='bold' fill='rgb(0, 0, 0)'>LO<tspan fill='rgb(128, 


Copyright Info

© Copyright 2013-2021 admin@qa.1r1g.com

如未特别说明,本网站的内容使用如下协议:
Creative Commons Atution-NonCommercial-ShareAlike 4.0 International license
.

用以下方式浏览
回到顶部