I have a codepen here - https://codepen.io/anon/pen/GybENz
I've created a simple stacked bar chart with a legend to filter the chart.
I'd like to animated the height of the bar from the bottom axis up.
Currently its animating from the left and down
let layersBar = layersBarArea.selectAll('.layer').data(stackedSeries)
.enter()
.append('g')
.attr('class', 'layer')
.style('fill', (d, i) => {
return colors[i];
});
layersBar.selectAll('rect')
.data((d) => {
return d
})
.enter()
.append('rect')
.attr('height', 100)
.transition()
.duration(400)
.attr('height', (d, i) => {
return y(d[0]) - y(d[1]);
})
.attr('y', 0)
.attr('y', (d) => {
return y(d[1]);
})
.attr('x', (d, i) => {
return x(d.data.date)
})
.attr('width', x.bandwidth());
}
Set the x position, the width, the y position (as the baseline) and the height (as zero) before the transition:
.attr('height', 0)
.attr("y", h - margin.bottom - margin.top)
.attr('x', (d, i) => {
return x(d.data.date)
})
.attr('width', x.bandwidth())
Here is the updated CodePen: https://codepen.io/anon/pen/ypdoMK?editors=0010
PS: It would be a good idea transitioning each rectangle individually. For instance, if the user clicked usedInf, you should transition only those rectangles... however, since you did this...
layersBarArea.selectAll('g.layer').remove();
... at the beginning of your drawChart function, which is a wrong approach, such suggestion will need a big refactor, out of the scope of this question/answer.
Related
My bar charts start growing in height slowly and then rapidly towards the end.
I want them to grow at the same pace
I have set ease to easeLinear but it doesn't appear to have any impact.
As an experiment, I used .ease(d3.easeBounce) but nothing changed. The bars started growing slowly and then rapidly towards the end.
Where am I going wrong?
function renderVerticalBars(data, measurements, metric, countryID) {
let selectDataForBarCharts = d3.select("svg")
.selectAll("rect")
.data(data, d => d[countryID])
selectDataForBarCharts
.enter()
.append("rect")
.attr('width', measurements.xScale.bandwidth())
.attr("height", 0)
.attr('y', d => measurements.yScale(0))
.merge(selectDataForBarCharts)
.attr("fill", d => setBarColor(d))
.attr("transform", `translate(0, ${measurements.margin.top})`)
.attr('width', measurements.xScale.bandwidth())
.attr('x', (d) => measurements.xScale(d[countryID]))
.on('mouseover', (event, barData) => { displayComparisons(event, barData, data, metric, countryID, measurements) })
.on('mouseout', (event) => { renderValuesInVerticalBars(data, metric, countryID, measurements) })
.transition()
.ease(d3.easeLinear)
.duration(4000)
.attr("height", d => measurements.innerHeight - measurements.yScale(d[metric]))
.attr("y", (d) => measurements.yScale(d[metric]))
}
I am having a stacked bar chart and i would like to animate the stacked bar chart. I am using d3js v5.
my project is available at https://stackblitz.com/edit/angular-2xgkwr
https://angular-2xgkwr.stackblitz.io
the animation i like is bars growing up as show here https://bl.ocks.org/guilhermesimoes/8913c15adf7dd2cab53a
really appreciate if you can help
I just updated the code and it starts to work
g.append('g')
.selectAll('g')
.data(d3.stack().keys(keys)(data))
.enter().append('g')
// tslint:disable-next-line:only-arrow-functions
.attr('class', d => z(d.key))
.selectAll('rect')
.data(d => d )
.enter().append('rect')
.attr('x', d =>
x(new Date(d.data.dateRange))
)
.attr("height", 0)
.attr('height', function(d) { return y(d[0]) - y(d[1]); })
.transition().duration(750).delay(calculateDelayFn)
.attr('y', function(d) {
return y(d[1]); }
)
I am working on a population pyramid that has an updating function.
http://bricbracs.com/hh/
As you can see the bars expand and contract in a horizontal line when you update it with new data. I want to modify the transition effect so that the bars enter and exit vertically like this:
http://vis.stanford.edu/jheer/d3/pyramid/shift.html
I have been following this tutorial and modifying the code but so far no luck.
https://strongriley.github.io/d3/tutorial/bar-2.html
Here is the code that first draws the bars on loading. (this is the male bar group, the female bar group is the same)
leftBarGroup.selectAll('.bar.left')
.data(data)
.enter()
.append('rect')
.attr('class', 'bar left')
.attr('y', function(d) { return yScale(d.group); })
.attr("width", 0)
.attr("opacity", 0)
.transition()
.duration(500)
.attr('width', function(d) { return xScale(d.male); })
.attr('height', yScale.rangeBand())
.attr("opacity", 1)
And here is the corresponding part of the code in the updating function which changes the bars.
var sel = leftBarGroup.selectAll('.bar.left')
.attr('class', 'bar left')
.data(data)
.data(data, function(d) { return d.male; })
.transition()
.attr('y',0)
.duration(500)
.attr('y', function(d) { return yScale(d.group); })
.attr('height', yScale.rangeBand())
.attr('width', function(d) { return xScale(d.male); })
.attr('height', yScale.rangeBand())
Thanks in advance.
Here's one way to reproduce the effect in your linked example. I offset the bars and then slide them back into place. You then handle the top and bottom bars slightly different.
Note, I only did the slide down on the male side of the pyramid, if you need help going the rest of the way just leave me a comment.
var sel = leftBarGroup.selectAll('.bar.left')
.attr('class', 'bar left')
.data(data)
.data(data, function(d) {
return d.male;
})
// offset y to slide down
.attr('y', function(d){
var self = d3.select(this);
return +self.attr('y') - yScale.rangeBand();
})
.transition()
.duration(500)
// slide it back into place
.attr('y', function(d) {
return yScale(d.group);
})
// and set new width
.attr('width', function(d) {
return xScale(d.male);
});
// for the very top bar
// not only slide it but "fade it in"
leftBarGroup.select(':last-child')
.style('opacity', 0)
.transition()
.duration(500)
.style('opacity', 1)
.attr('y', function(d) {
return yScale(d.group);
});
// append a fake bar on the bottom
// to slide and fade out
leftBarGroup.append('rect')
.attr('y', function(d) {
return yScale('0-4');
})
.attr('height', yScale.rangeBand())
.attr('width', function(){
return leftBarGroup.select(':first-child').attr('width');
})
.style('fill', 'steelblue')
.style('opacity', 0.6)
.transition()
.duration(500)
.attr('y', function(d) {
return yScale('0-4') + yScale.rangeBand();
})
.style('opacity', 0)
.remove();
EDITS
Going up is just a matter of reversing the logic:
var sel = leftBarGroup.selectAll('.bar.left')
.attr('class', 'bar left')
.data(data)
.data(data, function(d) {
return d.male;
})
// offset y to slide up
.attr('y', function(d){
var self = d3.select(this);
return +self.attr('y') + yScale.rangeBand()
})
.transition()
.duration(500)
// slide it back into place
.attr('y', function(d) {
return yScale(d.group);
})
// and set new width
.attr('width', function(d) {
return xScale(d.male);
});
// for the very bottom bar
// not only slide it but "fade it in"
leftBarGroup.select(':first-child')
.style('opacity', 0)
.transition()
.duration(500)
.style('opacity', 1)
.attr('y', function(d) {
return yScale(d.group);
});
// append a fake bar on the top
// to slide and fade out
var w = leftBarGroup.select(':last-child').attr('width');
leftBarGroup.append('rect')
.attr('class','fake')
.attr('y', function(d) {
return yScale('90+');
})
.attr('height', yScale.rangeBand())
.attr('width', w)
.style('fill', 'steelblue')
.style('opacity', 0.6)
.transition()
.duration(500)
.attr('y', function(d) {
return yScale('90+') - yScale.rangeBand();
})
.style('opacity', 0)
.remove();
Updated code sample here.
I'm trying to add some space/padding for a nvd3 multi bar chart. "groupSpacing" is not what I need, since it only adds space between groups. I'll need space between each bar inside group. I found one link in github support. Can you post any solution or tweak?
I also found a d3 example of grouped bar chart. Any help in this example also very helpful to me.
Thanks.
I have draw a d3 group barchart:
fiddle
You can adjust the groupSpacing by change the code on line 56:
var groupSpacing = 6;
Technically i just achieve it by change the width of each rects' width:
var barsEnter = bars.enter().append('rect')
.attr('class', 'stm-d3-bar')
.attr('x', function(d,i,j) {
return (j * x1.rangeBand() );
})
.attr('y', function(d) { return y(d.y); })
.attr('height', function(d) { return height - y(d.y); })
.attr('width', x0.rangeBand() / barData.length - groupSpacing )
.attr('transform', function(d,i) {
return 'translate(' + x0(d.x) + ',0)';
})
.style("fill", function(d, i, j) {
return color(data[j].key);
});
Hope it helps you understand how you can achieve it in d3.
I minus the number of group spacing from the "width" attribute also. I found that the x-axis label looks a little off after I did that so I add the (group spacing / 2) to the "x" attribute. Here is the example of my code.
var groupSpacing = 15;
var rect = groups.selectAll("rect")
.data(function (d) { return d; })
.enter()
.append("rect")
.attr("x", function (d) { return x(d.x) + (groupSpacing / 2) ; })
.attr("y", function (d) { return y(d.y0 + d.y); })
.attr("height", function (d) { return y(d.y0) - y(d.y0 + d.y); })
.attr("width", x.rangeBand() - groupSpacing)
So that I can transition the bars in a bar chart smoothly I need to set the height before I call transition().
When the chart first renders the bars animate up from the bottom of the chart as required:
chart.svg.selectAll('.bar')
.attr('y', chart.options.height)
.attr('x', function (d) {
return chart.xScale(d.title);
})
.attr('width', chart.xScale.rangeBand())
.attr('height', function () {
return 0;
})
.transition()
.attr('y', function (d) {
return chart.yScale(d.score);
})
.attr('height', function (d) {
return chart.options.height - chart.yScale(d.score);
});
However, when I change the data I don't want to set the height back to 0. Instead I need to set the height to the current height of the rectangle. How can I access this from the attr function?
.attr('height', function () {
return 0; // how do I get the current height
})
When I log this I have access to the DOM element but not sure where to go from there. I tried d3.select(this).attr('height') but it always returns null.
As #LarsKotthoff is hinting at in his comment, just break apart your initial draw from your update:
// intial draw of bars
node
.enter()
.append("rect")
.attr("class", "myBars")
.style("fill", "steelblue")
.attr('y', config.height)
.attr('x', function(d, i) {
return xScale(i);
})
.attr('width', xScale.rangeBand())
.attr('height', function() {
return 0;
});
Then fire the update to transition the bars from their current position:
function update() {
node = svg
.selectAll(".myBars")
.data(data);
node
.transition()
.attr('y', function(d) {
return yScale(d);
})
.attr("height", function(d) {
return config.height - yScale(d);
});
}
Here's the most minimal example I could code up.