ditto
ditto

Reputation: 6277

d3 filling an area with a gradient

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/

Upvotes: 4

Views: 13362

Answers (2)

Roman T.
Roman T.

Reputation: 393

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/

Upvotes: 2

Cyril Cherian
Cyril Cherian

Reputation: 32327

You can achieve this making a linear gradient like this:

//make defs and add the linear gradient
var lg = svgmain.append("defs").append("linearGradient")
.attr("id", "mygrad")//id of the gradient
.attr("x1", "0%")
.attr("x2", "0%")
.attr("y1", "0%")
.attr("y2", "100%")//since its a vertical linear gradient 
;
lg.append("stop")
.attr("offset", "0%")
.style("stop-color", "red")//end in red
.style("stop-opacity", 1)

lg.append("stop")
.attr("offset", "100%")
.style("stop-color", "blue")//start in blue
.style("stop-opacity", 1)

Then add this gradient to the fill of your path.

  svg.append("path")
      .datum(data)
      .attr("class", "area")
      .attr("d", area)
        .style("fill", "url(#mygrad)");//id of the gradient for fill

Working code here.

Another example as per your requirement (So that the bottom of the gradient is transparent, and the top of the area fill is red).

Upvotes: 12

Related Questions