I trying to display a d3 linechart. I've a problem - I cannot stop the date from repeating. How can I stop the date keep repeating? I only want to show two columns(17/12/2013 and 18/12/2013) based on the JSON data reflected below. Or what do I need to do so the first tickmark would show 17/12/2013 and the last one would show 18/12/2013?
[
{
"key": "Excited",
"values": [ [1387212490000, 0], [1387298890000 , 10] ]
},
{
"key": "Sad",
"values": [ [1387212490000, 20], [1387298890000 , 50] ]
},
{
"key": "Angry",
"values": [ [1387212490000, 30], [1387298890000 , 30] ]
},
{
"key": "Happy",
"values": [ [1387212490000, 40], [1387298890000 , 70] ]
}
]
Below is the JS script
$(document).ready(function() {
d3.json('sales.json', function(data) {
nv.addGraph(function() {
var chart = nv.models.lineChart().x(function(d) {
return d[0]
}).y(function(d) {
return d[1]
}).color(d3.scale.category10().range())
.useInteractiveGuideline(true);
chart.xAxis.tickFormat(function(d) {
return d3.time.format('%d/%m/%Y')(new Date(d))
});
//chart.xScale(d3.time.scale());
d3.select('#nvd3 svg').datum(data).transition().duration(500).call(chart);
nv.utils.windowResize(chart.update);
return chart;
});
});
});
You didn't show enough code so it may be difficult to debug...
Anyway, try this and I'm working on an example to prove it...
chart.xAxis
.tickFormat(function(d) {
return d3.time.format('%d/%m/%Y')(new Date(d))
})
.ticks(d3.time.days, 1)
<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/1.11.1/jquery.min.js"></script>
var margin = {top: 20, right: 40, bottom: 30, left: 20},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom,
barWidth = Math.floor(width / 19) - 1;
var x = d3.scale.linear()
.range([barWidth / 2, width - barWidth / 2]);
var y = d3.scale.linear()
.range([height, 0]);
var yAxis = d3.svg.axis()
.scale(y)
.orient("right")
.tickSize(-width)
.tickFormat(function(d) { return Math.round(d / 1e6) + "M"; });
// An SVG element with a bottom-right origin.
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 + ")");
// A sliding container to hold the bars by birthyear.
var birthyears = svg.append("g")
.attr("class", "birthyears");
// A label for the current year.
var title = svg.append("text")
.attr("class", "title")
.attr("dy", ".71em")
.text(2000);
d3.csv("population.csv", function(error, data) {
// Convert strings to numbers.
data.forEach(function(d) {
d.people = +d.people;
d.year = +d.year;
d.age = +d.age;
});
// Compute the extent of the data set in age and years.
var age1 = d3.max(data, function(d) { return d.age; }),
year0 = d3.min(data, function(d) { return d.year; }),
year1 = d3.max(data, function(d) { return d.year; }),
year = year1;
// Update the scale domains.
x.domain([year1 - age1, year1]);
y.domain([0, d3.max(data, function(d) { return d.people; })]);
// Produce a map from year and birthyear to [male, female].
data = d3.nest()
.key(function(d) { return d.year; })
.key(function(d) { return d.year - d.age; })
.rollup(function(v) { return v.map(function(d) { return d.people; }); })
.map(data);
// Add an axis to show the population values.
svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate(" + width + ",0)")
.call(yAxis)
.selectAll("g")
.filter(function(value) { return !value; })
.classed("zero", true);
// Add labeled rects for each birthyear (so that no enter or exit is required).
var birthyear = birthyears.selectAll(".birthyear")
.data(d3.range(year0 - age1, year1 + 1, 5))
.enter().append("g")
.attr("class", "birthyear")
.attr("transform", function(birthyear) { return "translate(" + x(birthyear) + ",0)"; });
birthyear.selectAll("rect")
.data(function(birthyear) { return data[year][birthyear] || [0, 0]; })
.enter().append("rect")
.attr("x", -barWidth / 2)
.attr("width", barWidth)
.attr("y", y)
.attr("height", function(value) { return height - y(value); });
// Add labels to show birthyear.
birthyear.append("text")
.attr("y", height - 4)
.text(function(birthyear) { return birthyear; });
// Add labels to show age (separate; not animated).
svg.selectAll(".age")
.data(d3.range(0, age1 + 1, 5))
.enter().append("text")
.attr("class", "age")
.attr("x", function(age) { return x(year - age); })
.attr("y", height + 4)
.attr("dy", ".71em")
.text(function(age) { return age; });
// Allow the arrow keys to change the displayed year.
window.focus();
d3.select(window).on("keydown", function() {
switch (d3.event.keyCode) {
case 37: year = Math.max(year0, year - 10); break;
case 39: year = Math.min(year1, year + 10); break;
}
update();
});
function update() {
if (!(year in data)) return;
title.text(year);
birthyears.transition()
.duration(750)
.attr("transform", "translate(" + (x(year1) - x(year)) + ",0)");
birthyear.selectAll("rect")
.data(function(birthyear) { return data[year][birthyear] || [0, 0]; })
.transition()
.duration(750)
.attr("y", y)
.attr("height", function(value) { return height - y(value); });
}
});
svg {
font: 10px sans-serif;
}
.y.axis path {
display: none;
}
.y.axis line {
stroke: #fff;
stroke-opacity: .2;
shape-rendering: crispEdges;
}
.y.axis .zero line {
stroke: #000;
stroke-opacity: 1;
}
.title {
font: 300 78px Helvetica Neue;
fill: #666;
}
.birthyear,
.age {
text-anchor: middle;
}
.birthyear {
fill: #fff;
}
rect {
fill-opacity: .6;
fill: #e377c2;
}
rect:first-child {
fill: #1f77b4;
}
Related
I'm trying to figure out how to get two d3 graphs onto the same page with one on the left and one on the right. However, what I'm getting is this.
This is my html file.
<!DOCTYPE html>
<meta charset="utf-8">
<style>
svg {
font: 10px sans-serif;
}
.polyline path {
fill: none;
stroke: #666;
shape-rendering: crispEdges;
}
.axis line,
.axis path {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.axis text {
text-shadow: 0 1px 0 #fff, 1px 0 0 #fff, 0 -1px 0 #fff, -1px 0 0 #fff;
cursor: move;
}
.xaxis text {
font: 10px sans-serif;
}
.yaxis text {
font: 10px sans-serif;
}
.xaxis path,
.xaxis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.yaxis path,
.yaxis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
</style>
<body>
<svg class="chart"></svg>
<script src="//d3js.org/d3.v3.min.js"></script>
<script src = "project3.js">//https://my.up.ist.psu.edu/lng5099/project3.html </script>
</body>
</html>
This is my js file.
(function() {
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.scale.ordinal().rangePoints([0, width], 1),
y = {};
var axis = d3.svg.axis().orient("left");
var line = d3.svg.line() //define a function to convert points into a polyline
.x(function(d) { return d.x; })
.y(function(d) { return d.y; })
.interpolate("linear");//line style. you can try "cardinal".
var chart = d3.select(".chart")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var cars=[];
d3.csv("cars.csv", type, function(error, data) {
cars = data;
drawPC();
});
function drawPC() {
// Extract the list of dimensions and create a scale for each.
for (var dim in cars[0]) {
if (dim != "name") {
y[dim] = d3.scale.linear()
.domain([d3.min(cars, function(d) { return +d[dim]; }), d3.max(cars, function(d) { return +d[dim]; })])
.range([height,0]);
}
}
x.domain(dimensions = d3.keys(cars[0]).filter(function(d) { return d != "name";}));
//draw polylines
for (var i=1; i< cars.length; i++) { //for each car
//prepare the coordinates for a polyline
var lineData = []; //initialize an array for coordinates of a polyline
for (var prop in cars[0]) { //get each dimension
if (prop != "name" ) { //skip the name dimension
var point = {}; //initialize a coordinate object
var val = cars[i][prop]; //obtain the value of a car in a dimension
point['x'] = x(prop); //x value: mapping the dimension
point['y'] = y[prop](val);//y value: mapping the value in that dimension
lineData.push(point); //add the object into the array
}
}
//draw a polyline based on the coordindates
chart.append("g")
.attr("class", "polyline")
.append("path") // a path shape
.attr("d", line(lineData)); //line() is a function to turn coordinates into SVG commands
}
//next: draw individual dimension lines
//position dimension lines appropriately
var g = chart.selectAll(".dimension")
.data(dimensions)
.enter().append("g")
.attr("class", "dimension")
.attr("transform", function(d) { return "translate(" + x(d) + ")"; }); //translate each axis
// Add an axis and title.
g.append("g")
.attr("class", "axis")
.each(function(d) { d3.select(this).call(axis.scale(y[d])); })
.append("text")
.style("text-anchor", "middle")
.attr("y", -9)
.text(function(d) { return d; });
};
//this function coerces numerical data to numbers
function type(d) {
d.economy = +d.economy; // coerce to number
d.displacement = +d.displacement; // coerce to number
d.power = +d.power; // coerce to number
d.weight = +d.weight; // coerce to number
d.year = +d.year;
return d;
}
})();
(function() {
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.scale.linear().range([50, width]),
y = d3.scale.linear().range([height-20,0]);
var chart = d3.select(".chart")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var xAxis = d3.svg.axis().scale(x).orient("bottom");
var yAxis = d3.svg.axis().scale(y).orient("left");
var cars=[];
d3.csv("cars.csv", type, function(error, data) {
cars = data;
drawXY();
});
function drawXY(){
x.domain([d3.min(cars, function(d) { return d.year; }), d3.max(cars, function(d) { return d.year; })]);
y.domain([d3.min(cars, function(d) { return d.power; }), d3.max(cars, function(d) { return d.power; })]);
var yPos = height -20;
chart.append("g")
.attr("class", "xaxis")
.attr("transform", "translate(0," + yPos + ")")
.call(xAxis);
chart.append("g")
.attr("class", "yaxis")
.attr("transform", "translate(50,0)")
.call(yAxis);
chart.selectAll(".dot")
.data(cars)
.enter().append("circle")
.attr("class", "dot")
.attr("cx", function(d) { return x(d.year); })
.attr("cy", function(d) { return y(d.power); })
.attr("r", 3);
}
d3.selectAll("circle").data(cars).enter()
.append("circle")
.classed("circle", true)
.on("mouseover", function() { d3.select(d3.event.target).classed("highlight", true); })
.on("mouseout", function() { d3.select(d3.event.target).classed("highlight", false); });
function type(d) {
d.economy = +d.economy; // coerce to number
d.displacement = +d.displacement; // coerce to number
d.power = +d.power; // coerce to number
d.weight = +d.weight; // coerce to number
d.year = +d.year;
return d;
}
})();
Both are displaying, but I'm not sure how set it so that they position properly. Please help.
Figured it out there was an issue with the positioning of my margins, width and height. There is the java code for anyone who needs the help.
(function() {
var margin = {top: 20, right: 30, bottom: 30, left: 40},
width = 700 - margin.left - margin.right,
height = 600 - margin.top - margin.bottom;
var x = d3.scale.ordinal().rangePoints([0, width], 1),
y = {};
var axis = d3.svg.axis().orient("left");
var line = d3.svg.line() //define a function to convert points into a polyline
.x(function(d) { return d.x; })
.y(function(d) { return d.y; })
.interpolate("linear");//line style. you can try "cardinal".
var chart = d3.select(".chart")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var cars=[];
d3.csv("cars.csv", type, function(error, data) {
cars = data;
drawPC();
});
function drawPC() {
// Extract the list of dimensions and create a scale for each.
for (var dim in cars[0]) {
if (dim != "name") {
y[dim] = d3.scale.linear()
.domain([d3.min(cars, function(d) { return +d[dim]; }), d3.max(cars, function(d) { return +d[dim]; })])
.range([height,0]);
}
}
x.domain(dimensions = d3.keys(cars[0]).filter(function(d) { return d != "name";}));
//draw polylines
for (var i=1; i< cars.length; i++) { //for each car
//prepare the coordinates for a polyline
var lineData = []; //initialize an array for coordinates of a polyline
for (var prop in cars[0]) { //get each dimension
if (prop != "name" ) { //skip the name dimension
var point = {}; //initialize a coordinate object
var val = cars[i][prop]; //obtain the value of a car in a dimension
point['x'] = x(prop); //x value: mapping the dimension
point['y'] = y[prop](val);//y value: mapping the value in that dimension
lineData.push(point); //add the object into the array
}
}
//draw a polyline based on the coordindates
chart.append("g")
.attr("class", "polyline")
.append("path") // a path shape
.attr("d", line(lineData)); //line() is a function to turn coordinates into SVG commands
}
//next: draw individual dimension lines
//position dimension lines appropriately
var g = chart.selectAll(".dimension")
.data(dimensions)
.enter().append("g")
.attr("class", "dimension")
.attr("transform", function(d) { return "translate(" + x(d) + ")"; }); //translate each axis
// Add an axis and title.
g.append("g")
.attr("class", "axis")
.each(function(d) { d3.select(this).call(axis.scale(y[d])); })
.append("text")
.style("text-anchor", "middle")
.attr("y", -9)
.text(function(d) { return d; });
};
//this function coerces numerical data to numbers
function type(d) {
d.economy = +d.economy; // coerce to number
d.displacement = +d.displacement; // coerce to number
d.power = +d.power; // coerce to number
d.weight = +d.weight; // coerce to number
d.year = +d.year;
return d;
}
})();
(function() {
var margin = {top: 20, right: 30, bottom: 30, left: 690},
width = 1300 - margin.left - margin.right,
height = 600 - margin.top - margin.bottom;
var x = d3.scale.linear().range([50, width]),
y = d3.scale.linear().range([height-20,0]);
var chart = d3.select(".chart")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var xAxis = d3.svg.axis().scale(x).orient("bottom");
var yAxis = d3.svg.axis().scale(y).orient("left");
var cars=[];
d3.csv("cars.csv", type, function(error, data) {
cars = data;
drawXY();
});
function drawXY(){
x.domain([d3.min(cars, function(d) { return d.year; }), d3.max(cars, function(d) { return d.year; })]);
y.domain([d3.min(cars, function(d) { return d.power; }), d3.max(cars, function(d) { return d.power; })]);
var yPos = height -20;
chart.append("g")
.attr("class", "xaxis")
.attr("transform", "translate(0," + yPos + ")")
.call(xAxis);
chart.append("g")
.attr("class", "yaxis")
.attr("transform", "translate(50,0)")
.call(yAxis);
chart.selectAll(".dot")
.data(cars)
.enter().append("circle")
.attr("class", "dot")
.attr("cx", function(d) { return x(d.year); })
.attr("cy", function(d) { return y(d.power); })
.attr("r", 3);
}
d3.selectAll("circle").data(cars).enter()
.append("circle")
.classed("circle", true)
.on("mouseover", function() { d3.select(d3.event.target).classed("highlight", true); })
.on("mouseout", function() { d3.select(d3.event.target).classed("highlight", false); });
function type(d) {
d.economy = +d.economy; // coerce to number
d.displacement = +d.displacement; // coerce to number
d.power = +d.power; // coerce to number
d.weight = +d.weight; // coerce to number
d.year = +d.year;
return d;
}
})();
can you resolve this problem.
I am not able to rotate in X-axis values. can you please check below examples. Now x-axis text is coming horizontally but we wants Vertical alignment.
In my requirement is rotate -60 or -90 only. in "Model 1 , Module 2, Module 3" values i needs to rotate.
var margin = {top: 20, right: 30, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom,
padding = 0.3;
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], padding);
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickFormat(function(d) { return dollarFormatter(d); });
var chart = d3.select(".chart")
.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", type, function(error, data) {
var data = [{ name :"Module 1",value : 20 },{ name :"Module 2",value :15},{ name :"Module 3 ",value :45},
{ name :"Final Count ",value :200}];
//console.log(data);
// Transform data (i.e., finding cumulative values and total) for easier charting
var cumulative = 0;
for (var i = 0; i < data.length; i++) {
data[i].start = cumulative;
cumulative += data[i].value;
data[i].end = cumulative;
data[i].class = ( data[i].value >= 0 ) ? 'positive' : 'negative'
}
data.push({
name: 'Total',
end: cumulative,
start: 0,
class: 'total'
});
x.domain(data.map(function(d) { return d.name; }));
y.domain([0, d3.max(data, function(d) { return d.end; })]);
chart.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
chart.append("g")
.attr("class", "y axis")
.call(yAxis);
var bar = chart.selectAll(".bar")
.data(data)
.enter().append("g")
.attr("class", function(d) { return "bar " + d.class })
.attr("transform", function(d) { return "translate(" + x(d.name) + ",0)"; });
bar.append("rect")
.attr("y", function(d) { return y( Math.max(d.start, d.end) ); })
.attr("height", function(d) { return Math.abs( y(d.start) - y(d.end) ); })
.attr("width", x.rangeBand());
bar.append("text")
.attr("x", x.rangeBand() / 2)
.attr("y", function(d) { return y(d.end) + 5; })
.attr("dy", function(d) { return ((d.class=='negative') ? '-' : '') + ".75em" })
.text(function(d) { return dollarFormatter(d.end - d.start);});
bar.filter(function(d) { return d.class != "total" }).append("line")
.attr("class", "connector")
.attr("x1", x.rangeBand() + 5 )
.attr("y1", function(d) { return y(d.end) } )
.attr("x2", x.rangeBand() / ( 1 - padding) - 5 )
.attr("y2", function(d) { return y(d.end) } )
//});
function type(d) {
d.value = +d.value;
return d;
}
function dollarFormatter(n) {
n = Math.round(n);
var result = n;
if (Math.abs(n) > 1000) {
result = Math.round(n/1000) + 'K';
}
return result;
}
.bar.total rect {
fill: steelblue;
}
.bar.positive rect {
fill: darkolivegreen;
}
.bar.negative rect {
fill: crimson;
}
.bar line.connector {
stroke: grey;
stroke-dasharray: 3;
}
.bar text {
fill: white;
font: 10px sans-serif;
text-anchor: middle;
}
.axis text {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
<script src="http://d3js.org/d3.v3.min.js"></script>
<svg class="chart"></svg>
To rotate only the first 3 ticks ("module 1", "module 2" and "module 3"):
var ticks = d3.selectAll(".x.axis text").each(function(d, i) {
if (i < 3) {
d3.select(this).attr("y", 0)
d3.select(this).attr("x", 10)
d3.select(this).attr("dy", ".35em")
d3.select(this).attr("transform", "rotate(90)")
d3.select(this).style("text-anchor", "start");
}
});
Check the demo:
var margin = {
top: 20,
right: 30,
bottom: 60,
left: 40
},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom,
padding = 0.3;
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], padding);
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickFormat(function(d) {
return dollarFormatter(d);
});
var chart = d3.select(".chart")
.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", type, function(error, data) {
var data = [{
name: "Module 1",
value: 20
}, {
name: "Module 2",
value: 15
}, {
name: "Module 3 ",
value: 45
}, {
name: "Final Count ",
value: 200
}];
//console.log(data);
// Transform data (i.e., finding cumulative values and total) for easier charting
var cumulative = 0;
for (var i = 0; i < data.length; i++) {
data[i].start = cumulative;
cumulative += data[i].value;
data[i].end = cumulative;
data[i].class = (data[i].value >= 0) ? 'positive' : 'negative'
}
data.push({
name: 'Total',
end: cumulative,
start: 0,
class: 'total'
});
x.domain(data.map(function(d) {
return d.name;
}));
y.domain([0, d3.max(data, function(d) {
return d.end;
})]);
chart.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
var ticks = d3.selectAll(".x.axis text").each(function(d, i) {
if (i < 3) {
d3.select(this).attr("y", 0)
d3.select(this).attr("x", 10)
d3.select(this).attr("dy", ".35em")
d3.select(this).attr("transform", "rotate(90)")
d3.select(this).style("text-anchor", "start");
}
});
chart.append("g")
.attr("class", "y axis")
.call(yAxis);
var bar = chart.selectAll(".bar")
.data(data)
.enter().append("g")
.attr("class", function(d) {
return "bar " + d.class
})
.attr("transform", function(d) {
return "translate(" + x(d.name) + ",0)";
});
bar.append("rect")
.attr("y", function(d) {
return y(Math.max(d.start, d.end));
})
.attr("height", function(d) {
return Math.abs(y(d.start) - y(d.end));
})
.attr("width", x.rangeBand());
bar.append("text")
.attr("x", x.rangeBand() / 2)
.attr("y", function(d) {
return y(d.end) + 5;
})
.attr("dy", function(d) {
return ((d.class == 'negative') ? '-' : '') + ".75em"
})
.text(function(d) {
return dollarFormatter(d.end - d.start);
});
bar.filter(function(d) {
return d.class != "total"
}).append("line")
.attr("class", "connector")
.attr("x1", x.rangeBand() + 5)
.attr("y1", function(d) {
return y(d.end)
})
.attr("x2", x.rangeBand() / (1 - padding) - 5)
.attr("y2", function(d) {
return y(d.end)
})
//});
function type(d) {
d.value = +d.value;
return d;
}
function dollarFormatter(n) {
n = Math.round(n);
var result = n;
if (Math.abs(n) > 1000) {
result = Math.round(n / 1000) + 'K';
}
return result;
}
.bar.total rect {
fill: steelblue;
}
.bar.positive rect {
fill: darkolivegreen;
}
.bar.negative rect {
fill: crimson;
}
.bar line.connector {
stroke: grey;
stroke-dasharray: 3;
}
.bar text {
fill: white;
font: 10px sans-serif;
text-anchor: middle;
}
.axis text {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
<script src="http://d3js.org/d3.v3.min.js"></script>
<svg class="chart"></svg>
I have been trying to adapt the chained transition script of Mike Bostock to work with multiple lines but I do not get it to work. After the first display the lines and labels fly out of the plot and do not show anymore Whereas everything gets updated (I can see the values of the lines changing when inspecting the javascript console) . I do not understand what I am doing wrong. I will post the (lengthy) code here below (apologies for the length). I would appreciate any help, thank you!
<!DOCTYPE html>
<head>
<title>Modified Chained Transitions</title>
<meta charset="utf-8">
<script src="https://d3js.org/d3.v3.min.js"></script>
<style>
body {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
margin: auto;
position: relative;
width: 960px;
}
text {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis path {
display: none;
}
.line {
fill: none;
stroke: steelblue;
stroke-width: 1.5px;
}
form {
position: absolute;
right: 10px;
top: 10px;
}
</style>
</head>
<body>
<br>
<button type="button"> Request data</button>
<div id='chart'> </div>
</body>
<script>
var margin = {top: 20, right: 80, bottom: 30, left: 50},
width = 750 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var parseDate = d3.time.format("%Y%m%d").parse;
var xScale = d3.time.scale()
.range([0, width]);
var yScale = d3.scale.linear()
.range([height, 0]);
var color = d3.scale.category10();
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left");
var line = d3.svg.line()
.interpolate("basis")
.x(function(d) { return xScale(d.date); })
.y(function(d) { return yScale(d.temperature); });
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 getNewData = function() {
var data = [];
var counter = 0;
function generate(){
var startDate = new Date;
counter += 1;
var range = counter % 2 === 0 ? 10 : 100;
for (i = 0; i < 100; i++) {
data[i] = {"date": new Date(startDate - i),
"New York": Math.random() * (range - 1),
"San Francisco": Math.random() * (range - 1),
"Austin": Math.random() * (range - 10)};
}
return data;
}
return {
new: function () {return generate()}
};
}; // function getNewData()
var newData = getNewData();
data = newData.new();
color.domain(d3.keys(data[0]).filter(function(key) { return key !== "date"; }));
var cities = color.domain().map(function(name) {
return {
name: name,
values: data.map(function(d) {
return { date: d.date, temperature: +d[name]};
})
};
});
xScale.domain(d3.extent(data, function(d) { return d.date; }));
yScale.domain([
d3.min(cities, function(c) {
return d3.min(c.values, function(v) { return v.temperature; }); }),
d3.max(cities, function(c) {
return d3.max(c.values, function(v) { return v.temperature; }); })
]);
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", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Temperature (ºF)");
var city = svg.selectAll(".city")
.data(cities)
.enter().append("g")
.attr("class", "city");
city.append("path")
.attr("class", "line")
.attr("d", function(d) { return line(d.values); })
.style("stroke", function(d) { return color(d.name); });
city.append("text")
.datum(function(d) { return {name: d.name, values: d.values[0]}; })
.attr("class", "label")
.attr("transform", function(d) { return "translate(" +
xScale(d.values.date) + "," + yScale(d.values.temperature) + ")"; })
.attr("x", 3)
.attr("dy", ".35em")
.text(function(d) { return d.name; });
d3.selectAll("button").on("click", change);
function change() {
data = newData.new();
color.domain(d3.keys(data[0]).filter(function(key) { return key !== "date"; }));
cities = color.domain().map(function(name) {
return {
name: name,
values: data.map(function(d) {
return { date: d.date, temperature: +d[name]};
})
};
});
console.log(cities[0].values[0]);
xScale.domain(d3.extent(data, function(d) { return d.date; }));
yScale.domain([
d3.min(cities, function(c) {
return d3.min(c.values, function(v) { return v.temperature; }); }),
d3.max(cities, function(c) {
return d3.max(c.values, function(v) { return v.temperature; }); })
]);
var t0 = svg.transition().duration(750);
t0.selectAll(".line")
.attr("d", function(cities) { return line(cities.values); })
.style("stroke", function(cities) { return color(cities.name); });
t0.selectAll(".label").attr("transform",
"translate(0,0)").text(function(cities) { return cities.name; });
var t1 = t0.transition();
// t1.selectAll(".line").attr("d", line(data));
// t1.select(".line")
t1.selectAll(".line")
// t1.selectAll(".city")
.attr("d", function(cities) { return line(cities.values); })
.style("stroke", function(cities) { return color(cities.name); });
t1.select(".y.axis").call(yAxis);
t1.select(".x.axis").call(xAxis);
t1.select(".label")
.attr("transform", function(d) { return "translate(" +
xScale(d.values.date) + "," +
yScale(d.values.temperature) + ")"; });
} // function change()
</script>
</html>
I can help fix your transitions but I'm not sure what you are attempting to "chain". In the linked example, Bostock swaps one line for another (transition 1), then fits that line to a new domain (transition 2). You do not seem to want to swap lines, so you fit a new domain and then transition the lines to it (transition 1) but what's transition 2?
Now to answer your more direct question of why your transitions aren't working, it's simply because you never update your data. In the linked example Bostock has both datasets bound to his line and then swaps which he's drawing in the line function. You, though, only ever have the original dataset bound. Quick fix is:
function change() {
... //<-- get new data
// bind your new data
var cities = svg.selectAll(".city")
.data(cities)
// sub selection to transition line
cities
.select(".line")
.transition()
.duration(750)
.attr("d", function(d) { return line(d.values); })
.style("stroke", function(d) { return color(d.name); })
// concurrent sub selection to move labels
cities
.select(".label")
.transition()
.duration(750)
.attr("transform", function(d){
var last = d.values[0];
return "translate(" + xScale(last.date) + "," + yScale(last.temperature) + ")";
})
}
Running code:
<!DOCTYPE html>
<head>
<title>Modified Chained Transitions</title>
<meta charset="utf-8">
<script src="https://d3js.org/d3.v3.min.js"></script>
<style>
body {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
margin: auto;
position: relative;
width: 960px;
}
text {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis path {
display: none;
}
.line {
fill: none;
stroke: steelblue;
stroke-width: 1.5px;
}
form {
position: absolute;
right: 10px;
top: 10px;
}
</style>
</head>
<body>
<br>
<button type="button"> Request data</button>
<div id='chart'> </div>
</body>
<script>
var margin = {
top: 20,
right: 80,
bottom: 30,
left: 50
},
width = 500 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var parseDate = d3.time.format("%Y%m%d").parse;
var xScale = d3.time.scale()
.range([0, width]);
var yScale = d3.scale.linear()
.range([height, 0]);
var color = d3.scale.category10();
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left");
var line = d3.svg.line()
.interpolate("basis")
.x(function(d) {
return xScale(d.date);
})
.y(function(d) {
return yScale(d.temperature);
});
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 getNewData = function() {
var data = [];
var counter = 0;
function generate() {
var startDate = new Date;
counter += 1;
var range = counter % 2 === 0 ? 10 : 100;
for (i = 0; i < 100; i++) {
data[i] = {
"date": new Date(startDate - i),
"New York": Math.random() * (range - 1),
"San Francisco": Math.random() * (range - 1),
"Austin": Math.random() * (range - 10)
};
}
return data;
}
return {
new: function() {
return generate()
}
};
}; // function getNewData()
var newData = getNewData();
data = newData.new();
color.domain(d3.keys(data[0]).filter(function(key) {
return key !== "date";
}));
var cities = color.domain().map(function(name) {
return {
name: name,
values: data.map(function(d) {
return {
date: d.date,
temperature: +d[name]
};
})
};
});
xScale.domain(d3.extent(data, function(d) {
return d.date;
}));
yScale.domain([
d3.min(cities, function(c) {
return d3.min(c.values, function(v) {
return v.temperature;
});
}),
d3.max(cities, function(c) {
return d3.max(c.values, function(v) {
return v.temperature;
});
})
]);
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", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Temperature (ºF)");
var city = svg.selectAll(".city")
.data(cities)
.enter().append("g")
.attr("class", "city");
city.append("path")
.attr("class", "line")
.attr("d", function(d) {
return line(d.values);
})
.style("stroke", function(d) {
return color(d.name);
});
city.append("text")
.datum(function(d) {
return {
name: d.name,
values: d.values[0]
};
})
.attr("class", "label")
.attr("transform", function(d) {
return "translate(" +
xScale(d.values.date) + "," + yScale(d.values.temperature) + ")";
})
.attr("x", 3)
.attr("dy", ".35em")
.text(function(d) {
return d.name;
});
d3.selectAll("button").on("click", change);
function change() {
data = newData.new();
color.domain(d3.keys(data[0]).filter(function(key) {
return key !== "date";
}));
cities = color.domain().map(function(name) {
return {
name: name,
values: data.map(function(d) {
return {
date: d.date,
temperature: +d[name]
};
})
};
});
xScale.domain(d3.extent(data, function(d) {
return d.date;
}));
yScale.domain([
d3.min(cities, function(c) {
return d3.min(c.values, function(v) {
return v.temperature;
});
}),
d3.max(cities, function(c) {
return d3.max(c.values, function(v) {
return v.temperature;
});
})
]);
var cities = svg.selectAll(".city")
.data(cities)
cities
.select(".line")
.transition()
.duration(750)
.attr("d", function(d) {
return line(d.values);
})
.style("stroke", function(d) {
return color(d.name);
})
cities
.select(".label")
.transition()
.duration(750)
.attr("transform", function(d) {
var last = d.values[0];
return "translate(" + xScale(last.date) + "," + yScale(last.temperature) + ")";
})
svg.selectAll(".y.axis")
.transition()
.duration(750)
.call(yAxis);
svg.selectAll(".x.axis")
.transition()
.duration(750)
.call(xAxis);
} // function change()
</script>
</html>
var margin = {top: 20, right: 30, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom,
padding = 0.3;
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], padding);
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickFormat(function(d) { return percentage(d); })
var tooltip = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0)
.attr("align","middle");
var chart = d3.select(".chart")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
data = [
{"name":"Product Revenue","value":420000},
{"name":"Services Revenue","value":210000},
{"name":"Employee Revenue","value":190000},
{"name":"Fixed Costs","value":-170000},
{"name":"Variable Costs","value":-140000}
];
//function to find all the positive values
var positive_val = data.filter(function(d) { return d.value > 0; });
console.log(JSON.stringify(positive_val));
//function to calculate the sum of all the positive values
var maxSum = positive_val.reduce(function(sum, d) {
return sum + d.value;
}, 0);
console.log("The maximum sum is "+maxSum);
//to calculate the new Domain by adding 120
var yaxisRange=maxSum+120;
console.log("The y axis sum is "+yaxisRange);
var newDomain=percentage(yaxisRange);
console.log(newDomain);
var newDomain = newDomain.replace(/[!##$%^&*]/g, "");
console.log(newDomain);
// Transform data (i.e., finding cumulative values and total)
var cumulative = 0;
for (var i = 0; i < data.length; i++) {
data[i].start = cumulative;
cumulative += data[i].value;
data[i].end = cumulative;
data[i].class = ( data[i].value >= 0 ) ? 'positive' : 'negative'
}
data.push({
name: 'Total',
end: cumulative,
start: 0,
class: 'total',
value: cumulative
});
x.domain(data.map(function(d) { return d.name; }));
y.domain([0, d3.max(data, function(d) { return d.end; })]);
//WHen i try to use this as my new domain,the bar increase the height
//y.domain([0,newDomain]);
debugger;
chart.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
chart.append("g")
.attr("class", "y axis")
.call(yAxis);
var bar = chart.selectAll(".bar")
.data(data)
.enter().append("g")
.attr("class", function(d) { return "bar " + d.class })
.attr("transform", function(d) { return "translate(" + x(d.name) + ",0)"; });
bar.append("rect")
//.attr("y", function(d) { return y(d.value); })
.attr("y", function(d) { return y( Math.max(d.start, d.end) ); })
.attr("height", function(d) { return Math.abs( y(d.start) - y(d.end) ); })
//function to draw the tooltip
.attr("width", x.rangeBand()).on("mouseover", function(d) {
// to find the parent node,to calculate the x position
var parentG = d3.select(this.parentNode);
var barPos = parseFloat(parentG.attr('transform').split("(")[1]);
var xPosition = barPos+x.rangeBand()/2;
//to find the y position
var yPosition = parseFloat(d3.select(this).attr("y"))+ Math.abs( y(d.start) - y(d.end))/2;
tooltip.transition()
.duration(200)
.style("opacity", .9);
tooltip.html(d.name + "<br/>" + percentage(d.value))
.style("left", xPosition + "px")
.style("top", yPosition + "px");
}).on("mouseout", function(d) {
tooltip.transition()
.duration(500)
.style("opacity", 0);
});
bar.append("text")
.attr("x", x.rangeBand() / 2)
.attr("y", function(d) { return y(d.end) + 5; })
.attr("dy", function(d) { return ((d.class=='negative') ? '-' : '') + ".75em" })
.text(function(d) { return percentage(d.end - d.start);});
bar.filter(function(d) { return d.class != "total" }).append("line")
.attr("class", "connector")
.attr("x1", x.rangeBand() + 5 )
.attr("y1", function(d) { return y(d.end) } )
.attr("x2", x.rangeBand() / ( 1 - padding) - 5 )
.attr("y2", function(d) { return y(d.end) } )
function type(d) {
d.value = +d.value;
return d;
}
function percentage(n) {
n = Math.round(n);
var result = n;
if (Math.abs(n) > 100) {
result = Math.round(n/100) + '%';
}
return result;
}
-Here is the updated fiddle http://jsfiddle.net/7mkq4k8k/21/
-I want to make the yaxis label increase .for eg 9000,9500.I have calculated the newDomian.
-If i try to add this domain,my chart doesnt get drawn properly.The height of the bars increase ,and the due to this the rest of the bars are not drawn.Please help me in this issue.
So the chart you initially draw is based on this domain :
y.domain([0, d3.max(data, function (d) {
return d.end;
})]);
Try to console.log(d3.max(data, function (d) {return d.end;})) and you will find it returns 820000, which is the maximum of your cumulative calculation. That means your chart is drawn with a domain from 0 to 820000.
Now let's talk about your newDomain. You're taking the percentage of your maxSum, which means your newDomain is equal to 8201. So now you're trying to draw your chart from 0 to 8201.
But your bars height is calculated like this :
Math.abs(y(d.start) - y(d.end)), which means you are calculating ranges from y(0) to y(820000) (d.end is max equal to 820000).
y(820000) doesn't fit, as you specified with your domain that it could max go to y(8201). That's why your bars are reaching over the very top of your chart, because the domain you're giving doesn't correspond the numbers inside :
y(this number is too big and doesn't fit because it is not between 0 and newDomain).
How to solve this ?
You define your domain correctly, removing the percentage line
//function to calculate the sum of all the positive values
var maxSum = positive_val.reduce(function (sum, d) {
return sum + d.value;
}, 0);
console.log("The maximum sum is " + maxSum);
//to calculate the new Domain by adding 520000 (big number to show you it works)
var newDomain = maxSum + 520000;
console.log(newDomain); //1340000
y.domain([0,newDomain]);
Working snippet below :
var margin = {
top: 20,
right: 30,
bottom: 30,
left: 40
},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom,
padding = 0.3;
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], padding);
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickFormat(function (d) {
return percentage(d);
})
var tooltip = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0)
.attr("align", "middle");
var chart = d3.select(".chart")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
data = [{
"name": "Product Revenue",
"value": 420000
}, {
"name": "Services Revenue",
"value": 210000
}, {
"name": "Employee Revenue",
"value": 190000
}, {
"name": "Fixed Costs",
"value": -170000
}, {
"name": "Variable Costs",
"value": -140000
}
];
//function to find all the positive values
var positive_val = data.filter(function (d) {
return d.value > 0;
});
console.log(JSON.stringify(positive_val));
//function to calculate the sum of all the positive values
var maxSum = positive_val.reduce(function (sum, d) {
return sum + d.value;
}, 0);
console.log("The maximum sum is " + maxSum);
//to calculate the new Domain by adding 120
var newDomain = maxSum + 520000;
console.log(newDomain);
// Transform data (i.e., finding cumulative values and total)
var cumulative = 0;
for (var i = 0; i < data.length; i++) {
data[i].start = cumulative;
cumulative += data[i].value;
data[i].end = cumulative;
data[i].class = (data[i].value >= 0) ? 'positive' : 'negative'
}
data.push({
name: 'Total',
end: cumulative,
start: 0,
class: 'total',
value: cumulative
});
x.domain(data.map(function (d) {
return d.name;
}));
console.log(d3.max(data, function (d) {
return d.end;
}));
/*y.domain([0, d3.max(data, function (d) {
return d.end;
})]);*/
//WHen i try to use this as my new domain,the bar increase the height
y.domain([0,newDomain]);
debugger;
chart.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
chart.append("g")
.attr("class", "y axis")
.call(yAxis);
var bar = chart.selectAll(".bar")
.data(data)
.enter().append("g")
.attr("class", function (d) {
return "bar " + d.class
})
.attr("transform", function (d) {
return "translate(" + x(d.name) + ",0)";
});
bar.append("rect")
//.attr("y", function(d) { return y(d.value); })
.attr("y", function (d) {
return y(Math.max(d.start, d.end));
})
.attr("height", function (d) {
return Math.abs(y(d.start) - y(d.end));
})
//function to draw the tooltip
.attr("width", x.rangeBand()).on("mouseover", function (d) {
// to find the parent node,to calculate the x position
var parentG = d3.select(this.parentNode);
var barPos = parseFloat(parentG.attr('transform').split("(")[1]);
var xPosition = barPos + x.rangeBand() / 2;
//to find the y position
var yPosition = parseFloat(d3.select(this).attr("y")) + Math.abs(y(d.start) - y(d.end)) / 2;
tooltip.transition()
.duration(200)
.style("opacity", .9);
tooltip.html(d.name + "<br/>" + percentage(d.value))
.style("left", xPosition + "px")
.style("top", yPosition + "px");
}).on("mouseout", function (d) {
tooltip.transition()
.duration(500)
.style("opacity", 0);
});
bar.append("text")
.attr("x", x.rangeBand() / 2)
.attr("y", function (d) {
return y(d.end) + 5;
})
.attr("dy", function (d) {
return ((d.class == 'negative') ? '-' : '') + ".75em"
})
.text(function (d) {
return percentage(d.end - d.start);
});
bar.filter(function (d) {
return d.class != "total"
}).append("line")
.attr("class", "connector")
.attr("x1", x.rangeBand() + 5)
.attr("y1", function (d) {
return y(d.end)
})
.attr("x2", x.rangeBand() / (1 - padding) - 5)
.attr("y2", function (d) {
return y(d.end)
})
function type(d) {
d.value = +d.value;
return d;
}
function percentage(n) {
n = Math.round(n);
var result = n;
if (Math.abs(n) > 100) {
result = Math.round(n / 100) + '%';
}
return result;
}
.bar.total rect {
fill: steelblue;
}
.bar:hover rect {
fill:orange;
}
.bar.positive rect {
fill: darkolivegreen;
}
.bar:hover rect {
fill:orange;
}
.bar.negative rect {
fill: crimson;
}
.bar:hover rect {
fill:orange;
}
.bar line.connector {
stroke: grey;
stroke-dasharray: 3;
}
.bar text {
fill: white;
font: 12px sans-serif;
text-anchor: middle;
}
.axis text {
font: 10px sans-serif;
}
.axis path, .axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
div.tooltip {
position:absolute;
text-align: center;
padding: 2px;
font: 12px sans-serif;
background: #33CC00;
border: 0px;
border-radius: 8px;
pointer-events: none;
width: 90px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<svg class="chart"></svg>
Hope this helps !
I want to animate a sort of stacked bar chart from this chart with params like in this chart
I well succeed to sort x axis, but I didn't succeed to move stacked bars.
here is my code :
HTML
<button id="test">
Sort values
</button>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
CSS
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.bar {
fill: steelblue;
}
.x.axis path {
display: none;
}
JAVASCRIPT
var margin = {
top: 20,
right: 20,
bottom: 30,
left: 40
},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scale.ordinal().rangeRoundBands([0, width], .1);
var y = d3.scale.linear().rangeRound([height, 0]);
var color = d3.scale.ordinal().range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
var xAxis = d3.svg.axis().scale(x).orient("bottom");
var yAxis = d3.svg.axis().scale(y).orient("left").tickFormat(d3.format(".2s"));
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.tsv", function(error, data) {
if (error)
throw error;
color.domain(d3.keys(data[0]).filter(function(key) {
return key !== "State";
}));
data.forEach(function(d) {
var y0 = 0;
d.ages = color.domain().map(function(name) {
return {
name: name,
y0: y0,
y1: y0 += +d[name]
};
});
d.total = d.ages[d.ages.length - 1].y1;
});
//
x.domain(data.map(function(d) {
return d.State;
}));
y.domain([0, d3.max(data, function(d) {
return d.total;
})]);
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", 6).attr("dy", ".71em").style("text-anchor", "end").text("Population");
var state = svg.selectAll(".state").data(data).enter().append("g").attr("class", "g").attr("transform", function(d) {
return "translate(" + x(d.State) + ",0)";
});
state.selectAll("rect").data(function(d) {
return d.ages;
}).enter().append("rect").attr("width", x.rangeBand()).attr("y", function(d) {
return y(d.y1);
}).attr("height", function(d) {
return y(d.y0) - y(d.y1);
}).style("fill", function(d) {
return color(d.name);
});
var legend = svg.selectAll(".legend").data(color.domain().slice().reverse()).enter().append("g").attr("class", "legend").attr("transform", function(d, i) {
return "translate(0," + i * 20 + ")";
});
legend.append("rect").attr("x", width - 18).attr("width", 18).attr("height", 18).style("fill", color);
legend.append("text").attr("x", width - 24).attr("y", 9).attr("dy", ".35em").style("text-anchor", "end").text(function(d) {
return d;
});
d3.select("#test").on("click", change);
function change() {
data.sort(function(a, b) {
return b.total - a.total;
});
x.domain(data.map(function(d) {
return d.State;
}));
y.domain([0, d3.max(data, function(d) {
return d.total;
})]);
var transition = svg.transition().duration(750),
delay = function(d, i) {
return i * 50;
};
//problem seems coming from here
transition.selectAll("g.rect").delay(delay).attr("width", x.rangeBand()).attr("y", function(d) {
return y(d.y1);
}).attr("height", function(d) {
return y(d.y0) - y(d.y1);
});
//x axis is sorted
transition.select(".x.axis").call(xAxis).selectAll("g").delay(delay);
}
});
Thanks for helping
This can be done like this. Giving a class to the g group which holds the full stack:
//Now each g which holds a stack has a class for selection.
var state = svg.selectAll(".state").data(data).enter().append("g").attr("class", "g").attr("transform", function (d) {
return "translate(" + x(d.State) + ",0)";
}).attr("id", function (d) {
return d.State;
}).attr("class", "stack");
And then in the change code which triggers on change of the select do transition like this:
//translate the stack post sorting.
transition.selectAll(".stack")
.delay(delay)
.attr("transform", function (d) {
return "translate(" + x0(d.State) + ",0)";
});
I have added comments in code for you to understand the demo.
Full working fiddle here.