Konrad Viltersten
Konrad Viltersten

Reputation: 39078

Setting color in D3 based on data like a boss

I've created a set of bars that get recolored upon new data entry. The idea's to indicate the value not only by their width but also their color. To make it easy, as a first step, I picked blue and red (the thresholds of 40 and 160 are irrelevant), which resulted in the following.

.style("background-color", function (d) {
  var low = 40;
  var high = 160;
  if (d < low) return "#0000ff";
  if (d > high) return "#ff0000";

  var ratio = (d - low) / (high - low);
  return "#" + Math.round(255 * ratio).toString(16)
    + "00" + Math.round(255 * (1 - ratio)).toString(16);
})

While it works, I realize that it poses a maintnance issue. If I suddenly wish to go from green to yellow, I'll need to change quite a bit of the calculations. And trying out different color sets will turn out to a tedious and time consuming anti-fun.

There's got to be a more like-a-boss-ish way?

I can think of something like declaring the extreme colors, parsing them into separate RGB channels, multiplying the ratio and reconstructing the string again like below. However, I have a feeling that D3 offers something much, much smoother than that. Also, as an extra bonus, I'd like to use the color names, not the hexa codes.

var color = "#abcdef";
var ratio = 0.7;

var oldRed = color.substring(1,3);
var tmpRed = Math.round(parseInt(oldRed,16) * ratio);
var newRed = newRed.toString(16);
  1. What's a pro-like approach to the color shifting?
  2. Is there a facility to work with names instead of hexa codes?
  3. How should it be formulated to make animated change possible?

Upvotes: 2

Views: 133

Answers (1)

tobspr
tobspr

Reputation: 8376

You can solve 1) and 2) using interpolators:

var red = d3.rgb("red")
var blue = d3.rgb("blue")
return d3.interpolate(red, blue)(my_factor)

The wiki also lists various possibilities for creating the rgb objects:

  • "rgb(255,255,255)"
  • "hsl(120,50%,20%)"
  • "#ffeeaa"
  • "#fea"
  • "red", "white", "blue", etc.

For 3), you can use transitions, for example:

my_obj
   .style("fill", start_color)
   .transition()
   .attr("fill", end_color)
   .duration(1000)

See: D3 Wiki - interpolators, D3 Wiki - colors and Creating transitions with d3

Upvotes: 5

Related Questions