生成人类可区分的随机颜色

我试图在 javascript 中随机生成一个十六进制的颜色。

然而,生成的颜色几乎无法区分彼此。
有办法改进吗?


下面是我使用的代码:

function randomColor(){
var allowed = "ABCDEF0123456789", S = "#";


while(S.length < 7){
S += allowed.charAt(Math.floor((Math.random()*16)+1));
}
return S;
}

我听说了一些关于 HSLHSV颜色模型,但不能得到 它在我的代码中工作。请帮助。

提前感谢

74444 次浏览

试试这个:

function getRandomColor() {
var letters = '0123456789ABCDEF'.split('');
var color = '#';
for (var i = 0; i < 6; i++ ) {
color += letters[Math.round(Math.random() * 15)];
}
return color;
}

看看它的实际效果: Http://jsfiddle.net/3wjgg/1/

对于随机生成的颜色,我倾向于使用这样简单的东西:

​function randomColor () {
var max = 0xffffff;
return '#' + Math.round( Math.random() * max ).toString( 16 );
}
​

我不知道你说的面目全非是什么意思。这种方法没有提供太多的定制,但至少可以很容易地防止数字太亮或太暗。

如果要在生成的颜色之间设置更大的间隔,可以尝试减少允许的字符数。过去我曾经使用过类似的方法,只使用 0369cf作为要从中提取的字符池。结合重复检查可以得到更易于区分的颜色,而且只使用 #fff3个字符的语法。

下面是修改后使用此方法的原始函数:

function randomColor(){
var allowed = "0369cf".split( '' ), s = "#";
while ( s.length < 4 ) {
s += allowed.splice( Math.floor( ( Math.random() * allowed.length ) ), 1 );
}
return s;
}

您可以使用一组固定的颜色,例如 Jquery.color. js 插件中列出的那些。

Jquery.color.js 插件中的颜色列表:

Colors = {};
Colors.names = {
aqua: "#00ffff",
azure: "#f0ffff",
beige: "#f5f5dc",
black: "#000000",
blue: "#0000ff",
brown: "#a52a2a",
cyan: "#00ffff",
darkblue: "#00008b",
darkcyan: "#008b8b",
darkgrey: "#a9a9a9",
darkgreen: "#006400",
darkkhaki: "#bdb76b",
darkmagenta: "#8b008b",
darkolivegreen: "#556b2f",
darkorange: "#ff8c00",
darkorchid: "#9932cc",
darkred: "#8b0000",
darksalmon: "#e9967a",
darkviolet: "#9400d3",
fuchsia: "#ff00ff",
gold: "#ffd700",
green: "#008000",
indigo: "#4b0082",
khaki: "#f0e68c",
lightblue: "#add8e6",
lightcyan: "#e0ffff",
lightgreen: "#90ee90",
lightgrey: "#d3d3d3",
lightpink: "#ffb6c1",
lightyellow: "#ffffe0",
lime: "#00ff00",
magenta: "#ff00ff",
maroon: "#800000",
navy: "#000080",
olive: "#808000",
orange: "#ffa500",
pink: "#ffc0cb",
purple: "#800080",
violet: "#800080",
red: "#ff0000",
silver: "#c0c0c0",
white: "#ffffff",
yellow: "#ffff00"
};

剩下的只是 从 Javascript 对象中选择一个随机属性

Colors.random = function() {
var result;
var count = 0;
for (var prop in this.names)
if (Math.random() < 1/++count)
result = prop;
return result;
};

使用 Colors.random()可能会得到一个人类可读的颜色。

(function(){
Colors = {};
Colors.names = {
aqua: "#00ffff",
azure: "#f0ffff",
beige: "#f5f5dc",
black: "#000000",
blue: "#0000ff",
brown: "#a52a2a",
cyan: "#00ffff",
darkblue: "#00008b",
darkcyan: "#008b8b",
darkgrey: "#a9a9a9",
darkgreen: "#006400",
darkkhaki: "#bdb76b",
darkmagenta: "#8b008b",
darkolivegreen: "#556b2f",
darkorange: "#ff8c00",
darkorchid: "#9932cc",
darkred: "#8b0000",
darksalmon: "#e9967a",
darkviolet: "#9400d3",
fuchsia: "#ff00ff",
gold: "#ffd700",
green: "#008000",
indigo: "#4b0082",
khaki: "#f0e68c",
lightblue: "#add8e6",
lightcyan: "#e0ffff",
lightgreen: "#90ee90",
lightgrey: "#d3d3d3",
lightpink: "#ffb6c1",
lightyellow: "#ffffe0",
lime: "#00ff00",
magenta: "#ff00ff",
maroon: "#800000",
navy: "#000080",
olive: "#808000",
orange: "#ffa500",
pink: "#ffc0cb",
purple: "#800080",
violet: "#800080",
red: "#ff0000",
silver: "#c0c0c0",
white: "#ffffff",
yellow: "#ffff00"
};
Colors.random = function() {
var result;
var count = 0;
for (var prop in this.names)
if (Math.random() < 1/++count)
result = prop;
return { name: result, rgb: this.names[result]};
};
var $placeholder = $(".placeholder");
$placeholder.click(function(){
var color = Colors.random();
$placeholder.css({'background-color': color.rgb});
$("#color").html("It's " + color.name);
});
})();
.placeholder {
width: 150px;
height: 150px;
border: 1px solid black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="placeholder"></div>
<span id="color">Click the square above.</span>

首先,为什么要从字符串构建十六进制值?只需使用数字作为值,然后输出类似于 yourNumber.toString(16)的内容。

然后,为了使颜色更加明显,不要使用每个颜色组成部分0到255的全范围,但也许可以跳10次,或20次,或任何你需要产生足够广泛的差异。

你说的是你不想生成随机的颜色,你是说你想生成不同的颜色。
你可以在这里找到一个很好的教程: http://krazydad.com/tutorials/makecolors.php

我在教程中用相关的代码演示了如何生成非重复的颜色:

Http://jsfiddle.net/rgl52/

与教程代码的唯一区别是 makegradient ()函数返回一个颜色数组,以后可以在页面中随意应用这些颜色。

我同意所有的答案,我们不知道你在这里期望什么..。

这种可能性可以让您在 css 元素的 rgb (r,g,b)输出和十六进制输出之间做出选择..。

这是一个快速的例子,你只需要修改这个草案,但它在 Firefox 上的工作原理是:

<script type="text/javascript">
//<![CDATA[
function RndColor(){
var maximum = 255;
var minimum = 100;
var range = maximum - minimum;
var red = Math.floor(Math.random()*range)+minimum;
var green = Math.floor(Math.random()*range)+minimum;
var blue = Math.floor(Math.random()*range)+minimum;
var redToHex = red.toString(16);
var greenToHex = green.toString(16);
var blueToHex = blue.toString(16);
this.rgbValue = "rgb(" + red + "," + green + "," + blue + ")";
this.hexValue = "#" + redToHex + "" + greenToHex + "" + blueToHex;
}
RndColor.prototype.getRGB = function(){
return this.rgbValue;
}
RndColor.prototype.getHex = function(){
return this.hexValue;
}
//]]>
</script>

然后你可以检索到下面的值:

<script type="text/javascript">
//<![CDATA[
rndCol = new RndColor();
document.write("<div style = width:150px;height:100px;background-color:" + rndCol.getHex() + ">" + rndCol.getHex() + "</div><br /><br />");
document.write("<div style = width:150px;height:100px;background-color:" + rndCol.getRGB() + ">" + rndCol.getRGB() + "</div>");
//]]>
</script>

我希望这个能帮到你。 最好的问候。

最简单的方法来选择最大限度地不同的颜色将是使用 HSL 值而不是 RGB,然后操纵色调,因为它有一个值从0到360,并包围(0是红色,所以是360) ;

如果你需要10种可区分的颜色,你可以用360除以10,然后用索引乘以值(从零开始)来选择单独的颜色。 下面是一个示例函数,它允许您从以下方面选择颜色:

function selectColor(colorNum, colors){
if (colors < 1) colors = 1; // defaults to one color - avoid divide by zero
return "hsl(" + (colorNum * (360 / colors) % 360) + ",100%,50%)";
}

这样你可以通过随机索引来随机选择颜色,但是颜色总是在同一个调色板中。

这将从10个调色板中随机选择一种颜色:

var color = selectColor(Math.floor(Math.random() * 10), 10);

还有这个:

var color = selectColor(Math.floor(Math.random() * 999), 10);

或者你可以从调色板中选择一种特定的颜色,比如从13个调色板中选择第9种颜色(索引8) :

var color = selectColor(8, 13);

这里有一个小提琴玩: http://jsfiddle.net/2UE2B/

2020-02-23年度最新情况:

所以,今天我需要一个解决同样问题的方法。在这里搜索这个问题的答案(我知道,这是一种非常奇怪的在 SO 上寻找东西的方法) ,我遇到了 黄金角的概念。它将使上面的示例更加琐碎,并且不需要提供预定数量的颜色:

function selectColor(number) {
const hue = number * 137.508; // use golden angle approximation
return `hsl(${hue},50%,75%)`;
}

这回答了@netOperator-wibby 的问题

Example

我知道我来晚了,但我写了一个更复杂的函数来为另一个项目生成一组对比随机颜色。它们都(至少在一定程度上)有吸引力,而且是真正随机的(不是基于预定义的颜色) ,但是我的代码比其他一些响应要复杂一些(所以它不仅仅是为了获得基本的东西)

这是为那些希望页面上有多种随机颜色并且希望确保没有两种颜色太相似的用户准备的。

小提琴

var generateRandomColors=function(number){
/*
This generates colors using the following algorithm:
Each time you create a color:
Create a random, but attractive, color{
Red, Green, and Blue are set to random luminosity.
One random value is reduced significantly to prevent grayscale.
Another is increased by a random amount up to 100%.
They are mapped to a random total luminosity in a medium-high range (bright but not white).
}
Check for similarity to other colors{
Check if the colors are very close together in value.
Check if the colors are of similar hue and saturation.
Check if the colors are of similar luminosity.
If the random color is too similar to another,
and there is still a good opportunity to change it:
Change the hue of the random color and try again.
}
Output array of all colors generated
*/
//if we've passed preloaded colors and they're in hex format
if(typeof(arguments[1])!='undefined'&&arguments[1].constructor==Array&&arguments[1][0]&&arguments[1][0].constructor!=Array){
for(var i=0;i<arguments[1].length;i++){ //for all the passed colors
var vals = /^#?([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i.exec(arguments[1][i]); //get RGB values
arguments[1][i]=[parseInt(vals[1], 16),parseInt(vals[2], 16),parseInt(vals[3], 16)]; //and convert them to base 10
}
}
var loadedColors=typeof(arguments[1])=='undefined'?[]:arguments[1],//predefine colors in the set
number=number+loadedColors.length,//reset number to include the colors already passed
lastLoadedReduction=Math.floor(Math.random()*3),//set a random value to be the first to decrease
rgbToHSL=function(rgb){//converts [r,g,b] into [h,s,l]
var r=rgb[0],g=rgb[1],b=rgb[2],cMax=Math.max(r,g,b),cMin=Math.min(r,g,b),delta=cMax-cMin,l=(cMax+cMin)/2,h=0,s=0;if(delta==0)h=0;else if(cMax==r)h=60*((g-b)/delta%6);else if(cMax==g)h=60*((b-r)/delta+2);else h=60*((r-g)/delta+4);if(delta==0)s=0;else s=delta/(1-Math.abs(2*l-1));return[h,s,l]
},hslToRGB=function(hsl){//converts [h,s,l] into [r,g,b]
var h=hsl[0],s=hsl[1],l=hsl[2],c=(1-Math.abs(2*l-1))*s,x=c*(1-Math.abs(h/60%2-1)),m=l-c/2,r,g,b;if(h<60){r=c;g=x;b=0}else if(h<120){r=x;g=c;b=0}else if(h<180){r=0;g=c;b=x}else if(h<240){r=0;g=x;b=c}else if(h<300){r=x;g=0;b=c}else{r=c;g=0;b=x}return[r,g,b]
},shiftHue=function(rgb,degree){//shifts [r,g,b] by a number of degrees
var hsl=rgbToHSL(rgb); //convert to hue/saturation/luminosity to modify hue
hsl[0]+=degree; //increment the hue
if(hsl[0]>360){ //if it's too high
hsl[0]-=360 //decrease it mod 360
}else if(hsl[0]<0){ //if it's too low
hsl[0]+=360 //increase it mod 360
}
return hslToRGB(hsl); //convert back to rgb
},differenceRecursions={//stores recursion data, so if all else fails we can use one of the hues already generated
differences:[],//used to calculate the most distant hue
values:[]//used to store the actual colors
},fixDifference=function(color){//recursively asserts that the current color is distinctive
if(differenceRecursions.values.length>23){//first, check if this is the 25th recursion or higher. (can we try any more unique hues?)
//if so, get the biggest value in differences that we have and its corresponding value
var ret=differenceRecursions.values[differenceRecursions.differences.indexOf(Math.max.apply(null,differenceRecursions.differences))];
differenceRecursions={differences:[],values:[]}; //then reset the recursions array, because we're done now
return ret; //and then return up the recursion chain
} //okay, so we still have some hues to try.
var differences=[]; //an array of the "difference" numbers we're going to generate.
for(var i=0;i<loadedColors.length;i++){ //for all the colors we've generated so far
var difference=loadedColors[i].map(function(value,index){ //for each value (red,green,blue)
return Math.abs(value-color[index]) //replace it with the difference in that value between the two colors
}),sumFunction=function(sum,value){ //function for adding up arrays
return sum+value
},sumDifference=difference.reduce(sumFunction), //add up the difference array
loadedColorLuminosity=loadedColors[i].reduce(sumFunction), //get the total luminosity of the already generated color
currentColorLuminosity=color.reduce(sumFunction), //get the total luminosity of the current color
lumDifference=Math.abs(loadedColorLuminosity-currentColorLuminosity), //get the difference in luminosity between the two
//how close are these two colors to being the same luminosity and saturation?
differenceRange=Math.max.apply(null,difference)-Math.min.apply(null,difference),
luminosityFactor=50, //how much difference in luminosity the human eye should be able to detect easily
rangeFactor=75; //how much difference in luminosity and saturation the human eye should be able to dect easily
if(luminosityFactor/(lumDifference+1)*rangeFactor/(differenceRange+1)>1){ //if there's a problem with range or luminosity
//set the biggest difference for these colors to be whatever is most significant
differences.push(Math.min(differenceRange+lumDifference,sumDifference));
}
differences.push(sumDifference); //otherwise output the raw difference in RGB values
}
var breakdownAt=64, //if you're generating this many colors or more, don't try so hard to make unique hues, because you might fail.
breakdownFactor=25, //how much should additional colors decrease the acceptable difference
shiftByDegrees=15, //how many degrees of hue should we iterate through if this fails
acceptableDifference=250, //how much difference is unacceptable between colors
breakVal=loadedColors.length/number*(number-breakdownAt), //break down progressively (if it's the second color, you can still make it a unique hue)
totalDifference=Math.min.apply(null,differences); //get the color closest to the current color
if(totalDifference>acceptableDifference-(breakVal<0?0:breakVal)*breakdownFactor){ //if the current color is acceptable
differenceRecursions={differences:[],values:[]} //reset the recursions object, because we're done
return color; //and return that color
} //otherwise the current color is too much like another
//start by adding this recursion's data into the recursions object
differenceRecursions.differences.push(totalDifference);
differenceRecursions.values.push(color);
color=shiftHue(color,shiftByDegrees); //then increment the color's hue
return fixDifference(color); //and try again
},color=function(){ //generate a random color
var scale=function(x){ //maps [0,1] to [300,510]
return x*210+300 //(no brighter than #ff0 or #0ff or #f0f, but still pretty bright)
},randVal=function(){ //random value between 300 and 510
return Math.floor(scale(Math.random()))
},luminosity=randVal(), //random luminosity
red=randVal(), //random color values
green=randVal(), //these could be any random integer but we'll use the same function as for luminosity
blue=randVal(),
rescale, //we'll define this later
thisColor=[red,green,blue], //an array of the random values
/*
#ff0 and #9e0 are not the same colors, but they are on the same range of the spectrum, namely without blue.
Try to choose colors such that consecutive colors are on different ranges of the spectrum.
This shouldn't always happen, but it should happen more often then not.
Using a factor of 2.3, we'll only get the same range of spectrum 15% of the time.
*/
valueToReduce=Math.floor(lastLoadedReduction+1+Math.random()*2.3)%3, //which value to reduce
/*
Because 300 and 510 are fairly close in reference to zero,
increase one of the remaining values by some arbitrary percent betweeen 0% and 100%,
so that our remaining two values can be somewhat different.
*/
valueToIncrease=Math.floor(valueToIncrease+1+Math.random()*2)%3, //which value to increase (not the one we reduced)
increaseBy=Math.random()+1; //how much to increase it by
lastLoadedReduction=valueToReduce; //next time we make a color, try not to reduce the same one
thisColor[valueToReduce]=Math.floor(thisColor[valueToReduce]/16); //reduce one of the values
thisColor[valueToIncrease]=Math.ceil(thisColor[valueToIncrease]*increaseBy) //increase one of the values
rescale=function(x){ //now, rescale the random numbers so that our output color has the luminosity we want
return x*luminosity/thisColor.reduce(function(a,b){return a+b}) //sum red, green, and blue to get the total luminosity
};
thisColor=fixDifference(thisColor.map(function(a){return rescale(a)})); //fix the hue so that our color is recognizable
if(Math.max.apply(null,thisColor)>255){ //if any values are too large
rescale=function(x){ //rescale the numbers to legitimate hex values
return x*255/Math.max.apply(null,thisColor)
}
thisColor=thisColor.map(function(a){return rescale(a)});
}
return thisColor;
};
for(var i=loadedColors.length;i<number;i++){ //Start with our predefined colors or 0, and generate the correct number of colors.
loadedColors.push(color().map(function(value){ //for each new color
return Math.round(value) //round RGB values to integers
}));
}
//then, after you've made all your colors, convert them to hex codes and return them.
return loadedColors.map(function(color){
var hx=function(c){ //for each value
var h=c.toString(16);//then convert it to a hex code
return h.length<2?'0'+h:h//and assert that it's two digits
}
return "#"+hx(color[0])+hx(color[1])+hx(color[2]); //then return the hex code
});
}

请注意,虽然在我的示例中我没有这样做,但是这也可以用来添加新的独特的、随机的颜色到一个集合中:

generateRandomColors(1,generateRandomColors(10))

我编写了一个名为 SwitchColors.js 的小脚本,它可以在这里找到: https://github.com/akulmehta/SwitchColors.js

脚本产生更饱和的颜色和亮度可以控制。虽然它可能不会产生视觉上可区分的颜色,它产生高饱和度和明亮的颜色,也可以是有吸引力的。

Unique color swatches

今天我需要为我正在写的新课程解决这个问题,所以这是我的解决方案:

function getUniqueColor(n) {
const rgb = [0, 0, 0];
  

for (let i = 0; i < 24; i++) {
rgb[i%3] <<= 1;
rgb[i%3] |= n & 0x01;
n >>= 1;
}
  

return '#' + rgb.reduce((a, c) => (c > 0x0f ? c.toString(16) : '0' + c.toString(16)) + a, '')
}

它将位从输入数字向后传递到 RGB 值。这并不完美,但代码是紧凑的,相邻的颜色是可以区分的。这里是 小提琴