Wednesday, 16 January 2013

Applying a colour gradient to an area fill in d3.js

The following post is a portion of the D3 Tips and Tricks document which it free to download. To use this post in context, consider it with the others in the blog or just download the pdf  and / or the examples from the downloads page:-)
--------------------------------------------------------

The previous example of a varying gradient on a line is neat, but hopefully you're already thinking “Hang on, can't that same thing be applied to an area fill?”.

Damn! You're catching on.

To do this there's only a few things we need to change;

First of all the CSS for the line needs to be amended to refer to the area. So this...
.line {                         
    fill: none;                 
    stroke: url(#line-gradient);    
    stroke-width: 2px;          
}
…gets changed to this...
.area {                         
    fill: url(#area-gradient);                  
    stroke-width: 0px;          
}
We've defined the styles for the area this time, but instead of the stroke being defined by the separate script, now it's the area. While we've changed the url name, it's actually the same piece of code, with a different id (because it seemed wrong to be talking about an area when the label said line). We've also set the stroke width to zero, because we don't want any lines around our filled area.

Now we want to take the block of code that defined our line...
var valueline = d3.svg.line()
    .x(function(d) { return x(d.date); })
    .y(function(d) { return y(d.close); });
… and we need to replace it with the standard block that defined an area fill.
var area = d3.svg.area()    
    .x(function(d) { return x(d.date); })   
    .y0(height)                 
    .y1(function(d) { return y(d.close); });
So we're not going to be drawing a line at all. Just the area fill.

Next as I mentioned earlier, we change the id for the linearGradient block from “line-gradient” to “area-gradient”
.attr("id", "area-gradient")    

And lastly, we remove the block of code that drew the line and replace it with a block that draws an area. So change this....
svg.append("path")              
        .attr("class", "line")      
        .attr("d", valueline(data));
… for this;
svg.append("path")  
        .datum(data)    
        .attr("class", "area")  
        .attr("d", area);
And then sit back and marvel at your creation;
For a slightly 'nicer' looking example, you could check out a variation of one of Mike Bostocks originals here; http://bl.ocks.org/4433087.

The above description (and heaps of other stuff) is in the D3 Tips and Tricks document that can be accessed from the downloads page of d3noob.org.

16 comments:

  1. Hi!
    This was useful!
    But I have one question.
    What if I want to a chart with gradient colors and I want a part of the are to be transparent.
    Like this: http://farm4.static.flickr.com/3288/2715799179_778ed2ab97_o.png
    The bottom part is transparent and it goes into orange. But a bit the all of it transparent because we can see the grid lines.
    And sorry for my English, it's not my native language.

    ReplyDelete
    Replies
    1. Cool. That graph looks nice! I think you could accomplish the same thing without changing the transparency of the colour. I believe that it's just a gradient from orange to black, with the grid lines in front of the whole thing.
      However, I also think you COULD vary the opacity by changing the [data] section from this post (http://www.d3noob.org/2013/01/applying-colour-gradient-to-graph-line.html) where we set colour starting and stopping points to use opacity starting and stopping points. Probably something like (and this isn't tested)
      {offset: "0%", opacity: 1},
      {offset: "100%", opacity: 0}
      It would be an interesting experiment. Sadly I don't have time to play at the moment, but if you find a solution that works, I would be interested to know. Thanks for the question.

      Delete
    2. Hi!
      I tried to make that chart's look. Here is the result:
      https://dl.dropboxusercontent.com/u/8091726/d3js.png
      The grid lines is in front as you said.
      And the offsets:

      {offset: "0%", color: "black", opacity:0},
      {offset: "50%", color: "orange", opacity:0.5},
      {offset: "100%", color: "orange", opacity:1}

      So, I used your advice and this tutorial
      http://bl.ocks.org/d3noob/4433087 that you linked to the post.
      Once more, thank you!

      Delete
    3. Hey great! Good to get a result. Well done.

      Delete
    4. How to give gradient to a circle... normally the code


      var cdata=[50,40]; var xscale=40; var xspace =50; var yscale=70;
      var svg = d3.select("body").append("svg").attr("width", 1600).attr("height", 1600);

      var circle = svg.selectAll("circle")
      .data(cdata)
      .enter()
      .append("circle")

      var circleattr =circle.attr("cx",function(d){xscale=xscale+xspace; return xscale;})
      .attr("cy",function(d){yscale=yscale+xspace+10; return yscale;})
      .attr("r",function(d){return d;})
      .style("fill","yellow")

      Delete
    5. Sorry, I'm not sure if this is a question or a statement. Could you elaborate?

      Delete
  2. Does this work for arcs? I can't seem to get gradients to function properly on arc segments... any idea?

    ReplyDelete
  3. I haven't played with it myself, but I know it can be done. Here's an example from the master himself...
    http://bl.ocks.org/mbostock/4163057
    Good luck and let us know how you get on.

    ReplyDelete
  4. This looks quite cool, but I just want the color change without the gradient, do you know how to do this?

    ReplyDelete
    Replies
    1. Sure, nice and easy :-). Have a read of the previous post here (http://www.d3noob.org/2013/01/applying-colour-gradient-to-graph-line.html) (or get it from the book (https://leanpub.com/D3-Tips-and-Tricks)). The trick is to have the offset percentages the same figure for adjacent colours. Have a play with the figures in the code and see how they turn out on the screen. Good luck and enjoy!

      Delete
    2. Great it works! Thank you :)
      But can I set the offset at a specific value too?

      Delete
    3. I think so. Instead of having the percentage values in there, use the actual values you wanted to use (you may or may not need to use the quotes, I'm not sure)

      Delete
    4. That doesn't work, the graph turns then into one color.. I've tried using y(VALUE) and function (d) { return y2(0); }

      Delete
    5. Darn! There will be some nuance there that will get it right, I'm afraid there might be a bit of trial and error involved :-(.

      Delete
  5. Is there a way where the "offset" values of the data[] can be values from an array ? So that the gradient stops are determined through the array values and not random percentages ?

    ReplyDelete
    Replies
    1. That's a really good idea. I suspect that this would be eminently do-able, but know that my knowledge on array manipulation is pretty weak. I also semi-agree about the unusual appearance of the gradient stops. It's far from obvious where the start and stop points are for them. I suspect that our perceptions are being messed about by colour scales variation in intensity and I'm not entirely sure (from memory) if the scale is referenced to the data or the graph limits.

      Delete