I've put together a multi line chart which has a transition set so the lines are 'drawn' and labels attached one after the other. It works but I can't see how to access the length of each line individually. The code that I have returns the length for the first path only via .node()
. The result of this is that all paths are given the length of the first which gives incorrect start points for the other paths. The code is below. Any help is much appreciated.
var margin = {top: 20, right: 80, bottom: 30, left: 60},
width = 600 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.rangePoints([0, width], 1);
var y = d3.scale.linear()
.range([height, 0]);
var color = d3.scale.category20();
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var line = d3.svg.line()
.interpolate("linear")
.x(function(d) { return x(d.month); })
.y(function(d) { return y(d.rainfall); });
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("data.csv", function(error, data) {
color.domain(d3.keys(data[0]).filter(function(key) { return key !== "Month"; }));
var countries = color.domain().map(function(country) {
return {
country: country,
values: data.map(function(d) {
return {month: d.Month, rainfall: +d[country]};
})
};
});
x.domain(data.map(function(d) { return d.Month; }));
y.domain([
d3.min(countries, function(c) { return d3.min(c.values, function(v) { return v.rainfall - 7; }); }),
d3.max(countries, function(c) { return d3.max(c.values, function(v) { return v.rainfall + 7; }); })
]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", "1.1em")
.style("text-anchor", "end")
.text("Rainfall (mm)");
var country = svg.selectAll(".country")
.data(countries)
.enter().append("g")
.attr("class", "country");
var path = country.append("path")
.attr("id", function(d, i) { return "line" + i; })
.attr("class", "line")
.attr("d", function(d) { return line(d.values); })
.style("stroke", function(d) { return color(d.country); })
var totalLength = path.node().getTotalLength();
path.attr("stroke-dasharray", totalLength + " " + totalLength)
.attr("stroke-dashoffset", totalLength)
.transition()
.delay(function(d, i) { return i * 1000; })
.duration(1000)
.ease("linear")
.attr("stroke-dashoffset", 0)
.each("end", function() {
labels.transition()
.delay(function(d, i) { return i * 1000; })
.style("opacity", 1);
});
var labels = country.append("text")
.datum(function(d) { return {country: d.country, value: d.values[d.values.length - 1]}; })
.attr("class", "label")
.attr("transform", function(d) { return "translate(" + x(d.value.month) + "," + y(d.value.rainfall) + ")"; })
.attr("x", 3)
.attr("dy", ".35em")
.style("opacity", 0)
.text(function(d) { return d.country; });
});
You could add the total length of the path as another attribute to the data and then use that:
path.each(function(d) { d.totalLength = this.getTotalLength(); })
.attr("stroke-dasharray", function(d) { return d.totalLength + " " + d.totalLength; })
.attr("stroke-dashoffset", function(d) { return d.totalLength; })
// etc
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With