在JavaScript中将两个十六进制颜色平均混合

25

我想向大家提出一个问题。

给定一个期望接收两个十六进制颜色字符串 (例如 #FF0000) 的函数 (使用javascript编写)

返回这两种颜色的平均值的十六进制颜色。

function averageColors(firstColor,secondColor)
{
  ...
  return avgColor;
}

--编辑--

平均值的定义为:enter image description here

如果传递的颜色是黄色,第二个是浅紫色,则返回的颜色将是中等橙色。


1
你如何定义“平均值”?这取决于你所使用的颜色空间等多种因素,因此可能有多种可能的含义。 - Amber
@Amber,我更新了问题以澄清。 - samccone
这里有一丝作业的味道... - Marc B
3
不用抱歉,@Marc B,这不是作业,只是一个个人项目。我希望我的计算机科学课程能够教授JS :/ - samccone
8个回答

18

快速/方便/ES6方式,通过指定的百分比混合两种十六进制颜色的方法:

// blend two hex colors together by an amount
function blendColors(colorA, colorB, amount) {
  const [rA, gA, bA] = colorA.match(/\w\w/g).map((c) => parseInt(c, 16));
  const [rB, gB, bB] = colorB.match(/\w\w/g).map((c) => parseInt(c, 16));
  const r = Math.round(rA + (rB - rA) * amount).toString(16).padStart(2, '0');
  const g = Math.round(gA + (gB - gA) * amount).toString(16).padStart(2, '0');
  const b = Math.round(bA + (bB - bA) * amount).toString(16).padStart(2, '0');
  return '#' + r + g + b;
}

console.log(blendColors('#00FF66', '#443456', 0.5));

amount应该是0到1之间的值,其中0代表完全是colorA,1代表完全是colorB,0.5代表"中点"。


1
警告,此代码不会对输入有效性进行任何检查。它假设始终会收到格式良好的6位十六进制颜色。仅在您可以确保始终接收到该颜色时使用。 - V. Rubinetti

15

10

如果您不想烦恼很多不必要的东西,只需要几行POJS代码:

// Expects input as 'nnnnnn' where each nn is a 
// 2 character hex number for an RGB color value
// e.g. #3f33c6
// Returns the average as a hex number without leading #
var averageRGB = (function () {

  // Keep helper stuff in closures
  var reSegment = /[\da-z]{2}/gi;

  // If speed matters, put these in for loop below
  function dec2hex(v) {return v.toString(16);}
  function hex2dec(v) {return parseInt(v,16);}

  return function (c1, c2) {

    // Split into parts
    var b1 = c1.match(reSegment);
    var b2 = c2.match(reSegment);
    var t, c = [];

    // Average each set of hex numbers going via dec
    // always rounds down
    for (var i=b1.length; i;) {
      t = dec2hex( (hex2dec(b1[--i]) + hex2dec(b2[i])) >> 1 );

      // Add leading zero if only one character
      c[i] = t.length == 2? '' + t : '0' + t; 
    }
    return  c.join('');
  }
}());

谢谢你,RobG,我向你致敬。 - i-g

4

这听起来像是一道作业题,但这是我的提示。

对于每个颜色的R、G和B值,取其十六进制数并求平均值。如果需要,可将其转换为十进制数进行计算。

function d2h(d) {return d.toString(16).padStart(2,'0');}

function h2d(h) {return parseInt(h,16);}

然后返回一个字符串,其中包含三个元素的连接值。


好的建议 @Deleplace。我更新了函数并添加了 .padStart()。 - nageeb

3

这里是一组相关(相互依存)的紧凑函数:

十六进制 ⟷ RGB 颜色转换:

function hexToRgb(h){return['0x'+h[1]+h[2]|0,'0x'+h[3]+h[4]|0,'0x'+h[5]+h[6]|0]}
function rgbToHex(r,g,b){return"#"+((1<<24)+(r<<16)+(g<<8)+ b).toString(16).slice(1);}

计算2个十六进制颜色的平均值:需要上述转换函数

function avgHex(h1,h2){a=hexToRgb(h1);b=hexToRgb(h2); return rgbToHex(~~((a[0]+b[0])/2),~~((a[1]+b[1])/2),~~((a[2]+b[2])/2));}

生成随机十六进制颜色:
function rndHex(){return'#'+('00000'+(Math.random()*(1<<24)|0).toString(16)).slice(-6);}

运行代码片段以进行演示:

// color functions (average/random/conversion)
function hexToRgb(h){return['0x'+h[1]+h[2]|0,'0x'+h[3]+h[4]|0,'0x'+h[5]+h[6]|0]}
function rgbToHex(r,g,b){return"#"+((1<<24)+(r<<16)+(g<<8)+ b).toString(16).slice(1);}
function rndHex(){return'#'+('00000'+(Math.random()*(1<<24)|0).toString(16)).slice(-6);}
function avgHex(h1,h2){a=hexToRgb(h1);b=hexToRgb(h2);return rgbToHex(~~((a[0]+b[0])/2),~~((a[1]+b[1])/2),~~((a[2]+b[2])/2));}

//code below is just for the demo
function auto(){if(chk.checked){tmr=setInterval(rnd,1000)}else{clearTimeout(tmr)}}auto();
function rnd(go){for(h of[h1,h2]){h.value=rndHex();}avgInput();}
addEventListener('input',avgInput); 
function avgInput(){ // get avg & colorize
 ha.value=avgHex(h1.value,h2.value);
 for(h of [h1,h2,ha])h.style.background=h.value;
}
*{font-family:monospace;font-size:5vw; }
<label>Color 1 → <input id='h1'></label><br>
<label>Average → <input id='ha'></label><br>
<label>Color 2 → <input id='h2'></label><br>
<label>Type hex colors or <input type='checkbox' id='chk' onclick='auto()' style=' transform: scale(1.5)'checked>Autorandom</label>


1
非常晚才来参加这个派对,但我个人正在寻找一种平均未定义数量的十六进制值的方法。基于@RobG的答案,我想出了这个方法。当然,你添加的颜色越多,它们就会变得更棕色/灰色,但也许这可以帮助你!

/**
 * Averages an array of hex colors. Returns one hex value (with leading #)
 *
 * @param {Array} colors - An array of hex strings, e.g. ["#001122", "#001133", ...]
 */
function averageHex(colors) {

  // transform all hex codes to integer arrays, e.g. [[R, G, B], [R,G,B], ...]
  let numbers = colors.map(function(hex) {
    // split in seperate R, G and B
    let split = hex.match(/[\da-z]{2}/gi);

    // transform to integer values
    return split.map(function(toInt) {
      return parseInt(toInt, 16);
    });
  });

  // reduce the array by averaging all values, resulting in an average [R, G, B]
  let averages = numbers.reduce(function(total, amount, index, array) {
    return total.map(function(subtotal, subindex) {

      // if we reached the last color, average it out and return the hex value
      if (index == array.length - 1) {

        let result = Math.round((subtotal + amount[subindex]) / array.length).toString(16);

        // add a leading 0 if it is only one character
        return result.length == 2 ? '' + result : '0' + result;

      } else {
        return subtotal + amount[subindex];
      }
    });
  });

  // return them as a single hex string
  return "#" + averages.join('');
}

console.log(averageHex(["#FF110C", "#0000AA", "#55063d", "#06551e"]));
// expected: #571b44, see also https://www.colorhexa.com/ and enter "#FF110C+#0000AA+#55063d+#06551e"


嗯,现在我看到@Endorox之前的回答正好做到了这一点... - davidverweij

1

这是我的函数,希望能帮到你。

function averageColors( colorArray ){
    var red = 0, green = 0, blue = 0;

    for ( var i = 0; i < colorArray.length; i++ ){
        red += hexToR( "" + colorArray[ i ] + "" );
        green += hexToG( "" + colorArray[ i ] + "" );
        blue += hexToB( "" + colorArray[ i ] + "" );
    }

    //Average RGB
    red = (red/colorArray.length);
    green = (green/colorArray.length);
    blue = (blue/colorArray.length);

    console.log(red + ", " + green + ", " + blue);
    return new THREE.Color( "rgb("+ red +","+ green +","+ blue +")" );
}

//get the red of RGB from a hex value
function hexToR(h) {return parseInt((cutHex( h )).substring( 0, 2 ), 16 )}

//get the green of RGB from a hex value
function hexToG(h) {return parseInt((cutHex( h )).substring( 2, 4 ), 16 )}

//get the blue of RGB from a hex value
function hexToB(h) {return parseInt((cutHex( h )).substring( 4, 6 ), 16 )}

//cut the hex into pieces
function cutHex(h) {if(h.charAt(1) == "x"){return h.substring( 2, 8 );} else {return h.substring(1,7);}}

-1
这是函数。
function avgColor(color1, color2) {
  //separate each color alone (red, green, blue) from the first parameter (color1) 
  //then convert to decimal
  let color1Decimal = {
    red: parseInt(color1.slice(0, 2), 16),
    green: parseInt(color1.slice(2, 4), 16),
    blue: parseInt(color1.slice(4, 6), 16)
  }
  //separate each color alone (red, green, blue) from the second parameter (color2) 
  //then convert to decimal
  let color2Decimal = {
    red: parseInt(color2.slice(0, 2), 16),
    green: parseInt(color2.slice(2, 4), 16),
    blue: parseInt(color2.slice(4, 6), 16),
  }
  // calculate the average of each color (red, green, blue) from each parameter (color1,color2) 
  let color3Decimal = {
    red: Math.ceil((color1Decimal.red + color2Decimal.red) / 2),
    green: Math.ceil((color1Decimal.green + color2Decimal.green) / 2),
    blue: Math.ceil((color1Decimal.blue + color2Decimal.blue) / 2)
  }
  //convert the result to hexadecimal and don't forget if the result is one character
  //then convert it to uppercase
  let color3Hex = {
    red: color3Decimal.red.toString(16).padStart(2, '0').toUpperCase(),
    green: color3Decimal.green.toString(16).padStart(2, '0').toUpperCase(),
    blue: color3Decimal.blue.toString(16).padStart(2, '0').toUpperCase()
  }
  //put the colors (red, green, blue) together to have the output
  let color3 = color3Hex.red + color3Hex.green + color3Hex.blue
  return color3
}
console.log(avgColor("FF33CC", "3300FF"))
// avgColor("FF33CC", "3300FF") => "991AE6"

console.log(avgColor("991AE6", "FF0000"))
// avgColor("991AE6", "FF0000") => "CC0D73"

console.log(avgColor("CC0D73", "0000FF"))
// avgColor("CC0D73", "0000FF") => "6607B9"

您可以使用此链接进行检查,将中点设置为1,然后混合颜色 https://meyerweb.com/eric/tools/color-blend/#CC0D73:0000FF:1:hex


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