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

5

我在我的图像资产中有几个标志。这些标志将被导入到this website中。每个标志都有一个随机颜色(可以是白色、黑色、灰色、红色、蓝色、绿色等),并且具有透明的背景。例如:

white logo black logo gray logo

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

.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;
}
<div class="images">
  <div class="magic-box">
    <img src="https://istack.dev59.com/b7yHv.webp" class="magic-image" />
  </div>
  <div class="magic-box">
    <img src="https://istack.dev59.com/IhCH1.webp" class="magic-image" />
  </div>
  <div class="magic-box">
    <img src="https://istack.dev59.com/tYjdM.webp" class="magic-image" />
  </div>
</div>

问题是当我将.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;
}
<div class="images">
  <div class="magic-box">
    <img src="https://istack.dev59.com/b7yHv.webp" class="magic-image" />
  </div>
  <div class="magic-box">
    <img src="https://istack.dev59.com/IhCH1.webp" class="magic-image" />
  </div>
  <div class="magic-box">
    <img src="https://istack.dev59.com/tYjdM.webp" class="magic-image" />
  </div>
</div>

如何通过编程确定每个 .magic-box 元素应用的最合适的 background-color

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


2
粉色 - DCR
有一些内联色度工具可以用来处理这个问题,或者你可以通过约翰尼斯·伊顿的书籍来学习它。 - Mister Jojo
@MisterJojo 代码能否应用色度测量学? - Jordy
这些工具是用JS代码制作的,因此这是可能的。 - Mister Jojo
1
这个能解决你的问题吗?https://dev59.com/v3I-5IYBdhLWcg3wbXxN - Rohìt Jíndal
显示剩余10条评论
6个回答

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(" ");
    };

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(" ");
  };
}
body {
  background: #eee;
}

img {
  width: 10em;
  margin: 1em;
}
<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>

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

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

1.2高级平均颜色计算

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

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

但是,由于更详细的颜色检测,此脚本速度显着较慢

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;
}
body {
  background: #eee;
}

img {
  width: 10em;
  margin: 1em;
}
<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>

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

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

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}`;
  };
}
body {
  background: #eee;
}

img {
  width: 10em;
  margin: 1em;
}
<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, 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>


<svg xmlns='http://www.w3.org/2000/svg' width='0' height='0' style="position:absolute">
    <defs>
      <filter id='fillBlack' filterUnits='userSpaceOnUse'>
        <feFlood flood-color='#000' result='flood' />
        <feComposite in='flood' in2='SourceAlpha' operator='in' />
      </filter>
    </defs>
  </svg>

3. 仅使用CSS:通过应用SVG滤镜增强对比度

这种方法基于SVG flood filter

将隐藏的SVG内联到您的HTML主体中:

  <svg xmlns='http://www.w3.org/2000/svg' width='0' height='0' style="position:absolute">
    <defs>
      <filter id='fillBlack' filterUnits='userSpaceOnUse'>
        <feFlood flood-color='#000' result='flood' />
        <feComposite in='flood' in2='SourceAlpha' operator='in' />
      </filter>
    </defs>
  </svg>

并通过定义CSS类引用此过滤器。

  .fillBlack {
      filter: url('#fillBlack');
    }

我们不能使用DataURL吗?

不幸的是,当作为DataURL嵌入式时,当前的Chromium版本存在一些过滤器方面的问题。

在Firefox中,您也可以使用以下方式:

.fillBlack {
  filter: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'><filter id='fillBlack' filterUnits='userSpaceOnUse'><feFlood flood-color='black' result='flood'/><feComposite in='flood' in2='SourceAlpha' operator='in'/></filter></svg>#fillBlack");
}

let images = document.querySelectorAll("img");

function applySvgFilter() {
  images.forEach((img) => {
    img.classList.add('fillBlack');
  });
}

function revert() {
  images.forEach((img) => {
    img.classList.remove('fillBlack');
  });
}
body {
  background: #eee;
}

img {
  width: 10em;
  margin: 1em;
}

.fillBlack {
  filter: url('#fillBlack');
}
<p><button onclick="revert()">revert</button> <button onclick="applySvgFilter()">Apply svg filter</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>">

</div>

<!-- hidden svg filter definition -->
<svg xmlns='http://www.w3.org/2000/svg' width='0' height='0' style="position:absolute">
    <defs>
      <filter id='fillBlack' filterUnits='userSpaceOnUse'>
        <feFlood flood-color='#000' result='flood' />
        <feComposite in='flood' in2='SourceAlpha' operator='in' />
      </filter>
    </defs>
  </svg>

(上面的 JavaScript 只是用来说明“之前 / 之后”效果的)。


1
我喜欢你的解决方案,但是第一种方法似乎有缺陷。使用这张图片:https://i.postimg.cc/tg28S2VV/pngegg5.png 和 <img src=".." style="width: 1px"> 结果仅显示一个白色像素,然而通过循环所有像素计算平均颜色得出的结果是rgb(47, 63, 74),非常暗淡的蓝色(来自colorhexa.com)。可能是由于默认HTML缩放导致原点50%50%。在你的情况下,如果图像中心大部分的颜色是相同的(如上方的“透明”),那么平均颜色也是相同的。不确定在这个Codepen链接中使用第一种方法时发生了什么。 - Rene van der Lende
我使用了这个来获取平均颜色... (average-color.js) - Rene van der Lende
我喜欢第一种方法,但它会破坏这张图片(https://i.imgur.com/qO6ZdET.png)。你能修复它吗? - Jordy
1
@Jordy:不好意思,我花了一些时间才发现自己的愚蠢错误:我已经修改了代码片段并添加了更高级的脚本(增加了新功能)。以前脚本的主要问题是对比度计算中的错误(白色为1,黑色为0-但应该相反)。 - herrstrietzel
我还没有完成这个任务,不是特指你的解决方案。你有注意到 OP 在第一段提到了“随机颜色(可以是白色、黑色、灰色、红色、蓝色、绿色等)”,在第一个代码片段后面的段落中提到“当背景颜色设置为黑色时,黑色标志不可见,等等。” 这让我认为 BG 应该是“任何”颜色。对我来说,唯一的选择似乎是将颜色转换为 HSL,求平均值并添加 180 度(以获取互补颜色),然后将结果用作 BG 颜色。平均 RGB 与 HSL 相比产生了巨大的差异。 - Rene van der Lende
显示剩余2条评论

2
如果您已经知道标志的颜色,可以使用 colord 计算标志与任何给定颜色之间的对比度。然后,您可以循环比较,调整背景颜色,直到达到可用值。
请记住,您需要使用 a11y 插件才能访问 .contrast() 类似于以下内容:
let contrast = 0;
let backgroundColor = logoColor;
const logoBrightness = colord(logoColor).brightness()
while (contrast < 4.5) {
  contrast = colord(logoColor).contrast(backgroundColor);
  if (logoBrightness <= 0.5) { 
      backgroundColor = colord(backgroundColor).lighten(0.05);
  } else backgroundColor = colord(backgroundColor).darken(0.05);
}

您可以将4.5替换为任何所需的对比度值。如何将该值应用于元素的backgroundColor取决于您的框架和偏好。


2

1.) 添加阴影

我知道这可能是一个简单而且有点傻的解决方案,但添加 drop-shadow 也可以帮助解决问题。每个标志都将可见。当然,这取决于您的网站是否适合这种设计。

.magic-image {
  filter: drop-shadow(0px 0px 7px rgba(10, 12, 15, 0.4));
  -webkit-filter: drop-shadow(0px 0px 7px rgba(10, 12, 15, 0.4));
}

@media (prefers-color-scheme: dark) {
  .magic-image {
    filter: drop-shadow(0px 0px 7px rgba(255, 255, 255, 0.4));
    -webkit-filter: drop-shadow(0px 0px 7px rgba(255, 255, 255, 0.4));
  }
}

添加到你的示例中:

.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;
  filter: drop-shadow(0px 0px 7px rgba(10, 12, 15, 0.4));
  -webkit-filter: drop-shadow(0px 0px 7px rgba(10, 12, 15, 0.4));
}

@media (prefers-color-scheme: dark) {

  body,
  .magic-box {
    background-color: #000;
  }

  .magic-image {
    filter: drop-shadow(0px 0px 12px rgba(255, 255, 255, 0.6));
    -webkit-filter: drop-shadow(0px 0px 12px rgba(255, 255, 255, 0.6));
  }
}

.images {
  display: flex;
}
<div class="images">
  <div class="magic-box">
    <img src="https://istack.dev59.com/b7yHv.webp" class="magic-image" />
  </div>
  <div class="magic-box">
    <img src="https://istack.dev59.com/IhCH1.webp" class="magic-image" />
  </div>
  <div class="magic-box">
    <img src="https://istack.dev59.com/tYjdM.webp" class="magic-image" />
  </div>
</div>

2.) 使用灰度和/或亮度

在许多网站上,徽标以黑白方式显示,正是出于这个原因。无论这是一个好还是坏的解决方案,都取决于你自己的设计。在任何情况下,这个想法是存在的。

通过降低亮度,您可以使图像中的白色部分变得更灰,使得所有的图像在白色背景下看起来完美。灰度是可选的,但为了更一致的外观 - 以便不会注意到元素的白化 - 我建议使用它。

.magic-image {
  filter: brightness(50%) grayscale(50%);
  -webkit-filter: brightness(50%) grayscale(50%);
}

添加到你的示例中:

.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;
  filter: brightness(50%) grayscale(50%);
  -webkit-filter: brightness(50%) grayscale(50%);
}

.images {
  display: flex;
}
<div class="images">
  <div class="magic-box">
    <img src="https://istack.dev59.com/b7yHv.webp" class="magic-image" />
  </div>
  <div class="magic-box">
    <img src="https://istack.dev59.com/IhCH1.webp" class="magic-image" />
  </div>
  <div class="magic-box">
    <img src="https://istack.dev59.com/tYjdM.webp" class="magic-image" />
  </div>
</div>


2
简单的答案可能是使用渐变。虽然不完美,但它简单且不需要很多代码。

.magic-box {
    width: 200px;
    height: 100px;
    border: 1px solid black;
    position: relative;
    border-radius: 20px;
    background-image: linear-gradient(white, black);
}

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

.images {
  display: flex;
}
<div class="images">
  <div class="magic-box">
    <img src="https://istack.dev59.com/b7yHv.webp" class="magic-image" />
  </div>
  <div class="magic-box">
    <img src="https://istack.dev59.com/IhCH1.webp" class="magic-image" />
  </div>
  <div class="magic-box">
    <img src="https://istack.dev59.com/tYjdM.webp" class="magic-image" />
  </div>
</div>

但是当然,那看起来并不太好,有点单调。


或许可以像这样输入颜色,生成相反的颜色,并且始终具有对比度。(来自这个Stack Overflow问题的代码

然而,我在这里做出的唯一假设是标志将始终是实色的,并且您将能够将该颜色HEX代码输出到元素的某个位置。否则,需要编写更复杂的图像分析代码。

function invertColor(hex, bw) {
    if (hex.indexOf('#') === 0) {
        hex = hex.slice(1);
    }
    // convert 3-digit hex to 6-digits.
    if (hex.length === 3) {
        hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
    }
    if (hex.length !== 6) {
        throw new Error('Invalid HEX color.');
    }
    var r = parseInt(hex.slice(0, 2), 16),
        g = parseInt(hex.slice(2, 4), 16),
        b = parseInt(hex.slice(4, 6), 16);
    if (bw) {
        // https://dev59.com/sW865IYBdhLWcg3wLrhE#3943023
        return (r * 0.299 + g * 0.587 + b * 0.114) > 186
            ? '#000000'
            : '#FFFFFF';
    }
    // invert color components
    r = (255 - r).toString(16);
    g = (255 - g).toString(16);
    b = (255 - b).toString(16);
    // pad each with zeros and return
    return "#" + padZero(r) + padZero(g) + padZero(b);
}

function padZero(str, len) {
    len = len || 2;
    var zeros = new Array(len).join('0');
    return (zeros + str).slice(-len);
}

//-------

document.querySelectorAll('.magic-box').forEach(box => {
  const img = box.querySelector('.magic-image');
  const logoColor = img.getAttribute('data-color');
  
  box.style.backgroundColor = invertColor(logoColor, true);
  
});
.magic-box {
    width: 200px;
    height: 100px;
    border: 1px solid black;
    position: relative;
    border-radius: 20px;
    background: #FFF;
}

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

.images {
  display: flex;
}
<div class="images">
  <div class="magic-box">
    <img src="https://istack.dev59.com/b7yHv.webp" class="magic-image" data-color="#FFFFFF" />
  </div>
  <div class="magic-box">
    <img src="https://istack.dev59.com/IhCH1.webp" class="magic-image" data-color="#000000" />
  </div>
  <div class="magic-box">
    <img src="https://istack.dev59.com/tYjdM.webp" class="magic-image" data-color="#808080" />
  </div>
</div>


2

这似乎是一个需要检查标志和背景之间对比度的时候。这里有一个颜色无障碍检查器,可以更轻松地看到两种颜色是否有足够的对比度。

在SO上有一些其他关于如何通过javascript计算对比度的主题:如何编程计算两种颜色之间的对比度比率?

您可以获取颜色的HSL值,然后调整背景的亮度,使其与标志的亮度相距最远。

例如:let backgroundLightness = (logoLightness + 50) % 100,如果标志的亮度为20,则背景可以是灰色,亮度为70 hsl(0,0,70)


-1

@jordy

我认为一种背景颜色并不适用于所有的标志,因为白色很亮,而灰色、黑色就比较暗。对于这些标志,你必须选择至少两种或最多三种背景颜色。

尝试使用以下链接来选择颜色: https://coolors.co/contrast-checker


为标志选择文本颜色,为背景选择背景颜色。 - ashirhusaain
我认为你的解决方案不错,但是需要手动操作。我想要自动化完成这个任务。 - Jordy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接