d3 bar chart transition y-axis upside down - javascript

I am new in d3 and I am trying to draw animated bar chart (following this example). I was able to generate the chart but the problem I am facing is that the motion is upside down (start from height of the bar builds the bar towards the x-axis). I want the motion starts from the x-axis and go up!
This is my code
var y0 = d3.scale.linear().range([height, 0]);
var y1 = d3.scale.linear().range([height, 0]);
..
..
control.selectAll("rect")
.data(function(d) { return d.category; })
.enter().append("rect")
.attr("width", x1.rangeBand())
.attr("x", function(d) { return x1(d.name); })
.attr("height", 0)
.attr("y", function(d, i) { if(columnNum == 2 && i == 2) return y1(d.value); else return y0(d.value); })
.on('mouseover', tip.show)
.on('mouseout', tip.hide)
.style("fill", function(d) { return color(d.name); });
control.selectAll("rect")
.transition()
.delay(function(d, i) { return i * 100; })
.duration(1000)
.attr("height", function(d, i) { if(columnNum == 2 && i == 2) return y1(d.value); else return y0(d.value); })
.attr("y", function(d, i) { if(columnNum == 2 && i == 2) return height - y1(d.value); else return height - y0(d.value); });
columnNum is just an indicator to number of columns per group.
Thanks for all the help.

Initialise y to the x axis position (i.e. height) for this:
control.selectAll("rect")
.data(function(d) { return d.category; })
.enter().append("rect")
.attr("height", 0)
.attr("y", height);

Related

D3 position x axis label within rectangle and rotate 90 degrees

I am using D3 to create a basic bar graph
For my x-axis, I want to position each label above their respective bar. The text should also be rotated 90 degrees
To see the code that does this, start at line 51. https://codepen.io/Fallenstedt/pen/xdYooE
//This is where I attempt to create an x Axis label
//create a container to hold the text element
var textContainer = svg.append('g')
.selectAll('g')
.data(data).enter()
.append('g')
.attr('class', 'x-axis')
.attr('x', function(d, i) {
return i * (width/data.length)
})
.attr('y', function(d, i) {
return height - (d.value) + 15;
})
.attr("transform", function(d, i) {return "translate(" + (i * (width/data.length)) + ",330)";});
//now that a container is made, I can append a text element to it so I can rotate the text 90 degrees.
textContainer.append('text')
.text(function(d) {
return d.type
})
.attr('font-size', '34px')
.attr('fill', 'white')
.attr("text-anchor","end")
.attr("transform", function(d, i) {return "translate(40,0) rotate(-90,0,0)";});
The labels appear and they are rotated 90 degrees, however I cannot position them to be above their respective rectangle. How can I position each x-axis label to be directly above their rectangle? I feel that my approach to this is overly complicated.
You can create the rect and text elements inside the same container, e.g.
var rContainer = svg
.selectAll(".bar")
.data(data)
.enter()
.append("g");
//append rectangles for the bar chart
rContainer
.append("rect")
.attr("class", "bar")
.style("fill", function(d, i) { return color(i); })
.attr("x", function(d) { return x(d.type); })
.attr("y", 0)
.attr("width", x.bandwidth())
.attr("height", 0)
.transition()
.duration(500) //length of animation
.delay(function(d, i) { return i * 100; }) //delay must be less than duration
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); });
//append a text element to it so I can rotate the text 270 degrees.
rContainer
.append("text")
.text(function(d) { return d.type; })
.attr("width", x.bandwidth())
.attr("font-size", "34px")
.attr("fill", "white")
.attr("text-anchor", "start")
.attr("transform", function(d, i) {
// http://stackoverflow.com/questions/11252753/rotate-x-axis-text-in-d3
var yVal = y(d.value) - 6;
var xVal = x(d.type) + x.bandwidth() / 1.6;
return "translate(" + xVal + "," + yVal + ") rotate(270)";
});
You can check this working demo // starts in line 40

SVG: positioning RECT based on value

I'm wanting to recreate this graph
<div class="discrete" data-value="1,0,1,0,0,1,0,0,1,1,1,1,1,0,1,0,1,1,1,1"></div>
For each 1 I want to make a top half line, and 0 the opposite.
function discreteChart(self, dataset) {
var w = parseInt(d3.select(self).style("width")),
h = parseInt(d3.select(self).style("height")),
svg = d3.select(self)
.append("svg")
.attr("width", w)
.attr("height", h),
yScale = d3.scale
.linear()
.domain([0, d3.max(dataset)])
.range([0, h]);
svg.selectAll("rect")
.data(dataset)
.enter()
.append("rect")
.attr('fill', '#363636')
.attr('rx', '1')
.attr('ry', '1')
.attr("x", function(d, i) {
return i * 3;
})
.attr("y", function(d) {
return h - yScale(d) / 1;
})
.attr("width", 1) // width of bar
.attr("height", function(d) {
return yScale(d);
});
}
My problem is I'm not entirely sure how to do this. I have the rectangles made but I can't place them into position.
http://jsfiddle.net/everina/1eec20xe/
You can make line instead of rectangle as pointed by Anko.
Here is how you can make a line:
svg.selectAll("line")
.data(dataset)
.enter()
.append("line")
.attr('stroke', '#363636')
.attr('x1', function(d, i) {
return (i + 1) * 10;//xposition of line
})
.attr('x2', function(d, i) {
return (i + 1) * 10;
})
.attr('y1', function(d, i) {
return 10;//starting point of line
})
.attr('y2', function(d, i) {
if (d) {
return 0; //if 0 then line should be below
} else {
return 20;//if non 0 then line should be above
}
})
working example here
Hope this helps!
If you want to make the chart scaleable, so the positions are correct for any height:
svg.selectAll("rect")
.data(dataset)
.enter()
.append("rect")
.attr('fill', '#363636')
.attr('rx', '1')
.attr('ry', '1')
.attr("x", function(d, i) {
return i * 3;
})
.attr("y", function(d) {
return d ? h / 2 : 0;
})
.attr("width", 1) // width of bar
.attr("height", function(d) {
return h/2;
});

D3 bar chart using time scale rectangles not aligning to x axis

hello world> I have been battling this problem for a while now. Im trying to create a bar graph that will take an array of objects as data with time being a date object and value being a number.
My Scale looks like this
d3.time.scale.utc()
.domain(d3.extent(data, function (d) { return d.time; }))
.rangeRound([20, this.state.width - 60]).nice(data.length);
My rectangles are being drawn like this, using the same scale
const self = this,
xScale = this.state.xScale,
yScale = this.state.yScale,
barWidth = this.getBarWidth(data.length),
bars = chart.selectAll('rect')
.data(data);
// UPDATE
bars
.transition()
.duration(500)
.style('fill', color)
.attr('x', function(d) {
console.log(xScale(d.time)- (barWidth / 2));
return xScale(d.time) - (barWidth / 2);
})
.attr('width', barWidth)
.attr('y', function(d) { return yScale(d.value); })
.attr('height', function(d) { return self.state.height - yScale(d.value); });
// ENTER
bars
.enter()
.append('rect')
.style('fill', color)
.attr('class', 'bar')
.attr('x', function(d) {
console.log(xScale(d.time) - barWidth);
return xScale(d.time) - barWidth;
})
.attr('width', barWidth + (barWidth / data.length))
.attr('y', function(d) { return yScale(d.value); })
.attr('height', function(d) { return self.state.height - yScale(d.value); });
// EXIT
bars
.exit()
.transition()
.duration(500)
.style('fill', 'red')
.style('opacity', 0)
.remove();
I get the problem below where the ticks have some length and the axis another and the tick marks don't match the bars.
Please friends, help me find a solution to the below problem.
My bar problem

d3 update bar chart when data change

I am new in d3 and I want to update my bar chart when the data change. A part of my d3 function is
var control = svg.selectAll(".control")
.data(data)
.enter().append("g")
.attr("class", "g")
.attr("transform", function(d) { return "translate(" + x0(d.Control) + ",0)"; });
control.selectAll("rect")
.data(function(d) { return d.category; })
.enter().append("rect")
.attr("width", x1.rangeBand())
.attr("x", function(d) { return x1(d.name); })
.attr("height", 0)
.attr("y", height)
.on('mouseover', tip.show)
.on('mouseout', tip.hide)
.style("fill", function(d) { return color(d.name); });
control.selectAll("rect")
.transition()
.delay(function(d, i) { return i * 100; })
.duration(1000)
.attr("height", function(d, i) { if(columnNum == 2 && i == 2) return height - y1(d.value); else return height - y0(d.value); })
.attr("y", function(d, i) { if(columnNum == 2 && i == 2) return y1(d.value); else return y0(d.value); });
I can see that the figure reads the updated data because it shows the tooltip for the updated data but the bar doesn't reflects these changes (e.g. a bar with 100 updated to 50, I will have 2 tooltips but original bar still fill with 100?)
Any idea on how to update/add new/ delete bars? Thanks.

How to display data value on every layout of stacked barchart in d3

I have created a stacked bar chart in d3.
Here I want to display the value like this below example (This is Simple Bar chart)
http://bl.ocks.org/enjalot/1218567
But not outside the bar, inside the bar like below :
http://bl.ocks.org/diethardsteiner/3287802
This is my Stacked function which is working fine :
function barStack(d)
{
var l = d[0].length
while (l--) {
var posBase = 0,
negBase = 0;
d.forEach(function (d) {
d = d[l]
d.size = Math.abs(d.y)
if (d.y < 0) {
d.y0 = negBase
negBase -= d.size
} else {
d.y0 = posBase = posBase + d.size
}
})
}
d.extent = d3.extent(d3.merge(d3.merge(d.map(function (e) {
return e.map(function (f) {
return [f.y0, f.y0 - f.size]
})
}))))
return d
}
For stacked Bar
svg.selectAll(".series")
.data(data)
.enter()
.append("g")
.attr("class", "g")
.style("fill", function (d, i) {return color(i)})
.selectAll("rect")
.data(Object)
.enter()
.append("rect")
.attr("x", function (d, i) {return x(x.domain()[i])})
.attr("y", function (d) { return y(d.y0) })
.attr("height", function (d) { return y(0) - y(d.size) })
.attr("width", x.rangeBand())
;
This was also running fine.
My data is like that
var data = [{x:"abc", y1:"3", y2:"4", y3:"10"},
{x:"abc2", y1:"6", y2:"-2", y3:"-3" },
{x:"abc3", y1:"-3", y2:"-9", y3:"4"}
]
Now I want to show this value of y1, y2 and y3 in every stacked layout.
I have tried this below code, but this is not displaying the value over layout.
svg.selectAll("text")
.data(data)
.enter()
.append("text")
.attr("transform", "translate(50,0)")
.attr("text-anchor", "middle")
.attr("x", function(d, i) {return (i * (width / data.length)) + ((width / data.length - 50) / 2);})
.attr("y", function(d) {return y(0) - y(d.size) + 14;})
.attr("class", "yAxis")
.text(function(d) {return y(d.size);})
;
Please help me on this, where I need to change or what exact I need to put instead of this or might be above code was totally wrong.
Please let me know if any more input required by me. I have the total POC and I can share that too.
i have added all code in jsfiddle
http://jsfiddle.net/goldenbutter/HZqkm/
Here is a fiddle that does what you want.
var plots = svg.selectAll(".series").data(data)
.enter()
.append("g")
.classed("series",true)
.style("fill", function(d,i) {return color(i)})
plots.selectAll("rect").data(Object)
.enter().append("rect")
.attr("x",function(d,i) { return x(x.domain()[i])})
.attr("y",function(d) { return y(d.y0)})
.attr("height",function(d) { return y(0)-y(d.size)})
.attr("width",x.rangeBand());
plots.selectAll("text.lab").data(Object)
.enter().append("text")
.attr('fill','black')
.attr("text-anchor", "middle")
.attr("x", function(d, i) { return x(x.domain()[i]) + (x.rangeBand()/2)})
.attr("y", function(d) {return y(d.y0) + 20})
.text(function(d) {return (d.size).toFixed(2);});

Categories