如何根据当前颜色生成相反的颜色?


76

我正在尝试创建与当前颜色相反的颜色。我的意思是,如果当前颜色是黑色,则需要生成白色

实际上我有一个文本此文本的颜色是动态的,其颜色可以随意设置)。这是文成div,我需要设置文本的相反颜色background-colordiv。我希望该文本在(颜色透视图)中清晰可见div

相反的颜色表示:暗/亮

我具有文本的当前颜色,可以将其传递给此函数:

var TextColor = #F0F0F0;    // for example (it is a bright color)

function create_opp_color(current color) {

    // create opposite color according to current color

}

create_opp_color(TextColor); // this should be something like "#202020" (or a dark color)

有创建create_opp_color()功能的想法吗?



黑暗/明亮?所以对面的红色(#FF0000)是...黑色(#000000)?对于黑白,“轴”很容易,但是如果想要的目标是获得“对比度”而不是“互补”颜色或类似的颜色,那么处理颜色可能会比较棘手。
miguel-svq '16

@ miguel-svq好点..我的目标是使文本可读(彩色透视图),因此,如果文本的颜色是红色,则背景的颜色几乎可以是黑色,白色,蓝色..
堆叠

那里确实有很好的用于操纵颜色的模块。例如看一下具有功能的tinycolor(github.com/bgrins/TinyColormostReadable。我认为这比自己酿造更好。
Elmar Zander

Answers:


207

更新GitHub上的生产就绪代码。


这是我的方法:

  1. 将HEX转换为RGB
  2. 反转R,G和B分量
  3. 将每个组件转换回十六进制
  4. 用零填充每个组件并输出。
function invertColor(hex) {
    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.');
    }
    // invert color components
    var r = (255 - parseInt(hex.slice(0, 2), 16)).toString(16),
        g = (255 - parseInt(hex.slice(2, 4), 16)).toString(16),
        b = (255 - parseInt(hex.slice(4, 6), 16)).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);
}

示例输出:

在此处输入图片说明

进阶版本:

它具有一个bw选项,可以决定是反转为黑色还是白色。因此您会获得更多对比度,这通常对人眼来说更好。

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) {
        // http://stackoverflow.com/a/3943023/112731
        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);
}

示例输出:

在此处输入图片说明


这个“对立”问题的答案,将颜色设置为具有背景色的前景色,几乎是相同的。根据原始问题更改小提琴会导致对比度较低的颜色(无“ bw”)或背景色为jsfiddle.net/cffcd5bj/5。希望@stack对其进行调整,以避免出现不需要的低对比度颜色对。
miguel-svq '16

您制作了一个函数库并将其放在github上?:-)
Shafizadeh,

是。那就是所谓的微型图书馆。更易于维护,拥抱SRP等。
厄尼尔耶尔德勒姆

好..只是一件事,我创建了一个支持rtl方向的编辑器,没有类似的编辑器。我怎样才能像图书馆一样?换句话说,如何使它像这样可下载npm install myEditor --save?我应该在哪里找到可以通过Internet使用的代码npm
Shafizadeh

@Shafizadeh,如果它是最终产品,则不是npm。如果是JS / Node库/组件,请执行npm publish。参见npm docs。
OnurYıldırım18年

27

简洁大方。

function invertHex(hex) {
  return (Number(`0x1${hex}`) ^ 0xFFFFFF).toString(16).substr(1).toUpperCase()
}

invertHex('00FF00'); // Returns FF00FF

2
不知道它的作用或工作原理,但确实简单而优雅:)
Jeremy Thille

如果有人能解释它的工作原理,我将非常高兴。
Ar2zee

4
@ Ar2 Number(0x1 $ {hex})首先,它采用给定的十六进制(字符串)值并将其转换为HEX ^ 0xFFFFFF,然后对该值进行按位执行0xFFFFFF,如果要反转一个数字,我现在只能用这种方式来解释尽快 3> 7,8> 2,1> 9等 那是那部分的工作,除了十六进制值。其余的基本上是格式化和删除第一个值,这是等式的溢出。
Gerardlamo19年

确实很简单,但是请注意,它是“在颜色中间的镜像”,因此接近中间的颜色会得到非常接近它的颜色。例如invertHex('808080'); 产生几乎相同颜色的“ 7F7F7F”。
MoonLite

18

使用CSS实现此目标的简单方法:

mix-blend-mode: difference;
color:white;

2
注意事项:!E或Microsoft Edge当前不支持mix-blend-mode,并且根据您的使用情况,可能不太可能失败。
丹·迪瓦恩

11

@Onur的答案bw部分的纯CSS实现。

  <input type="color" oninput="['--r','--g','--b'].forEach((k,i)=>this.nextElementSibling.style.setProperty(k,parseInt(event.target.value.slice(1+i*2,3+i*2),16)))" />
 
  <div style="--r: 0; --g: 0; --b: 0; --c: calc(-1 * ((var(--r) * 0.299 + var(--g) * 0.587 + var(--b) * 0.114) - 186) * 255)">
    <div style="background-color: rgb(var(--r), var(--g), var(--b)); color: rgb(var(--c), var(--c), var(--c))">Test</div>
  </div>


4

当心可及性(AA / AAA)。颜色对比本身是没有用的。对于色盲者来说,完全不同的颜色完全没有对比度。恕我直言,这种颜色的计算可能是这样的:

(为简单起见,请使用“ HLS”)

  • 旋转色相180º,以获得(也许没用的)最大的色彩对比度
  • 计算亮度差。
  • (计算色差...是不必要的,这是最大的或几乎是)
  • 计算对比度。
  • 如果结果颜色符合要求,则计算结束,否则,循环:
    • 如果亮度差还不足以使计算的颜色亮度(L)增大或减小一定的比例(向上或向下取决于原始颜色的亮度:大于或小于中间值)
    • 检查它是否符合您的要求,如果计算结束。
    • 如果可以增加(或消除)亮度,则没有有效的颜色可以满足要求,只需尝试黑白,选择其中的“最好的一种”(可能是对比度较大的一种)并结束。

4

在我对您的问题的理解中,相反的颜色表示反色。

InvertedColorComponent = 0xFF - ColorComponent

因此,对于红色(#FF0000),这意味着:R = 0xFF或255 G = 0x00或0 B = 0x00或0

反转红色(#00FFFF)为:

R = 0xFF - 0xFF = 0x00 or 255 - 255 = 0
G = 0xFF - 0x00 = 0xFF or 255 - 0 = 255
B = 0xFF - 0x00 = 0xFF or 255 - 0 = 255

另一个例子:

黑色(#000000)变为白色(#FFFFFF)。

橙色(#FFA500)变为#005AFF


我也应该使用“反色”,但请记住,“相反”也可以表示“与色轮相反”。
工程师

2

仅将背景色翻转为文本色对某些中间范围值无效,例如0x808080。我尝试过改变颜色值- (v + 0x80) % 0x100在此处查看演示。

同意miguel-svq的评论-尽管希望为每个计算步骤看到更详细的算法。


2

这是一个简单的函数,可以反转十六进制颜色

const invertColor = (col) => {
  const colors = ['0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f']
  let inverseColor = '#'
  col.replace('#','').split('').forEach(i => {
    const index = colors.indexOf(i)
    inverseColor += colors.reverse()[index]
  })
  return inverseColor
}

Codepen示例


1

反转元素颜色的功能。获取每个的亮度,如果它们很接近,则反转文本颜色。

function adjustColor(element) {
    var style = window.getComputedStyle(element);
    var background = new Color(style['background-color']);
    var text = new Color(style['color']);
    if (Math.abs(background.luma - text.luma) < 100) {
        element.style.color = text.inverted.toString();
    }
}

下面的颜色“类别”。接受十六进制,rgb,rgba(即使有百分数),也可以输出到任意一个。资源管理器将需要为String.padStartString.startsWith使用polyfill,并且toString()方法中的插值字符串将需要使用concat进行修改。

const Color = (function () {
    function toHex(num, padding) { return num.toString(16).padStart(padding || 2); }
    function parsePart(value) {
        var perc = value.lastIndexOf('%');
        return perc < 0 ? value : value.substr(0, perc);
    }
    function Color(data) {
        if (arguments.length > 1) {
            this[0] = arguments[0];
            this[1] = arguments[1];
            this[2] = arguments[2];
            if (arguments.length > 3) { this[3] = arguments[3]; }
        } else if (data instanceof Color || Array.isArray(data)) {
            this[0] = data[0];
            this[1] = data[1];
            this[2] = data[2];
            this[3] = data[3];
        } else if (typeof data === 'string') {
            data = data.trim();
            if (data[0] === "#") {
                switch (data.length) {
                    case 4:
                        this[0] = parseInt(data[1], 16); this[0] = (this[0] << 4) | this[0];
                        this[1] = parseInt(data[2], 16); this[1] = (this[1] << 4) | this[1];
                        this[2] = parseInt(data[3], 16); this[2] = (this[2] << 4) | this[2];
                        break;
                    case 9:
                        this[3] = parseInt(data.substr(7, 2), 16);
                    //Fall Through
                    case 7:
                        this[0] = parseInt(data.substr(1, 2), 16);
                        this[1] = parseInt(data.substr(3, 2), 16);
                        this[2] = parseInt(data.substr(5, 2), 16);
                        break;
                }
            } else if (data.startsWith("rgb")) {
                var parts = data.substr(data[3] === "a" ? 5 : 4, data.length - (data[3] === "a" ? 6 : 5)).split(',');
                this.r = parsePart(parts[0]);
                this.g = parsePart(parts[1]);
                this.b = parsePart(parts[2]);
                if (parts.length > 3) { this.a = parsePart(parts[3]); }
            }
        }
    }
    Color.prototype = {
        constructor: Color,
        0: 255,
        1: 255,
        2: 255,
        3: 255,
        get r() { return this[0]; },
        set r(value) { this[0] = value == null ? 0 : Math.max(Math.min(parseInt(value), 255), 0); },
        get g() { return this[1]; },
        set g(value) { this[1] = value == null ? 0 : Math.max(Math.min(parseInt(value), 255), 0); },
        get b() { return this[2]; },
        set b(value) { this[2] = value == null ? 0 : Math.max(Math.min(parseInt(value), 255), 0); },
        get a() { return this[3] / 255; },
        set a(value) { this[3] = value == null ? 255 : Math.max(Math.min(value > 1 ? value : parseFloat(value) * 255, 255), 0); },
        get luma() { return .299 * this.r + .587 * this.g + .114 * this.b; },
        get inverted() { return new Color(255 - this[0], 255 - this[1], 255 - this[2], this[3]); },
        toString: function (option) {
            if (option === 16) {
                return '#' + toHex(this.r) + toHex(this.g) + toHex(this.b) + (this[3] === 255 ? '' : toHex(this[3]));
            } else if (option === '%') {
                if (this.a !== 1) {
                    return `rgba(${this.r / 255 * 100}%, ${this.b / 255 * 100}%, ${this.g / 255 * 100}%, ${this.a / 255})`;
                } else {
                    return `rgb(${this.r / 255 * 100}%, ${this.b / 255 * 100}%, ${this.g / 255 * 100})%`;
                }
            } else {
                if (this.a !== 1) {
                    return `rgba(${this.r}, ${this.b}, ${this.g}, ${this.a})`;
                } else {
                    return `rgb(${this.r}, ${this.b}, ${this.g})`;
                }
            }
        }
    };

    return Color;
}());

0

对于打字稿爱好者,这里我使用的是:

invertHex(hex: string) {
  if (hex.indexOf('#') === 0) {
    hex = hex.slice(1);
  }

  if (hex.length != 6) {
    console.warn('Hex color must be six hex numbers in length.');
    return '#' + hex;
  }

  hex = hex.toUpperCase();
  const splitNum = hex.split('');
  let resultNum = '';
  const simpleNum = 'FEDCBA9876'.split('');
  const complexNum = {
    A: '5', B: '4', C: '3', D: '2', E: '1', F: '0'
  };

  for (let i = 0; i < 6; i++) {
    if (!isNaN(Number(splitNum[i]))) {
      resultNum += simpleNum[splitNum[i]];
    } else if (complexNum[splitNum[i]]) {
      resultNum += complexNum[splitNum[i]];
    } else {
      console.warn('Hex colors must only include hex numbers 0-9, and A-F');
      return '#' + hex;
    }
  }

  return '#' + resultNum;
}

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.