How to synchronize animation of path and area? - animation
I'm getting stuck with the D3.js v4's animation of both line & area:
It's ok to do the animation separately for line & area
When 2 animations are combined, even at the same transition duration, they do not occur together.
For the reason of styling, I cannot drop the line away.
See the illustration below:
To make thing like above, I do 2 big steps:
Do animation for line via setting the properties stroke-dasharrow and stroke-dashoffset. (Inspired from Visual Cinnamon)
Do animation for area via tweaking parameters for d3.area() function (Inspired from other Stackoverlfow post)
The result is rather disappointing because line and area underneath do not appear in parallel.
My target is to mimic the Highchart library, see an example here, and its illustration below:
It seems the Highchart library uses a different animation technique, because during DOM inspection, there is no sign of any change for the DOM paths along the animation.
Appreciated if anyone could suggest me some ideas to experiment with.
My code sample is below:
let animationDuration = 5000;
// set the dimensions and margins of the graph
var margin = { top: 20, right: 20, bottom: 30, left: 50 },
width = 480 - margin.left - margin.right,
height = 250 - margin.top - margin.bottom;
// parse the date / time
var parseTime = d3.timeParse("%d-%b-%y");
// set the ranges
var x = d3.scaleTime().range([0, width]);
var y = d3.scaleLinear().range([height, 0]);
// define the area
var area = function (datum, boolean) {
return d3.area()
.y0(height)
.y1(function (d) { return boolean ? y(d.close) : y(d.close); })
.x(function (d) { return boolean ? x(d.date) : 0; })
(datum);
}
// define the line
var valueline = d3.line()
.x(function (d) { return x(d.date); })
.y(function (d) { return y(d.close); });
// append the svg obgect to the body of the page
// appends a 'group' element to 'svg'
// moves the 'group' element to the top left margin
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
var data = d3.csvParse(d3.select("pre#data").text());
data.reverse();
// format the data
data.forEach(function (d) {
d.date = parseTime(d.date);
d.close = +d.close;
});
// scale the range of the data
x.domain(d3.extent(data, function (d) { return d.date; }));
y.domain([0, d3.max(data, function (d) { return d.close; })]);
// add the area
svg.append("path")
.data([data])
.attr("class", "area")
.attr("d", d => area(d, false))
.attr("fill", "lightsteelblue")
.transition()
.duration(animationDuration)
.attr("d", d => area(d, true));
// add the valueline path.
svg.append("path")
.data([data])
.attr("class", "line")
.attr("d", valueline)
.style("stroke-dasharray", d => {
let path = document.querySelector(".line");
const totalLength = path.getTotalLength();
return `${totalLength} ${totalLength}`;
})
.style("stroke-dashoffset", d => {
let path = document.querySelector(".line");
const totalLength = path.getTotalLength();
return `${totalLength}`;
})
.transition()
.duration(animationDuration)
.style("stroke-dashoffset", 0);
// add the X Axis
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
// add the Y Axis
svg.append("g")
.call(d3.axisLeft(y));
.line {
fill: none;
stroke: steelblue;
stroke-width: 2px;
}
pre#data {display:none;}
<script src="https://d3js.org/d3.v4.min.js"></script>
<pre id="data">
date,close
1-May-12,58.13
30-Apr-12,53.98
27-Apr-12,67.00
26-Apr-12,89.70
25-Apr-12,99.00
24-Apr-12,130.28
23-Apr-12,166.70
20-Apr-12,234.98
19-Apr-12,345.44
18-Apr-12,443.34
17-Apr-12,543.70
16-Apr-12,580.13
13-Apr-12,605.23
12-Apr-12,622.77
11-Apr-12,626.20
10-Apr-12,628.44
9-Apr-12,636.23
5-Apr-12,633.68
4-Apr-12,624.31
3-Apr-12,629.32
2-Apr-12,618.63
30-Mar-12,599.55
29-Mar-12,609.86
28-Mar-12,617.62
27-Mar-12,614.48
26-Mar-12,606.98
</pre>
There is a way to animate both the line and the area, using a custom interpolator.
However, since your goal is to mimic that Highcharts animation you linked, there is a way easier alternative: use a <clipPath>.
In my proposed solution we create the area and the line the regular way. However, we reference a clipping path...
.attr("clip-path", "url(#clip)");
...in both area and line. The clipping path is created with 0 width:
var clip = svg.append("clipPath")
.attr("id", "clip");
var clipRect = clip.append("rect")
.attr("width", 0)
Then, after that, it's just a matter of applying the transition to the clipping path:
clipRect.transition()
.duration(5000)
.ease(d3.easeLinear)
.attr("width", someValue)
Here is a demo:
var svg = d3.select("svg");
var data = d3.range(30).map(d => Math.random() * 150);
var clip = svg.append("clipPath")
.attr("id", "clip");
var clipRect = clip.append("rect")
.attr("width", 0)
.attr("height", 150)
var lineGenerator = d3.line()
.x((_, i) => i * 10)
.y(d => d)
.curve(d3.curveMonotoneX)
var areaGenerator = d3.area()
.x((_, i) => i * 10)
.y1(d => d)
.y0(150)
.curve(d3.curveMonotoneX)
svg.append("path")
.attr("d", areaGenerator(data))
.attr("class", "area")
.attr("clip-path", "url(#clip)");
svg.append("path")
.attr("d", lineGenerator(data))
.attr("class", "line")
.attr("clip-path", "url(#clip)");
clipRect.transition()
.duration(5000)
.ease(d3.easeLinear)
.attr("width", 300)
.line {
fill: none;
stroke: #222;
stroke-width: 2px;
}
.area {
fill: limegreen;
stroke: none;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg></svg>
And here is your code with those changes:
let animationDuration = 5000;
// set the dimensions and margins of the graph
var margin = {
top: 20,
right: 20,
bottom: 30,
left: 50
},
width = 480 - margin.left - margin.right,
height = 250 - margin.top - margin.bottom;
// parse the date / time
var parseTime = d3.timeParse("%d-%b-%y");
// set the ranges
var x = d3.scaleTime().range([0, width]);
var y = d3.scaleLinear().range([height, 0]);
// define the area
var area = function(datum, boolean) {
return d3.area()
.y0(height)
.y1(function(d) {
return boolean ? y(d.close) : y(d.close);
})
.x(function(d) {
return boolean ? x(d.date) : 0;
})
(datum);
}
// define the line
var valueline = d3.line()
.x(function(d) {
return x(d.date);
})
.y(function(d) {
return y(d.close);
});
// append the svg obgect to the body of the page
// appends a 'group' element to 'svg'
// moves the 'group' element to the top left margin
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
var clip = svg.append("clipPath")
.attr("id", "clip");
var clipRect = clip.append("rect")
.attr("width", 0)
.attr("height", height);
var data = d3.csvParse(d3.select("pre#data").text());
data.reverse();
// format the data
data.forEach(function(d) {
d.date = parseTime(d.date);
d.close = +d.close;
});
// scale the range of the data
x.domain(d3.extent(data, function(d) {
return d.date;
}));
y.domain([0, d3.max(data, function(d) {
return d.close;
})]);
// add the area
svg.append("path")
.data([data])
.attr("class", "area")
.attr("d", d => area(d, true))
.attr("fill", "lightsteelblue")
.attr("clip-path", "url(#clip)");
// add the valueline path.
svg.append("path")
.data([data])
.attr("class", "line")
.attr("d", valueline)
.attr("clip-path", "url(#clip)");
// add the X Axis
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
// add the Y Axis
svg.append("g")
.call(d3.axisLeft(y));
clipRect.transition()
.duration(5000)
.ease(d3.easeLinear)
.attr("width", width)
.line {
fill: none;
stroke: steelblue;
stroke-width: 2px;
}
pre#data {
display: none;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<pre id="data">
date,close
1-May-12,58.13
30-Apr-12,53.98
27-Apr-12,67.00
26-Apr-12,89.70
25-Apr-12,99.00
24-Apr-12,130.28
23-Apr-12,166.70
20-Apr-12,234.98
19-Apr-12,345.44
18-Apr-12,443.34
17-Apr-12,543.70
16-Apr-12,580.13
13-Apr-12,605.23
12-Apr-12,622.77
11-Apr-12,626.20
10-Apr-12,628.44
9-Apr-12,636.23
5-Apr-12,633.68
4-Apr-12,624.31
3-Apr-12,629.32
2-Apr-12,618.63
30-Mar-12,599.55
29-Mar-12,609.86
28-Mar-12,617.62
27-Mar-12,614.48
26-Mar-12,606.98
</pre>
Related
d3.js - define grid line style with javascript
I prefer to use javascript to draw graph without css file since it's more easy to copy one file here and there! But I don't know how to translate grid line css to javascript! Thanks if anyone can help! (grid line and grid path commented out now in css file) console.clear() var margin = {top: 20, right: 20, bottom: 30, left: 50}, width = 600 - margin.left - margin.right, height = 400 - margin.top - margin.bottom; var x = d3.scaleTime().range([0, width]); var y = d3.scaleLinear().range([height, 0]); var valueline = d3.line() .x(function(d) { return x(d.date); }) .y(function(d) { return y(d.close); }); var svg = d3.select("body").append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); var datacsv = `date,close 1-May-12,58.13 30-Apr-12,53.98 27-Apr-12,67.00 26-Apr-12,89.70 25-Apr-12,99.00 24-Apr-12,130.28 23-Apr-12,166.70 20-Apr-12,234.98 19-Apr-12,345.44 18-Apr-12,443.34 17-Apr-12,543.70 16-Apr-12,580.13 13-Apr-12,605.23 12-Apr-12,622.77 11-Apr-12,626.20 10-Apr-12,628.44 9-Apr-12,636.23 5-Apr-12,633.68 4-Apr-12,624.31 3-Apr-12,629.32 2-Apr-12,618.63 30-Mar-12,599.55 29-Mar-12,609.86 28-Mar-12,617.62 27-Mar-12,614.48 26-Mar-12,606.98 ` var data = d3.csvParse(datacsv); // Get the data // d3.csv("data.csv").then(function(data) { // process(data) // }); process(data) function process(data) { var parseTime = d3.timeParse("%d-%b-%y"); data.forEach(function(d) { d.date = parseTime(d.date); d.close = +d.close; }); // Scale the range of the data x.domain(d3.extent(data, function(d) { return d.date; })); y.domain([0, d3.max(data, function(d) { return d.close; })]); // add the X gridlines svg.append("g") .attr("class", "grid") .attr("transform", "translate(0," + height + ")") .call( d3.axisBottom(x) .ticks(5) //.tickSize(-height) xaxis grid line .tickFormat("") ) // add the Y gridlines svg.append("g") .attr("class", "grid") .call( d3.axisLeft(y) .ticks(5) .tickSize(-width) .tickFormat("") ) svg.append("path") .data([data]) .attr("class", "line") .attr("d", valueline) .attr('fill', 'none') .attr('stroke','steelblue') .attr('stroke-width',2) svg.append("g") .attr("transform", "translate(0," + height + ")") .call(d3.axisBottom(x)); svg.append("g") .call(d3.axisLeft(y)); } /* .grid line { stroke: lightgrey; stroke-opacity: 0.7; shape-rendering: crispEdges; } .grid path { stroke-width: 0; } */ <script src="https://d3js.org/d3.v6.min.js"></script>
The d3 selection.style() method sets the style attribute of a given tag - allowing you to specify css style attributes. d3.selectAll() can be passed a css selector string, so this is: .grid line { stroke: lightgrey; stroke-opacity: 0.7; shape-rendering: crispEdges; } .grid path { stroke-width: 0; } Is similar to: d3.selectAll(".grid line") .style("stroke","lightgrey") .style("stroke-opacity",0.7) .style("shape-rendering","crispEdges") d3.selectAll(".grid path") .style("stroke-width",0) I say similar as the major difference is that by using .style() the elements have to already exist (otherwise we can't select them in order to modify them). So we could just place the above after the point you call the axis so we know the elements exist already: console.clear() var margin = {top: 20, right: 20, bottom: 30, left: 50}, width = 600 - margin.left - margin.right, height = 400 - margin.top - margin.bottom; var x = d3.scaleTime().range([0, width]); var y = d3.scaleLinear().range([height, 0]); var valueline = d3.line() .x(function(d) { return x(d.date); }) .y(function(d) { return y(d.close); }); var svg = d3.select("body").append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); var datacsv = `date,close 1-May-12,58.13 30-Apr-12,53.98 27-Apr-12,67.00 26-Apr-12,89.70 25-Apr-12,99.00 24-Apr-12,130.28 23-Apr-12,166.70 20-Apr-12,234.98 19-Apr-12,345.44 18-Apr-12,443.34 17-Apr-12,543.70 16-Apr-12,580.13 13-Apr-12,605.23 12-Apr-12,622.77 11-Apr-12,626.20 10-Apr-12,628.44 9-Apr-12,636.23 5-Apr-12,633.68 4-Apr-12,624.31 3-Apr-12,629.32 2-Apr-12,618.63 30-Mar-12,599.55 29-Mar-12,609.86 28-Mar-12,617.62 27-Mar-12,614.48 26-Mar-12,606.98 ` var data = d3.csvParse(datacsv); // Get the data // d3.csv("data.csv").then(function(data) { // process(data) // }); process(data) function process(data) { var parseTime = d3.timeParse("%d-%b-%y"); data.forEach(function(d) { d.date = parseTime(d.date); d.close = +d.close; }); // Scale the range of the data x.domain(d3.extent(data, function(d) { return d.date; })); y.domain([0, d3.max(data, function(d) { return d.close; })]); // add the X gridlines svg.append("g") .attr("class", "grid") .attr("transform", "translate(0," + height + ")") .call( d3.axisBottom(x) .ticks(5) //.tickSize(-height) xaxis grid line .tickFormat("") ) // add the Y gridlines svg.append("g") .attr("class", "grid") .call( d3.axisLeft(y) .ticks(5) .tickSize(-width) .tickFormat("") ) svg.append("path") .data([data]) .attr("class", "line") .attr("d", valueline) .attr('fill', 'none') .attr('stroke','steelblue') .attr('stroke-width',2) svg.append("g") .attr("transform", "translate(0," + height + ")") .call(d3.axisBottom(x)); svg.append("g") .call(d3.axisLeft(y)); d3.selectAll(".grid line") .style("stroke","lightgrey") .style("stroke-opacity",0.7) .style("shape-rendering","crispEdges") d3.selectAll(".grid path") .style("stroke-width",0) } /* { stroke: lightgrey; stroke-opacity: 0.7; shape-rendering: crispEdges; } .grid path { stroke-width: 0; } */ <script src="https://d3js.org/d3.v6.min.js"></script>
d3 line graph using csv
I have a data in csv like this: date,partner,units 2012-05-01,team1,34.12 2012-04-30,team1,45.56 2012-04-27,team2,67.89 2012-04-26,team1,78.54 2012-04-25,team2,89.23 2012-04-24,team2,99.23 2012-04-23,team2,101.34 I want to plot two lines (one for team1, one for team2 using this data), but I am just getting a scatterplot using the following complete d3 code, is my filtering wrong? <!DOCTYPE html> <meta charset="utf-8"> <style> /* set the CSS */ #line1 { fill: none; stroke: steelblue; stroke-width: 2px; } #line2 { fill: none; stroke: red; stroke-width: 2px; } </style> <input type="button" onclick="hideLine()"> <input type="button" onclick="showLine()"> <!-- load the d3.js library --> <script src="https://d3js.org/d3.v4.min.js"></script> <script> // set the dimensions and margins of the graph var margin = {top: 20, right: 20, bottom: 30, left: 50}, width = 960 - margin.left - margin.right, height = 500 - margin.top - margin.bottom; // parse the date / time var parseTime = d3.timeParse("%Y-%m-%d"); // set the ranges var x = d3.scaleTime().range([0, width]); var y = d3.scaleLinear().range([height, 0]); // define the 1st line var valueline = d3.line() .x(function(d) { return x(d.date); }) .y(function(d) { return y(d.units); }); // define the 2nd line var valueline2 = d3.line() .x(function(d) { return x(d.date); }) .y(function(d) { return y(d.units); }); var svg = d3.select("body").append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); // Get the data d3.csv("data1.csv", function(error, data) { if (error) throw error; // format the data data.forEach(function(d) { d.date = parseTime(d.date); d.units = +d.units; }); // Scale the range of the data x.domain(d3.extent(data, function(d) { return d.date; })); m = d3.max(data, function(d) { var m = d.units; return m; }); console.log("Max:", m); y.domain([0,m]); // Add the valueline path. svg.append("path") .data([data]) .filter(function(d) { return d.partner == 'team1'; }) .attr("id", "line1") .attr("d", valueline); console.log("DATA", data) svg.selectAll(".point") .data(data) .enter() .append("circle") .attr("class", "point") .attr("cx", function (d, i) { return x(d.date); }) .attr("cy", function(d) { return y(d.units); }) .attr("r", 4) .on("mouseover", function(d) { console.log(d.units) }); // Add the valueline2 path. svg.append("path") .data([data]) .filter(function(d) { return d.partner == 'team2'; }) .attr("id", "line2") .style("stroke", "red") .attr("d", valueline2) .on("mouseover", function(d) {console.log(d)}); // Add the X Axis svg.append("g") .attr("transform", "translate(0," + height + ")") .call(d3.axisBottom(x)); // Add the Y Axis svg.append("g") .call(d3.axisLeft(y)); }); function hideLine() { console.log("Hideline"); d3.select("#line2").attr("style", "opacity:0"); } function showLine() { console.log("ShowLine"); d3.select("#line2").attr("style", "opacity:1"); } </script> </body>
Yes, your filter is wrong. You are wrapping your data in another [] which means the filter is only operating on the outer array. Do this instead: svg.append("path") .datum(data.filter(function(d) { return d.partner == 'team1'; })) .attr("id", "line1") .attr("d", valueline); Working example here.
D3.js How to hide/show line when click select options?
I tried to use D3.js to draw lines when you click on different checkbox. It will get data of that option. Here is an example I used D3 multi-series line chart with tooltips and legend. I have got the specific data object that is corresponding the checkbox I choose. Howevery, I don't know how to draw it on svg. Please help me and I will appreciate you so much. I also find a website "www.cotrino.com/starpaths/" that shows the final effect I want to implement. My D3 effect <!DOCTYPE html> <meta charset="utf-8"> <style> .axis--x path { display: none; } .line { fill: none; stroke: steelblue; stroke-width: 1.5px; } </style> <script src="http://d3js.org/d3.v4.js"></script> <body> <svg width="1000" height="500"></svg> <div id="disease_list"></div> </body> <script> var svg = d3.select("svg"), margin = {top: 20, right: 80, bottom: 30, left: 50}, width = svg.attr("width") - margin.left - margin.right, height = svg.attr("height") - margin.top - margin.bottom, g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")"); //make a clip path for the graph var clip = svg.append("svg:clipPath") .attr("id", "clip") .append("svg:rect") .attr("x", 0) .attr("y", 0) .attr("width", width) .attr("height", height); var parseTime = d3.timeParse("%Y-%m"); var x = d3.scaleTime().range([0, width]), y = d3.scaleLinear().range([height, 0]); var line = d3.line() .curve(d3.curveBasis) .x(function(d) { console.log(d.date); return x(d.date); }) .y(function(d) { console.log(d.date); return y(d.count); }); var color = d3.scaleOrdinal(d3.schemeCategory20); d3.csv("./top10highestNormalize.csv", type, function(error, data) { if (error) throw error; var diseases = data.columns.slice(1).map(function(id) { return { id: id, values: data.map(function(d) { return {date: d.date, count: d[id]}; }) }; }); console.log(diseases); x.domain(d3.extent(data, function(d) { return d.date; })); y.domain([ d3.min(diseases, function(c) { return d3.min(c.values, function(d) { return d.count; }); }), d3.max(diseases, function(c) { return d3.max(c.values, function(d) { return d.count; }); }) ]); g.append("g") .attr("class", "axis axis--x") .attr("transform", "translate(0," + height + ")") .call(d3.axisBottom(x)); g.append("g") .attr("class", "axis axis--y") .call(d3.axisLeft(y)) .append("text") .attr("transform", "rotate(-90)") .attr("y", 6) .attr("dy", "0.71em") .attr("fill", "#000") .text("Count"); /* var disease = g.selectAll(".disease") .data(diseases) .enter().append("g") .attr("class", "disease"); */ // Create the shape selectors var selector = d3.select("#disease_list").append("select"); labels = selector.selectAll("option") .data(diseases) .enter() .append("option") .attr("value",function(d,i) {return i;}) .text(function(d) {return d.id;}); var menu = d3.select("#disease_list select") .on("change", redraw); // var series = menu.property("value"); //console.log(series); // all the meat goes in the redraw function function redraw() { console.log("redraw start"); // get value from menu selection // the option values are set in HTML and correspond //to the [type] value we used to nest the data var series = menu.property("value"); console.log(series); // only retrieve data from the selected series, using the nest we just created var adata = diseases[series]; console.log(adata); } }); function type(d, _, columns) { d.date = parseTime(d.date); for (var i = 1, n = columns.length, c; i < n; ++i) d[c = columns[i]] = +d[c]; return d; } </script> top10highestNormalize.csv date,disseminated sclerosis,sclerosis,gestural tics,venereal disease,bite,cot death,venereal disease,cardiovascular disease,diseases vascular,pruritis,pus,cystic fibrosis,fibroses 2010-04,0,0,0,0,0,0,0,0,0,0,0,0,0 2010-05,0,0,0.06898023,0.068783069,0.085790885,0.065761258,0.068783069,0,0,0.001204094,0.023051592,0,0 2010-06,0.076923077,0.076923077,0.190584554,0.199972867,0.201072386,0.171789373,0.199972867,0.071428571,0.071428571,0.004816376,0.031284303,0.2,0.2 2010-07,0.230769231,0.230769231,0.221590101,0.224664225,0.225201072,0.235167977,0.224664225,0.214285714,0.285714286,0.00602047,0.038419319,0,0 2010-08,0.538461538,0.538461538,0.174797326,0.182471849,0.174262735,0.192041935,0.182471849,0.071428571,0.071428571,0.003612282,0.023051592,0,0 2010-09,0.230769231,0.230769231,0.287725786,0.277845611,0.252010724,0.259471051,0.277845611,0,0,0.004214329,0.046652031,0,0 2010-10,0.076923077,0.076923077,0.295406059,0.299416633,0.285969616,0.265665952,0.299416633,0,0.071428571,0.007224564,0.03402854,0.066666667,0.066666667 2010-11,0.153846154,0.153846154,0.284027877,0.279337946,0.261840929,0.276149631,0.279337946,0,0,0.006622517,0.050493963,0,0 2010-12,0.153846154,0.153846154,0.271511876,0.237552571,0.213583557,0.237312366,0.237552571,0.142857143,0.142857143,0.004214329,0.035126235,0,0 2011-01,0.076923077,0.076923077,0.306642014,0.312440646,0.28150134,0.305694544,0.312440646,0.142857143,0.142857143,0.006622517,0.046103183,0,0.066666667 2011-02,0.076923077,0.076923077,0.288721377,0.262243929,0.219839142,0.25899452,0.262243929,0.142857143,0.142857143,0.007224564,0.038968167,0,0.066666667 2011-03,0.076923077,0.076923077,0.271654103,0.255324922,0.253798034,0.266857279,0.255324922,0.071428571,0.071428571,0.007224564,0.051591658,0,0 2011-04,0.461538462,0.461538462,0.291423695,0.252068919,0.235031278,0.284250655,0.252068919,0,0,0.009030704,0.045005488,0,0 2011-05,0.153846154,0.153846154,0.448158157,0.380681047,0.351206434,0.439123183,0.380681047,0,0,0.011438892,0.079582876,0.333333333,0.4 2011-06,0.153846154,0.153846154,0.498079932,0.437661104,0.391420912,0.424827258,0.437661104,0.142857143,0.142857143,0.009632751,0.063117453,0,0.066666667 2011-07,0,0,0.410467928,0.424094424,0.419124218,0.379080295,0.424094424,0,0.071428571,0.009030704,0.061470911,1,1 2011-08,0.076923077,0.076923077,0.268382876,0.262922263,0.238605898,0.267810341,0.262922263,0.214285714,0.214285714,0.002408188,0.038968167,0,0 2011-09,0.230769231,0.230769231,0.510027023,0.469949803,0.470956211,0.444841553,0.469949803,0,0,0.014449127,0.075740944,0.133333333,0.2 2011-10,0.076923077,0.076923077,0.462380885,0.434540768,0.431635389,0.417679295,0.434540768,0.142857143,0.142857143,0.006622517,0.073545554,0,0.066666667 2011-11,0.153846154,0.153846154,0.519698478,0.457061457,0.415549598,0.443888492,0.457061457,0.142857143,0.142857143,0.01384708,0.06805708,0.2,0.2 2011-12,1,1,0.382449154,0.35002035,0.319928508,0.315701692,0.35002035,0,0,0.002408188,0.060373216,0,0 2012-01,0.461538462,0.461538462,0.492390841,0.45312712,0.409294013,0.45389564,0.45312712,0.571428571,0.571428571,0.007224564,0.060373216,0,0 2012-02,0.076923077,0.076923077,0.382875836,0.375932709,0.350312779,0.369073147,0.375932709,0.071428571,0.071428571,0.003612282,0.049945115,0.066666667,0.066666667 2012-03,0.923076923,1,1,0.922127255,1,0.871098404,0.922127255,0.5,0.5,0.01384708,0.171789243,0,0.066666667 2012-04,0.230769231,0.307692308,0.699331532,0.676977344,0.63360143,0.645699309,0.676977344,0.142857143,0.142857143,0.012040939,0.092206367,0.133333333,0.133333333 2012-05,0.846153846,0.846153846,0.801735173,0.752408086,0.776586238,0.7436264,0.752408086,0.785714286,0.785714286,0.016857315,0.131723381,0.466666667,0.466666667 2012-06,0.384615385,0.461538462,0.730479306,0.732193732,0.625558534,0.657850846,0.732193732,0,0,0.011438892,0.118002195,0.6,0.666666667 2012-07,0.384615385,0.384615385,0.751386716,0.738434405,0.71849866,0.714081487,0.738434405,0.285714286,0.285714286,0.009030704,0.126783754,0.2,0.2 2012-08,0.384615385,0.461538462,0.700327123,0.643467643,0.619302949,0.646890636,0.643467643,0.285714286,0.285714286,0.012642986,0.150933041,0.2,0.266666667 2012-09,0.076923077,0.230769231,0.72137676,0.701804368,0.63538874,0.70455087,0.701804368,0.214285714,0.214285714,0.011438892,0.130076839,0.066666667,0.066666667 2012-10,0.230769231,0.230769231,0.846252311,0.863112196,0.796246649,0.825827972,0.863112196,0.071428571,0.071428571,0.036724865,0.127881449,0.333333333,0.333333333 2012-11,0.692307692,0.692307692,0.895605177,1,0.798927614,0.909935668,1,0.214285714,0.357142857,0.012642986,0.143798024,0,0.133333333 2012-12,0.923076923,1,0.795903854,0.803283137,0.683646113,0.827257565,0.803283137,0.142857143,0.142857143,0.008428657,0.104829857,0.6,0.6 2013-01,0.230769231,0.384615385,0.92106386,0.964862298,0.848078642,0.944007624,0.964862298,0.285714286,0.357142857,0.015653221,0.146542261,0.533333333,0.733333333 2013-02,0.153846154,0.307692308,0.830322856,0.872880206,0.798927614,0.755777937,0.872880206,0.142857143,0.142857143,0.010234798,0.110318332,0,0.066666667 2013-03,0.230769231,0.230769231,0.927037406,0.944105277,0.885612154,0.953061711,0.944105277,0.142857143,0.142857143,0.009632751,0.131174533,0,0.133333333 2013-04,0.384615385,0.384615385,0.796046082,0.775471442,0.671134942,0.715749345,0.775471442,0,0,0.012040939,0.12349067,0.133333333,0.133333333 2013-05,0.923076923,1,0.824633765,0.844254511,0.742627346,0.843697879,0.844254511,0.142857143,0.142857143,0.015653221,0.149286498,0,0 2013-06,0.307692308,0.307692308,0.884369222,0.949667616,0.865951743,1,0.949667616,0.071428571,0.071428571,0.020469597,0.135016465,0.466666667,0.466666667 2013-07,0.461538462,0.461538462,0.864172948,0.935829602,0.843610366,0.939480581,0.935829602,0.071428571,0.071428571,0.015051174,0.128979144,0.066666667,0.2 2013-08,0.153846154,0.153846154,0.670886076,0.738163071,0.753351206,0.821300929,0.738163071,0.071428571,0.214285714,0.012642986,0.098243688,0,0 2013-09,0.230769231,0.230769231,0.876262267,0.861484195,0.744414656,0.996426019,0.861484195,0,0,0.024081878,0.144895719,0.066666667,0.066666667 2013-10,0.615384615,0.615384615,0.917508178,0.885361552,0.806970509,0.841315225,0.885361552,0.642857143,0.642857143,0.030704395,0.115806806,0.2,0.4 2013-11,0,0.076923077,0.857061584,0.903540904,0.791778374,0.845127472,0.903540904,0.5,0.5,0.012642986,0.093852909,0,0 2013-12,0.230769231,0.230769231,0.704878396,0.719169719,0.584450402,0.81915654,0.719169719,0.285714286,0.5,0.015653221,0.108122942,0,0 2014-01,0.461538462,0.461538462,0.900014223,0.856328856,0.717605004,0.98903979,0.856328856,0.357142857,0.5,0.030102348,0.137211855,0,0.066666667 2014-02,0,0,0.707865169,0.703296703,0.63717605,0.796997856,0.703296703,1,1,0.012642986,0.097145993,0,0 2014-03,0.230769231,0.230769231,0.815531219,0.800434134,0.7256479,0.786275911,0.800434134,0.714285714,0.714285714,0.009632751,0.099341383,0.533333333,0.6 2014-04,0.153846154,0.153846154,0.756506898,0.790259124,0.615728329,0.778174887,0.790259124,0,0,0.011438892,0.12349067,0,0 2014-05,0.461538462,0.461538462,0.85990613,0.767331434,0.705987489,0.78008101,0.767331434,0.142857143,0.285714286,0.014449127,0.13611416,0.066666667,0.133333333 2014-06,0.076923077,0.153846154,0.670886076,0.713064713,0.615728329,0.735763641,0.713064713,0.285714286,0.285714286,0.010836845,0.102634468,0,0 2014-07,0.076923077,0.076923077,0.672592803,0.801655135,0.621090259,0.680009531,0.801655135,0.071428571,0.071428571,0.007224564,0.103183315,0,0 2014-08,0.384615385,0.461538462,0.487270659,0.58377425,0.486148347,0.575887539,0.58377425,0.071428571,0.071428571,0.005418423,0.079582876,0,0.133333333 2014-09,0,0.076923077,0.715545442,0.678062678,0.669347632,0.705980462,0.678062678,0,0,0.01384708,0.103183315,0,0.066666667 2014-10,0.230769231,0.307692308,0.742995306,0.723511057,0.630920465,0.679294734,0.723511057,0,0,0.016857315,0.1064764,0,0 2014-11,0,0,0.672735031,0.623388957,0.583556747,0.64927329,0.623388957,0,0,0.004816376,0.115806806,0.066666667,0.066666667 2014-12,0.307692308,0.384615385,0.591096572,0.55704789,0.478999106,0.491303312,0.55704789,0.285714286,0.428571429,0.003010235,0.074643249,0,0 2015-01,0.076923077,0.153846154,0.659223439,0.561117894,0.531724754,0.605432452,0.561117894,0.071428571,0.071428571,0.007224564,0.094401756,0.133333333,0.133333333 2015-02,0.230769231,0.307692308,0.61840421,0.564780898,0.512064343,0.585656421,0.564780898,0.071428571,0.071428571,0.007224564,0.096597146,0,0 2015-03,0,0,0.770302944,0.677927011,0.599642538,0.675482487,0.677927011,0.071428571,0.071428571,0.009632751,0.111964874,0.066666667,0.2 2015-04,0.076923077,0.076923077,0.706016214,0.61687695,0.731903485,0.563497736,0.61687695,0.071428571,0.071428571,0.008428657,0.097145993,0,0 2015-05,0,0.076923077,0.655383303,0.614027947,0.55406613,0.6154396,0.614027947,0.071428571,0.071428571,0.012642986,0.099341383,0,0 2015-06,0,0.076923077,0.564357844,0.540632207,0.527256479,0.598284489,0.540632207,0.142857143,0.142857143,0.00602047,0.091657519,0,0 2015-07,0.076923077,0.076923077,0.486417295,0.525301859,0.511170688,0.566356922,0.525301859,0,0,0.015653221,0.08726674,0.066666667,0.066666667 2015-08,0.230769231,0.230769231,0.408476746,0.386379053,0.320822163,0.465094115,0.386379053,0,0,0.003010235,0.056531284,0,0 2015-09,0.538461538,0.538461538,0.870999858,0.792701126,0.747095621,0.883964737,0.792701126,0,0,0.013245033,0.156421515,0,0 2015-10,0.153846154,0.153846154,0.469492249,0.435490435,0.320822163,0.51227067,0.435490435,0,0,0.174593618,0.221734358,0,0 2015-11,0.153846154,0.153846154,0.322998151,0.309455976,0.273458445,0.346676197,0.309455976,0,0,0.462974112,0.481888035,0.133333333,0.133333333 2015-12,0.076923077,0.076923077,0.342767743,0.309320309,0.27971403,0.384798666,0.309320309,0,0,0.464780253,0.482436883,0.066666667,0.066666667 2016-01,0.307692308,0.384615385,0.415872564,0.349477683,0.358355675,0.442458899,0.349477683,0,0,0.559903672,0.581229418,0.066666667,0.066666667 2016-02,0,0,0.445455838,0.403744404,0.316353887,0.457469621,0.403744404,0,0,0.54846478,0.568605928,0.066666667,0.066666667 2016-03,0,0,0.471198976,0.400352734,0.317247542,0.508220157,0.400352734,0.142857143,0.142857143,0.604455148,0.628430296,0,0 2016-04,0,0,0.582989617,0.570343237,0.575513852,0.603764594,0.570343237,0.214285714,0.214285714,1,1,0,0
You need to create your line variable: var myLine = svg.append("path"); And then, inside redraw(), changing it according to the option selected: myLine.datum(adata.values) .attr("d", line); Here is a demo plunker: https://plnkr.co/edit/YjGO9TLDBXj13JQuO5bm?p=preview PS: I changed your x-scale range: var x = d3.scaleTime().range([margin.left, width]); And also added a call to redraw() when the code runs for the first time.
d3 brushing and mouse move coexist
im trying to update http://bl.ocks.org/d3noob/6eb506b129f585ce5c8a and add brushing into it(brushing displayed under the line graph) to make it look like https://www.google.com.hk/#q=s%26p+500 added coded to the first link: var brush = d3.svg.brush() .x(x) .on("brush", brushmove) .on("brushend", brushend); svg.append("g") .attr("class", "brush") .call(brush) .selectAll('rect') .attr('height', height); function brushmove() { var extent = brush.extent(); } function brushend() { x.domain(brush.extent()) console.log(brush.extent()); } The problem is that once i add brushing into it, there's a background formed behind the graph and i can't perform mouse events(mousemove) anymore. Is there a way to fix it to make it look like google? 1) brushing and mouse event coexist 2) brushing area under the curve var csv = date,close1, close2 26-Mar-12,606.98,58.13 27-Mar-12,614.48,53.98 28-Mar-12,617.62,67.00 29-Mar-12,609.86,89.70 30-Mar-12,599.55,99.00 2-Apr-12,618.63,130.28 3-Apr-12,629.32,166.70 4-Apr-12,624.31,234.98 5-Apr-12,633.68,345.44 9-Apr-12,636.23,443.34 10-Apr-12,628.44,543.70 11-Apr-12,626.20,580.13 12-Apr-12,622.77,605.23 13-Apr-12,605.23,626.20 16-Apr-12,580.13,628.44 17-Apr-12,543.70,636.23 18-Apr-12,443.34,633.68 19-Apr-12,345.44,624.31 20-Apr-12,234.98,629.32 23-Apr-12,166.70,618.63 24-Apr-12,130.28,599.55 25-Apr-12,99.00,609.86 26-Apr-12,89.70,617.62 27-Apr-12,67.00,614.48 30-Apr-12,53.98,606.98 1-May-12,58.13,503.15
Both the example you link to and the brush add a rect on top of the plot to capture mouse events. The key to making them coexist is to add the brush (and allow it to create its rect) and then use that rect to add the tooltip events. This way you only end up with one point-events rect: // add a g for the brush var context = svg.append("g"); // add the brush context.call(brush); // grab the brush's rect and add the tooltip events context.select(".background") .on("mouseover", function() { focus.style("display", null); }) .on("mouseout", function() { focus.style("display", "none"); }) .on("mousemove", mousemove); Full code: <!DOCTYPE html> <meta charset="utf-8"> <style> /* set the CSS */ body { font: 12px Arial; } path { stroke: steelblue; stroke-width: 2; fill: none; } .axis path, .axis line { fill: none; stroke: grey; stroke-width: 1; shape-rendering: crispEdges; } .extent { stroke: #fff; fill-opacity: .125; shape-rendering: crispEdges; } </style> <body> <!-- load the d3.js library --> <script src="http://d3js.org/d3.v3.min.js"></script> <script> // Set the dimensions of the canvas / graph var margin = { top: 30, right: 20, bottom: 30, left: 50 }, width = 600 - margin.left - margin.right, height = 270 - margin.top - margin.bottom; // Parse the date / time var parseDate = d3.time.format("%d-%b-%y").parse, formatDate = d3.time.format("%d-%b"), bisectDate = d3.bisector(function(d) { return d.date; }).left; // Set the ranges var x = d3.time.scale().range([0, width]); var y = d3.scale.linear().range([height, 0]); // Define the axes var xAxis = d3.svg.axis().scale(x) .orient("bottom").ticks(5); var yAxis = d3.svg.axis().scale(y) .orient("left").ticks(5); // Define the line var valueline = d3.svg.line() .x(function(d) { return x(d.date); }) .y(function(d) { return y(d.close); }); var area = d3.svg.area() .x(function(d) { return x(d.date); }) .y0(height) .y1(function(d) { return y(d.close); }); // Adds the svg canvas var svg = d3.select("body") .append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); var defs = svg.append("defs"); var areaClip = defs.append("clipPath") .attr("id", "areaClip") .append("rect") .attr("x", width) .attr("y", 0) .attr("width", width) .attr("height", height); var lineSvg = svg.append("g"); var focus = svg.append("g") .style("display", "none"); var brush = d3.svg.brush() .x(x) .on("brush", function() { var s = brush.extent(), x1 = x(s[0]), x2 = x(s[1]); areaClip.attr('x', x1); areaClip.attr('width', x2 - x1); }) var csv = `date,close 26-Mar-12,606.98 27-Mar-12,614.48 28-Mar-12,617.62 29-Mar-12,609.86 30-Mar-12,599.55 2-Apr-12,618.63 3-Apr-12,629.32 4-Apr-12,624.31 5-Apr-12,633.68 9-Apr-12,636.23 10-Apr-12,628.44 11-Apr-12,626.20 12-Apr-12,622.77 13-Apr-12,605.23 16-Apr-12,580.13 17-Apr-12,543.70 18-Apr-12,443.34 19-Apr-12,345.44 20-Apr-12,234.98 23-Apr-12,166.70 24-Apr-12,130.28 25-Apr-12,99.00 26-Apr-12,89.70 27-Apr-12,67.00 30-Apr-12,53.98 1-May-12,58.13`; var data = d3.csv.parse(csv); data.forEach(function(d) { d.date = parseDate(d.date); d.close = +d.close; }); // Scale the range of the data x.domain(d3.extent(data, function(d) { return d.date; })); y.domain([0, d3.max(data, function(d) { return d.close+20; })]); // Add the valueline path. lineSvg.append("path") .attr("class", "line") .attr("d", valueline(data)); lineSvg.append("path") .attr("d", area(data)) .style("fill", "steelblue") .style("stroke", "none") .style("opacity", "0.5") .attr("clip-path", "url(#areaClip)") // Add the X Axis svg.append("g") .attr("class", "x axis") .attr("transform", "translate(0," + height + ")") .call(xAxis); // Add the Y Axis svg.append("g") .attr("class", "y axis") .call(yAxis); // append the x line focus.append("line") .attr("class", "x") .style("stroke", "blue") .style("stroke-dasharray", "3,3") .style("opacity", 0.5) .attr("y1", 0) .attr("y2", height); // append the y line focus.append("line") .attr("class", "y") .style("stroke", "blue") .style("stroke-dasharray", "3,3") .style("opacity", 0.5) .attr("x1", width) .attr("x2", width); // append the circle at the intersection focus.append("circle") .attr("class", "y") .style("fill", "none") .style("stroke", "blue") .attr("r", 4); // place the value at the intersection focus.append("text") .attr("class", "y1") .style("stroke", "white") .style("stroke-width", "3.5px") .style("opacity", 0.8) .attr("dx", 8) .attr("dy", "-.3em"); focus.append("text") .attr("class", "y2") .attr("dx", 8) .attr("dy", "-.3em"); // place the date at the intersection focus.append("text") .attr("class", "y3") .style("stroke", "white") .style("stroke-width", "3.5px") .style("opacity", 0.8) .attr("dx", 8) .attr("dy", "1em"); focus.append("text") .attr("class", "y4") .attr("dx", 8) .attr("dy", "1em"); // append the rectangle to capture mouse var context = svg.append("g"); context.call(brush); context.selectAll(".resize").append("path") .attr("d", "M0,2V" + (height - 2)) .style("stroke", "black") context.select(".extent") .attr("height", height - 2) .attr("fill", "none"); context.select(".background") .attr("height", height) .on("mouseover.tooltip", function() { focus.style("display", null); }) .on("mouseout.tooltip", function() { focus.style("display", "none"); }) .on("mousemove.tooltip", mousemove); function mousemove() { var x0 = x.invert(d3.mouse(this)[0]), i = bisectDate(data, x0, 1), d0 = data[i - 1], d1 = data[i], d = x0 - d0.date > d1.date - x0 ? d1 : d0; focus.select("circle.y") .attr("transform", "translate(" + x(d.date) + "," + y(d.close) + ")"); focus.select("text.y1") .attr("transform", "translate(" + x(d.date) + "," + y(d.close) + ")") .text(d.close); focus.select("text.y2") .attr("transform", "translate(" + x(d.date) + "," + y(d.close) + ")") .text(d.close); focus.select("text.y3") .attr("transform", "translate(" + x(d.date) + "," + y(d.close) + ")") .text(formatDate(d.date)); focus.select("text.y4") .attr("transform", "translate(" + x(d.date) + "," + y(d.close) + ")") .text(formatDate(d.date)); focus.select(".x") .attr("transform", "translate(" + x(d.date) + "," + y(d.close) + ")") .attr("y2", height - y(d.close)); focus.select(".y") .attr("transform", "translate(" + width * -1 + "," + y(d.close) + ")") .attr("x2", width + width); } </script> </body>
d3js stack chart: how to add text on each layer?
In the example of Stacked Area via Nest, I would like to add text on each layer. Like The position of the text doesn't really matters, just being center-right.
You can do something like this: 0) Calculte the center of the path using the function below: function getMyCentroid(element) { var bbox = element.getBBox(); return [bbox.x + bbox.width / 2, bbox.y + bbox.height / 2]; } 1) Next select all path and calculate its center. 2) Append a text and position it at the center svg.append("text") 3) Set the text associated with the path like this .text(d3.select(d).data()[0].key); d3.selectAll(".layer")[0].forEach(function(d) { var centroid = getMyCentroid(d); svg.append("text").attr("x", centroid[0]).attr("y", centroid[1]).text(d3.select(d).data()[0].key); }) var format = d3.time.format("%m/%d/%y"); var margin = {top: 20, right: 30, bottom: 30, left: 40}, width = 960 - margin.left - margin.right, height = 500 - margin.top - margin.bottom; var x = d3.time.scale() .range([0, width]); var y = d3.scale.linear() .range([height, 0]); var z = d3.scale.category20c(); var xAxis = d3.svg.axis() .scale(x) .orient("bottom") .ticks(d3.time.days); var yAxis = d3.svg.axis() .scale(y) .orient("left"); var stack = d3.layout.stack() .offset("zero") .values(function(d) { return d.values; }) .x(function(d) { return d.date; }) .y(function(d) { return d.value; }); var nest = d3.nest() .key(function(d) { return d.key; }); var area = d3.svg.area() .interpolate("cardinal") .x(function(d) { return x(d.date); }) .y0(function(d) { return y(d.y0); }) .y1(function(d) { return y(d.y0 + d.y); }); var svg = d3.select("body").append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); d3.csv("https://gist.githubusercontent.com/cyrilcherian/e6f56b1b9337142c0bde/raw/8b1728849e193db0e8b960ecb750062f4e0cb487/data.csv", function(error, data) { if (error) throw error; data.forEach(function(d) { d.date = format.parse(d.date); d.value = +d.value; }); var layers = stack(nest.entries(data)); x.domain(d3.extent(data, function(d) { return d.date; })); y.domain([0, d3.max(data, function(d) { return d.y0 + d.y; })]); svg.selectAll(".layer") .data(layers) .enter().append("path") .attr("class", "layer") .attr("d", function(d) { return area(d.values); }) .style("fill", function(d, i) { return z(i); }); svg.append("g") .attr("class", "x axis") .attr("transform", "translate(0," + height + ")") .call(xAxis); svg.append("g") .attr("class", "y axis") .call(yAxis); function getMyCentroid(element) { var bbox = element.getBBox(); return [bbox.x + bbox.width / 2, bbox.y + bbox.height / 2]; } d3.selectAll(".layer")[0].forEach(function(d) { var centroid = getMyCentroid(d); svg.append("text").attr("x", centroid[0]).attr("y", centroid[1]).text(d3.select(d).data()[0].key).style("fill", "red"); }) }); body { font: 10px sans-serif; } .axis path, .axis line { fill: none; stroke: #000; shape-rendering: crispEdges; } <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script> Hope this helps!