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

时间:2012-04-04 15:26:40

标签: javascript jquery

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

然而,产生的颜色几乎与彼此无法区分 有没有办法改善它?


以下是我正在使用的代码:

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

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

我听说过有关 HSL HSV 颜色模型的内容但无法获取 它在我的代码中工作。请帮忙。

先谢谢

10 个答案:

答案 0 :(得分:28)

选择最大不同颜色的最简单方法是使用HSL值而不是RGB然后操纵Hue,因为它具有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/

答案 1 :(得分:21)

Example

我知道我参加这个派对已经很晚了,但我写了一个更精细的功能,为另一个项目生成一组对比色随机颜色。它们(至少在某种程度上)具有吸引力且真正随机(不是基于预定义的颜色)但我的代码比其他一些响应更复杂(所以它不仅仅是为了获得基础知识)

这适用于想要在其网页上添加多种颜色的用户,并且希望确保没有两种颜色太相似。

Fiddle

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))

答案 2 :(得分:15)

您可以使用一组固定的颜色,例如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"
};

其余的只是picking a random property from a Javascript object

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()可能会让您获得人类可读的颜色。我甚至为example here提供了电力。

答案 3 :(得分:6)

你所说的是你不想生成随机颜色,你说要生成不同的颜色。
您可以在此处找到有关如何执行此操作的详细教程:http://krazydad.com/tutorials/makecolors.php

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

http://jsfiddle.net/rGL52/

与教程代码的唯一不同之处在于makegradient()函数返回一组颜色,您稍后可以在页面中随意应用这些颜色。

答案 4 :(得分:5)

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

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

我不确定你的意思是不可识别的。这种方法不能提供太多的自定义功能,但至少可以让数字不会太亮或太暗。

如果要在生成的颜色之间留出更大的间隙,可以尝试减少允许的字符数。我曾经使用过类似的方法,我只使用0369cf作为要从中拉出的字符池。将其与重复检查相结合往往会提供更多可区分的颜色,并且只使用#fff 3个字符的语法。

这是您修改为使用此方法的原始函数:

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

答案 5 :(得分:4)

试试这个:

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/

答案 6 :(得分:1)

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

这种可能性可以让你在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>

我希望这可以帮到你。 最好的问候。

答案 7 :(得分:1)

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 值向后“传播”。它并不完美,但代码紧凑且相邻颜色可区分。这是the fiddle

答案 8 :(得分:0)

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

然后,为了使颜色更清晰,不要为每个颜色分量使用0到255的整个范围,但可以跳过10或20,或者你需要的任何东西来产生足够大的差异。< / p>

答案 9 :(得分:0)

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

该脚本产生更多的饱和色,并且可以控制亮度。尽管它可能不会产生视觉上可辨别的颜色,但它会产生高饱和度和明亮的颜色,也可能很有吸引力。