Reputation: 41
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
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
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
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