使用它们的十六进制混合两种颜色



我正在制作一个反应原生应用程序,并尝试使用它们的十六进制代码将两种或多种颜色混合在一起,然后获得结果颜色及其十六进制代码。

例如,在每种颜色具有相同比例的情况下,混合颜色:#4285F4 + #EA4335 + #FBBC05 = #SomeColorHexcode.是否有一种算法可以用来获得结果颜色的十六进制代码?

我正在浏览一些旧帖子,这里似乎有一个解决方案:在javascript中"自然"混合两种颜色。但是这篇文章中的主要解决方案似乎使用了 jquery,而 react native 不支持它。另外,这个问题大约在 7 年前被问到,我想知道现在是否有更好的算法可用于混合颜色。

谢谢!

原理

  1. 你取颜色值
  2. 将 CSS 颜色值转换为整数
  3. 您取每个 RGB 值的中间

let color1 = document.getElementById('color1')
let color2 = document.getElementById('color2')
let colormixed = document.getElementById('colormixed')
let button = document.getElementById('mix')
// this function takes an integer and converts it in a 2 digit hexadecimal number
function componentToHex(c) {
var hex = c.toString(16);
return hex.length == 1 ? "0" + hex : hex;
}
// this function takes an array of 3 RGB integer values and converts this array into a CSS color, like this: #AAAAA
function rgbToHex([r, g, b]) {
return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b);
}
// the regex is separating the value into groups of 2 characters, these characters being letters from 'a' to 'f' and digits, that is to say hexadecimal numbers. 
function convert(color) {
return /^#?([a-fd]{2})([a-fd]{2})([a-fd]{2})$/i.exec(color.value).map(x=>parseInt('0x'+x)).slice(1, 4)
}
button.addEventListener("click", function() {
// we get the two colors and convert them in integer arrays
let [c1,c2]= [color1,color2].map(x=>convert(x))
// this variable will be the array of the mixed color
let cm = []
// we take the middle of each RGB value between the two colors
c1.forEach((c,i) => cm.push(parseInt((c1[i]+c2[i])/2)))
// we convert the value into a CSS value
colormixed.value = rgbToHex(cm)
});
<input type="color" id="color1" />
<input type="color" id="color2" />
<input type="button" id="mix" value="Mix" /><br />
<input type="color" id="colormixed"/>

exec解释如下:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/exec

短代码,多种颜色

document.getElementById('add').addEventListener("click", function() {
let newColor = document.createElement("input")
newColor.type = 'color'
newColor.className = 'color'
document.getElementById('colors').appendChild(newColor)
})
document.getElementById('mix').addEventListener("click", function() {
document.getElementById('colormixed').value = '#'+Array.from(document.getElementsByClassName('color')).map(x=>/^#?([a-fd]{2})([a-fd]{2})([a-fd]{2})$/i.exec(x.value).map(y=>parseInt('0x'+y)).slice(1, 4)).reduce((a,b)=>a.map( (num, i) => (num + b[i]))).map(x=>parseInt(x/Array.from(document.getElementsByClassName('color')).length).toString(16).padStart(2, '0')).join('')
});
<div id="colors">
<header>
<input type="button" id="mix" value="Mix" />
<input type="button" id="add" value="Add" />
</header>
<input type="color" class="color" value="#ffffff" />
<input type="color" class="color" value="#000000" />
</div>
<label for"colormixed">Result:</label> <input type="color" id="colormixed"/>

单行本说明

它采用所有颜色:

Array.from(document.getElementsByClassName('color'))

对于每个元素,我们采用CSS颜色代码并分离RGB值:

.map(x=>/^#?([a-fd]{2})([a-fd]{2})([a-fd]{2})$/i.exec(x.value)

对于其中的每个元素,我们将每个十六进制数转换为整数:

.map(y=>parseInt('0x'+y))

我们只取 RGB 三元组:

.slice(1, 4)

然后我们做所有 RGB 数组的总和:

.reduce((a,b)=>a.map( (num, i) => (num + b[i])))

我们将每个 RGB 总和值除以颜色总数:

.map(x=>parseInt(x/Array.from(document.getElementsByClassName('color')).length)

我们将每个值转换回十六进制:

.toString(16)

当只有一个数字时,不要忘记添加"0":

.padStart(2, '0')

我们将 RGB 数组转换为字符串:

.join('')

我们在前面加上"#":

'#' + 

那里有你的混色器。

我相信这段代码产生的结果与之前的答案相同,但希望它的工作原理更加清晰。

console.log(mix_hexes('#3890b9', '#f6ff00')); // #8cc46f
function hex2dec(hex) {
return hex.replace('#', '').match(/.{2}/g).map(n => parseInt(n, 16));
}
function rgb2hex(r, g, b) {
r = Math.round(r);
g = Math.round(g);
b = Math.round(b);
r = Math.min(r, 255);
g = Math.min(g, 255);
b = Math.min(b, 255);
return '#' + [r, g, b].map(c => c.toString(16).padStart(2, '0')).join('');
}
function rgb2cmyk(r, g, b) {
let c = 1 - (r / 255);
let m = 1 - (g / 255);
let y = 1 - (b / 255);
let k = Math.min(c, m, y);
c = (c - k) / (1 - k);
m = (m - k) / (1 - k);
y = (y - k) / (1 - k);
return [c, m, y, k];
}
function cmyk2rgb(c, m, y, k) {
let r = c * (1 - k) + k;
let g = m * (1 - k) + k;
let b = y * (1 - k) + k;
r = (1 - r) * 255 + .5;
g = (1 - g) * 255 + .5;
b = (1 - b) * 255 + .5;
return [r, g, b];
}

function mix_cmyks(...cmyks) {
let c = cmyks.map(cmyk => cmyk[0]).reduce((a, b) => a + b, 0) / cmyks.length;
let m = cmyks.map(cmyk => cmyk[1]).reduce((a, b) => a + b, 0) / cmyks.length;
let y = cmyks.map(cmyk => cmyk[2]).reduce((a, b) => a + b, 0) / cmyks.length;
let k = cmyks.map(cmyk => cmyk[3]).reduce((a, b) => a + b, 0) / cmyks.length;
return [c, m, y, k];
}
function mix_hexes(...hexes) {
let rgbs = hexes.map(hex => hex2dec(hex)); 
let cmyks = rgbs.map(rgb => rgb2cmyk(...rgb));
let mixture_cmyk = mix_cmyks(...cmyks);
let mixture_rgb = cmyk2rgb(...mixture_cmyk);
let mixture_hex = rgb2hex(...mixture_rgb);
return mixture_hex;
}
  • Github Repository

我喜欢@GirkovArpa的答案,但没有必要去cmyk着色,它不支持混合#000000。下面是一个受该答案启发的函数,它混合了两个十六进制,并可以选择接受混合比率。

对于 javascript,请删除类型注释

function hex2dec(hex: string) {
const matched = hex.replace('#', '').match(/.{2}/g)
if (!matched) throw new Error('Invalid hex string');
return matched.map(n => parseInt(n, 16));
}
function rgb2hex(r: number, g: number, b: number) {
r = Math.round(r);
g = Math.round(g);
b = Math.round(b);
r = Math.min(r, 255);
g = Math.min(g, 255);
b = Math.min(b, 255);
return '#' + [r, g, b].map(c => c.toString(16).padStart(2, '0')).join('');
}
export function mixHexes(hex1: string, hex2: string, ratio: number = 0.5) {
if (ratio > 1 || ratio < 0) throw new Error('Invalid ratio');
const [r1, g1, b1] = hex2dec(hex1);
const [r2, g2, b2] = hex2dec(hex2);
const r = Math.round(r1 * ratio + r2 * (1 - ratio));
const g = Math.round(g1 * ratio + g2 * (1 - ratio));
const b = Math.round(b1 * ratio + b2 * (1 - ratio));
return rgb2hex(r, g, b);
}

用法:

console.log(mixHexes('#3890b9', '#f6ff00', 0.7));

最新更新