JavaScript Color Blender

Para meu projeto atual, eu queria criar uma indicação visual que fluiria suavemente, com base na entrada, de verde para amarelo e de amarelo para vermelho. Isso alertaria o usuário de que ele está mudando de operação em um parâmetro normal (verde) para um estado de aviso (amarelo) para um estado ruim (vermelho). Eu queria misturar as cores naturalmente para que algo que se aproximasse da área de perigo fosse laranja. Isso daria ao usuário final uma visão rápida do que precisava de sua atenção imediata, sem ter apenas uma função de etapa de três cores.

Minha entrada seria a cor do limite inferior, a cor do limite superior e a porcentagem entre o limite inferior e o limite superior. Os valores foram fornecidos por meio de JSON e precisariam ser convertidos na IU. (Código completo abaixo, com saída de relatório)

Esta sandbox mostra o método:
– valida a entrada e cria um código hexadecimal de 6 caracteres
– converte as duas cores em valores RGB
– mistura as cores com base nos valores RGB
– converte a cor combinada em hexadecimal

É JS vanilla puro e fornece uma tela para mostrar os valores resultantes. Exemplos abaixo

Amostras:

blend_colors('#000', '#fff', .5)

# 000 e #fff, 50% => # 808080

Cenário

blend_colors('#f00', '#00f', .5)

# ff0000 e # 0000ff => # 800080

Cenário

blend_colors('#f00', '#00f', .8)

# ff0000 e # 0000ff => # 3300cc

Cenário

A lógica vem da conversão dos valores de HEX para RGB e de RGB para HEX.

Para RGB: use a função parseInt nativa em JavaScript com uma raiz de 16.

color1 = [parseInt(color1[0] + color1[1], 16), parseInt(color1[2] + color1[3], 16), parseInt(color1[4] + color1[5], 16)];

e converter RGB em HEX

color3 = '#' + int_to_hex(color3[0]) + int_to_hex(color3[1]) + int_to_hex(color3[2]);

O RGB para HEX deve levar em consideração o fato de que podemos terminar com um retorno de um único dígito ao usar a função parseInt e, portanto, precisaremos preencher. Além disso, os valores não funcionarão se contiverem um decimal, o que não será convertido corretamente em um valor HEX.

function int_to_hex(num)
{
var hex = Math.round(num).toString(16);
if (hex.length == 1)
hex
= '0' + hex;
return hex;
}

Código completo:

/*
blend two colors to create the color that is at the percentage away from the first color

this is a 5 step process

1: validate input

2: convert input to 6 char hex

3: convert hex to rgb

4: take the percentage to create a ratio between the two colors

5: convert blend to hex

@param: color1 => the first color, hex (ie: #000000)

@param: color2 => the second color, hex (ie: #ffffff)

@param: percentage => the distance from the first color, as a decimal between 0 and 1 (ie: 0.5)

@returns: string => the third color, hex, represenatation of the blend between color1 and color2 at the given percentage

*/

function blend_colors(color1, color2, percentage)
{
// check input
color1
= color1 || '#000000';
color2
= color2 || '#ffffff';
percentage
= percentage || 0.5;

// 1: validate input, make sure we have provided a valid hex
if (color1.length != 4 && color1.length != 7)
throw new error('colors must be provided as hexes');

if (color2.length != 4 && color2.length != 7)
throw new error('colors must be provided as hexes');

if (percentage > 1 || percentage < 0)
throw new error('percentage must be between 0 and 1');

// output to canvas for proof
var cvs = document.createElement('canvas');
var ctx = cvs.getContext('2d');
cvs
.width = 90;
cvs
.height = 25;
document
.body.appendChild(cvs);

// color1 on the left
ctx
.fillStyle = color1;
ctx
.fillRect(0, 0, 30, 25);

// color2 on the right
ctx
.fillStyle = color2;
ctx
.fillRect(60, 0, 30, 25);

// 2: check to see if we need to convert 3 char hex to 6 char hex, else slice off hash
// the three character hex is just a representation of the 6 hex where each character is repeated
// ie: #060 => #006600 (green)
if (color1.length == 4)
color1
= color1[1] + color1[1] + color1[2] + color1[2] + color1[3] + color1[3];
else
color1
= color1.substring(1);
if (color2.length == 4)
color2
= color2[1] + color2[1] + color2[2] + color2[2] + color2[3] + color2[3];
else
color2
= color2.substring(1);

console
.log('valid: c1 => ' + color1 + ', c2 => ' + color2);

// 3: we have valid input, convert colors to rgb
color1
= [parseInt(color1[0] + color1[1], 16), parseInt(color1[2] + color1[3], 16), parseInt(color1[4] + color1[5], 16)];
color2
= [parseInt(color2[0] + color2[1], 16), parseInt(color2[2] + color2[3], 16), parseInt(color2[4] + color2[5], 16)];

console
.log('hex -> rgba: c1 => [' + color1.join(', ') + '], c2 => [' + color2.join(', ') + ']');

// 4: blend
var color3 = [
(1 - percentage) * color1[0] + percentage * color2[0],
(1 - percentage) * color1[1] + percentage * color2[1],
(1 - percentage) * color1[2] + percentage * color2[2]
];

console
.log('c3 => [' + color3.join(', ') + ']');

// 5: convert to hex
color3
= '#' + int_to_hex(color3[0]) + int_to_hex(color3[1]) + int_to_hex(color3[2]);

console
.log(color3);

// color3 in the middle
ctx
.fillStyle = color3;
ctx
.fillRect(30, 0, 30, 25);

// return hex
return color3;
}

/*
convert a Number to a two character hex string

must round, or we will end up with more digits than expected (2)

note: can also result in single digit, which will need to be padded with a 0 to the left

@param: num => the number to conver to hex

@returns: string => the hex representation of the provided number

*/

function int_to_hex(num)
{
var hex = Math.round(num).toString(16);
if (hex.length == 1)
hex
= '0' + hex;
return hex;
}