Javascript 生成人类可区分的随机颜色
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10014271/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Generate Random Color distinguishable to Humans
提问by anuragsn7
I am trying to randomly generate a color in hex in javascript.
我正在尝试在 javascript 中随机生成十六进制颜色。
However the colors generated are almost indistinguishable from eachother.
Is there a way to improve it?
然而,产生的颜色彼此几乎无法区分。
有没有办法改善它?
Here is the code I am using:
这是我正在使用的代码:
function randomColor(){
var allowed = "ABCDEF0123456789", S = "#";
while(S.length < 7){
S += allowed.charAt(Math.floor((Math.random()*16)+1));
}
return S;
}
I heard something about HSLand HSVcolor model but can't get it to work in my code. Please help.
我听说了一些关于HSL和HSV颜色模型的信息,但无法在我的代码中使用它。请帮忙。
Thanks in Advance
提前致谢
采纳答案by Alexander
You could use a fixed set of colors, such as the ones listed in the jquery.color.js plugin.
您可以使用一组固定的颜色,例如jquery.color.js 插件中列出的颜色。
List of colors from jquery.color.js plugin:
来自 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"
};
The rest is simply picking a random property from a Javascript object.
剩下的就是简单地从 Javascript 对象中选择一个随机属性。
Colors.random = function() {
var result;
var count = 0;
for (var prop in this.names)
if (Math.random() < 1/++count)
result = prop;
return result;
};
Using Colors.random()
might get you a human-readable color. I even powered an example below.
使用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>
回答by goran
The easiest way to pick maximally different colors would be to to use HSL values instead of RGB and then manipulate Hue, as it has a value from 0 to 360 value and wraps around (0 is red, and so is 360);
选择最大不同颜色的最简单方法是使用 HSL 值而不是 RGB,然后操纵色调,因为它的值从 0 到 360 值并环绕(0 是红色,360 也是如此);
if you need 10 distinguishable colors you can divide 360 by 10 and then pick the individual color by multiplying the value by index (zero based). Here's an example function that allows you to pick a color from :
如果您需要 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%)";
}
This way you can randomize the color selection by randomizing index, but colors will always be in the same palette.
通过这种方式,您可以通过随机化索引来随机化颜色选择,但颜色将始终位于同一调色板中。
This will select a random color from a palette of 10:
这将从 10 个调色板中随机选择一种颜色:
var color = selectColor(Math.floor(Math.random() * 10), 10);
and so will this:
这也是:
var color = selectColor(Math.floor(Math.random() * 999), 10);
or you can select a specific color from the palette, like 9th color (index 8) out of palette of 13:
或者您可以从调色板中选择特定颜色,例如 13 个调色板中的第 9 种颜色(索引 8):
var color = selectColor(8, 13);
Here's a fiddle to play with: http://jsfiddle.net/2UE2B/
这是一个可以玩的小提琴:http: //jsfiddle.net/2UE2B/
Update on 2020-02-23:
2020-02-23 更新:
So, today I needed a solution to this same problem. Googling for this answer here (I know, a very weird way to look for stuff on SO) I ran into the Golden Angleconcept. It would make the above example even more trivial, and would not require a predetermined number of colors to be provided:
所以,今天我需要一个解决同样问题的方法。在这里谷歌搜索这个答案(我知道,在 SO 上寻找东西的一种非常奇怪的方式)我遇到了黄金角的概念。这将使上面的示例更加简单,并且不需要提供预定数量的颜色:
function selectColor(number) {
const hue = number * 137.508; // use golden angle approximation
return `hsl(${hue},50%,75%)`;
}
This answers the @netoperator-wibby's question
这回答了@netoperator-wibby 的问题
回答by Scelesto
I know I'm very late to this party, but I wrote up a more elaborate function to generate a set of contrasting random colors for another project. They are both (at least somewhat) attractive and genuinely random (not based on predefined colors) but my code is a bit more complicated than some of the other responses (so it's not for just getting the basics)
我知道我参加这个聚会已经很晚了,但我编写了一个更复杂的函数来为另一个项目生成一组对比鲜明的随机颜色。它们都(至少在某种程度上)有吸引力且真正随机(不是基于预定义的颜色),但我的代码比其他一些响应要复杂一些(所以它不仅仅是为了获得基础知识)
This is for users who want to have more than one random color on their page, and want to make sure no two colors are too similar.
这适用于希望在其页面上拥有一种以上随机颜色并希望确保没有两种颜色过于相似的用户。
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
});
}
Please note, although I don't do so in my example, that this can also be used to add new distinct, random colors to a set:
请注意,虽然我在我的示例中没有这样做,但这也可用于向集合添加新的不同的随机颜色:
generateRandomColors(1,generateRandomColors(10))
回答by Barry Chapman
Try this:
尝试这个:
function getRandomColor() {
var letters = '0123456789ABCDEF'.split('');
var color = '#';
for (var i = 0; i < 6; i++ ) {
color += letters[Math.round(Math.random() * 15)];
}
return color;
}
See it in action: http://jsfiddle.net/3wjgG/1/
看到它在行动:http: //jsfiddle.net/3wjgG/1/
回答by Michal
What you are saying is that you do not want to generate random colors, you are saying that you want to generate different colors.
You can find a good tutorial on how to do it here: http://krazydad.com/tutorials/makecolors.php.
你说的是你不想生成随机颜色,你是说你想生成不同的颜色。
你可以在这里找到一个很好的教程:http: //krazydad.com/tutorials/makecolors.php。
I made this fiddle with the relevant code from the tutorial demonstrating how you would generate non-repeating colors:
我使用教程中的相关代码制作了这个小提琴,演示了如何生成非重复颜色:
The only differnce from the tutorial code is that the makegradient() function returns an array of colors that you can later apply at will in your page.
与教程代码的唯一区别是 makegradient() 函数返回一个颜色数组,您可以稍后在页面中随意应用这些颜色。
回答by Robert Messerle
For randomly generating colors, I tend to go for something simple like this:
对于随机生成的颜色,我倾向于采用这样的简单方法:
?function randomColor () {
var max = 0xffffff;
return '#' + Math.round( Math.random() * max ).toString( 16 );
}
?
I'm not sure what you mean by unrecognizable. This method doesn't offer much customization, but at very least makes it easy to keep numbers from being too light or too dark.
我不确定你所说的无法识别是什么意思。这种方法没有提供太多的自定义,但至少可以很容易地防止数字太亮或太暗。
If you want to give bigger gaps between the generated colors, you could try reducing the number of allowed characters. I've used a method like that in the past where I only used 0369cf
as the pool of characters to pull from. Combining this with a check for duplicates tends to give more distinguishable colors, as well as only utilizing the #fff
3-character syntax.
如果您想在生成的颜色之间提供更大的差距,您可以尝试减少允许的字符数。我过去使用过类似的方法,我只用作0369cf
从中提取的字符池。将此与检查重复项相结合往往会提供更可区分的颜色,并且仅使用#fff
3 个字符的语法。
Here's your original function modified to use this method:
这是您修改为使用此方法的原始函数:
function randomColor(){
var allowed = "0369cf".split( '' ), s = "#";
while ( s.length < 4 ) {
s += allowed.splice( Math.floor( ( Math.random() * allowed.length ) ), 1 );
}
return s;
}
回答by tatactic
I agree with all the answers, we don't really know what you expect here...
我同意所有的答案,我们真的不知道你在这里期待什么......
This is a possibility that can give you the choice between the rgb(r, g, b) output for css elements, and the hex output...
这是一种可能性,可以让您在 css 元素的 rgb(r, g, b) 输出和十六进制输出之间进行选择...
This is a quick example, you have just to adapt this draft but it works as it on Firefox :
这是一个简单的例子,你只需要修改这个草案,但它在 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>
Then you can retrieve the value as here bellow :
然后你可以检索值,如下所示:
<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>
I hope this can help you. Best regards.
我希望这可以帮助你。此致。
回答by Coola
I wrote up a small script called SwitchColors.js which can be found here: https://github.com/akulmehta/SwitchColors.js
我写了一个名为 SwitchColors.js 的小脚本,可以在这里找到:https: //github.com/akulmehta/SwitchColors.js
The script produces more saturated colors and the brightness can be controlled. Although it may not produce visually distinguishable colors, it produces high saturation and bright colors which can also be attractive.
该脚本产生更饱和的颜色,并且可以控制亮度。虽然它可能不会产生视觉上可区分的颜色,但它会产生高饱和度和明亮的颜色,这也很有吸引力。
回答by Cylindric
First of all, why are you building hex values from strings? Just use numbers for the values, then output with something like yourNumber.toString(16)
.
首先,为什么要从字符串构建十六进制值?只需使用数字作为值,然后输出类似yourNumber.toString(16)
.
Then, to make the colours more distinct, don't use the full range of 0 to 255 for each colour component, but maybe go in jumps of 10, or 20, or whatever you need to generate wide enough differences.
然后,为了使颜色更清晰,不要对每个颜色分量使用 0 到 255 的完整范围,而是可以跳跃 10 或 20,或者任何你需要产生足够大差异的东西。