d3 filling an area with a gradient

Viewed 9478

I'm wanting to fill the area of this line chart with a gradient, so that the bottom of the gradient is transparent, and the top of the area fill is black. Is this possible?

All my google searches are bring up charts like: http://bl.ocks.org/d3noob/4433087 - I don't want the gradient to be based on the values, in this example I'd want red across the top of the area, not just when a value reaches that %.

var entries = [{"date":"2016-01-06","value":15},{"date":"2015-11-17","value":15.4},{"date":"2015-11-11","value":16.5},{"date":"2015-09-24","value":15.1},{"date":"2015-08-22","value":15},{"date":"2015-08-12","value":15},{"date":"2015-07-30","value":14.6},{"date":"2015-07-19","value":14.8},{"date":"2015-07-18","value":14.9},{"date":"2015-07-12","value":14.9},{"date":"2015-07-08","value":14.9},{"date":"2015-06-29","value":14.3},{"date":"2015-06-21","value":14.5},{"date":"2015-06-18","value":14.7},{"date":"2015-06-09","value":15},{"date":"2015-06-08","value":14.1},{"date":"2014-12-06","value":13.4},{"date":"2014-09-10","value":13.1},{"date":"2014-08-01","value":14.2},{"date":"2014-07-07","value":15},{"date":"2014-05-31","value":14},{"date":"2014-05-24","value":15},{"date":"2014-05-14","value":15},{"date":"2014-05-13","value":14},{"date":"2014-05-08","value":14.5},{"date":"2014-05-02","value":15}];

var margin = {top: 30, right: 20, bottom: 30, left: 50},
 overlayW = $('.chart').innerWidth(),
 overlayH = $('.chart').innerHeight(),
 width = overlayW,
 height = 250 - margin.top - margin.bottom,

 parseDate = d3.time.format("%Y-%m-%d").parse,
 bisectDate = d3.bisector(function(d) { return d.date; }).left,
 formatValue = d3.format(",.2f");

var x = d3.time.scale()
 .range([0, width]);

var y = d3.scale.linear()
 .range([height, 0]);

var xAxis = d3.svg.axis()
 .scale(x)
 .tickSize(0)
 .tickValues(x.domain())

 .orient("bottom")
 .tickFormat(d3.time.format("%b"));

var yAxis = d3.svg.axis()
 .scale(y)
 .tickSize(0)
 .ticks(5)
 .orient("left");

var area = d3.svg.area()
 .x(function(d) { return x(d.date); })
 .y0(height)
 .y1(function(d) { return y(d.value); });

var svg = d3.select(".drawn").append("svg")
  .attr("width", width)
  .attr("height", height + margin.top + margin.bottom)
 .append("g")
  .attr("transform", "translate(-15,30)");

enter(entries);






function enter(data) {

 data.forEach(function(d) {
  d.date = parseDate(d.date);
  d.value = +d.value;
 });

 data.sort(function(a, b) {
  return a.date - b.date;
 });

  x.domain([data[0].date, data[data.length - 1].date]);
  y.domain(d3.extent(data, function(d) { return d.value; }));
  //y.domain(d3.extent(data, function(d) { return d.value; }));
  var e = d3.extent(data, function(d) { return d.value; });
  



  svg.append("path")
   .datum(data)
   .attr("class", "area")
   .attr("d", area);





  svg.append("g")
   .attr("class", "x axis")
   .attr("transform", "translate(0," + height + ")")
   .call(xAxis);

  svg.append("g")
   .attr("class", "y axis")
  .attr("transform", "translate(" + width + ",0)")
   .call(yAxis);



}
.chart {display:block;background:#4BAC4E;height:250px;width:600px;}

.axis path,
.axis line {
  fill: none;
  stroke: rgba(0, 0, 0, .3);
  shape-rendering: crispEdges;
}

text {
  font: 400 11px mono;
  letter-spacing: .3px;
  text-transform: uppercase;
  fill: rgba(0, 0, 0, .3);
}

.domain {
  display: none;
}

.area {
  fill: black;
  stroke: none;
  stroke-width: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="chart">
  <div class="drawn"></div>
</div>

https://jsfiddle.net/5kesyqu0/3/

2 Answers

I might be late to this party, but exactly the same result I wanted to accomplish recently. While browsing internet I found this SO question and decided to post my own answer. Looks like no one else ever posted this solution yet. So, here goes:

let svgDefs = svg.append('defs');    

let filterBlur = svgDefs
    .append("filter")
    .attr('id', 'blur')
    .attr("x", "0")
    .attr("y", "0")
    .append("feGaussianBlur")
    .attr("in", "SourceGraphic")
    .attr("stdDeviation", 20);
    
let mask = svgDefs
    .append("mask")
    .attr('id', 'mask')
    .datum(data)
    .append("path")
    .attr("fill", "#fff")
    .attr("d", d3.svg.area()
.x(function(d) { return x(d.date); })
.y0(height)
.y1(function(d) { return y(d.value); }));

svg.append("path")
    .datum(data)
    .attr("stroke", "#274EF6")
    .attr("stroke-width", 15)
    .attr("fill", "none")
    .attr("filter", "url(#blur)")
    .attr("mask", "url(#mask)")
    .attr("class", "line-blur")
    .attr("d", d3.svg.line()
        .x(function (d) {
            return x(d.date);
        })
        .y(function (d) {
            return y(d.value);
        }));

Basic idea is not to use area chart, instead use line chart with pretty wide line-width. Then, apply blur effect to it and then mask property, where path of the mask will be created by that area, which we discarded at first step. Hope it is helpfulenter image description here

https://jsfiddle.net/RomanTourdyiev/dzcfsh1x/5/

Related