Sahar
Sahar

Reputation: 41

How to generate random colors in an specific range using D3?

I am trying to generate random colors in an specific range for the bubbles in my Bubble chart using D3. I wanted the colors to be only in the range of blue and grey. I tried the following code for that:

var domainMax = 20;
var colorFn = d3.scale.linear()
        .domain([0, domainMax])
        .range(['blue', 'grey']);

var randomNum = Math.floor((Math.random() * domainMax));
var color = colorFn(randomNum);

My problem is that the colors generated by this methods are mostly very close to each others and some of them are the same. I played with domainMax and make it smaller and bigger but I did not see any changes. I would appreciate a lot if someone can tell me what method I can use to generate randomly different colors in an specific range.

Upvotes: 4

Views: 2694

Answers (3)

Xavier Guihot
Xavier Guihot

Reputation: 61666

With current versions of d3, it's possible to use:

  • d3.interpolate to obtain a color within the range of two colors:

    d3.interpolate(d3.rgb(70, 130, 180), d3.rgb(169, 169, 169))(0.5) // rgb(120, 150, 175)
    

    The value (here 0.5) is between 0 and 1. The closer it is to 0 (respectively 1), the closer the color will be to the left color (respectively the right color).

  • d3.random to randomly pick a value between 0 and 1, in order to randomely pick a color from the interpolated range of colors:

    d3.randomUniform()() // a value within [0, 1]
    

Coupled together, the interpolation and the random generator provide a way to randomly pick a color in a range of colors between 2 colors:

d3.interpolate("steelblue", "darkgrey")(d3.randomUniform()())

For instance:

var colors = d3.interpolate("steelblue", "darkgrey");
var steelblue = colors(0); console.log(steelblue);   // rgb(70, 130, 180)
var bluegrey = colors(0.5); console.log(bluegrey);   // rgb(120, 150, 175)

var colors = d3.interpolate(d3.rgb(70, 130, 180), d3.rgb(169, 169, 169));
var steelblue = colors(0.5); console.log(steelblue); // rgb(70, 130, 180)

var randombluegrey = colors(d3.randomUniform()());
console.log(randombluegrey);
<script src="https://d3js.org/d3.v5.min.js"></script>

Which, applied on a series of circles, gives:

var nodes = Array(15).fill(0).map((_,i) => i+1);

var colors = d3.interpolate("steelblue", "darkgrey");

d3.select("svg").attr("width", 500).attr("height", 100)
  .selectAll("circle")
  .data(nodes).enter().append("circle")
    .style("fill", d => colors(d3.randomUniform()(d / 15)))
    .attr("cx", d => d*25)
    .attr("cy", d => 20)
    .attr("r",  d => 10);
<script src="https://d3js.org/d3.v5.min.js"></script>
<svg></svg>

Upvotes: 4

Mark
Mark

Reputation: 108512

If you choose to go the ColorBrewer route, it's as simple as loading the library and then referencing the color from it:

if (i < 9)
    return colorbrewer.Blues['9'][i];
else
    return colorbrewer.Greys['9'][i-9];

Here's @DanielSutantyo's fiddle modified to use ColorBrewer.

Upvotes: 1

Daniel Sutantyo
Daniel Sutantyo

Reputation: 183

Let me try something really basic here. If you want a colour that ranges between blue and grey, then for the RGB value, you want R and G to be the same and B to be greater than both. Note that you'll get different shades of grey if R == G == B, so to add a 'blue shade' to this grey, you simply need to raise the value of B.

(Conversely, if you lower B, you'll get various shades of R+G, i.e. yellow)

For example

function getColor(inputValue){
  r = 120;
  b = 120 + (d * 7) % (256-r)
  return 'rgb(' + r + ',' + r + ',' + b + ')' 
}

Here is a fiddle: http://jsfiddle.net/pt3seenj/

The higher the value of R and G, the brighter your blue is. Anyway, you just need to play around with the getColor function to get exactly what you want.

Upvotes: 4

Related Questions