Reputation: 4251
Normally chart data starts at the bottom of the Y-axis and left of the X-axis. However I have this bar chart that I'm trying to create in D3 that starts 30px above the bottom of the Y-axis and 30px to the right of the left of the X-axis (see mock-up design below). The 30px padding should be maintained on top and to the right as well.
I can't wrap my head around how this should be implemented because the axes lines should still be drawn all the way across but the ticks and the bar chart data should be padded 30px all the way around and the scale should be maintained.
Note: I've removed the rest of the ticks and tick values for clarity. X-axis ticks should be placed in the middle of each bar.
Upvotes: 3
Views: 3240
Reputation: 102218
For achieving what you want you'll have to change the settings of the scales. Since you have a bar chart, I'm assuming you have:
Also, because you didn't share any running code, I'll base my answer on this basic bar chart from d3noob.
The first step is setting your paddings:
const horPadding = 30;
const vertPadding = 30;
Now let's change the scales:
For setting the padding in the band scale, we'll use scale.paddingOuter
.
Because the value passed to that method is a multiple of scale.step()
(that is, if you pass 1
it equals to passing scale.step()
), we'll use that to calculate how much is 30px in padding. The math is simple:
scale.paddingOuter(horPadding / x.step());
Here the math is a bit more complicated. Basically, we'll calculate how much below zero we have to go to get exactly 30px (assuming that your lower domain is zero, which is a very basic rule in bar charts!).
That can be done with this as the first value of the domain, replacing 0
:
-(d3.max(data, function(d) {
return d.sales;
}) * vertPadding / height)
Here, sales
is the property used for the bars' height and height
is obviously the height used for the scale and the axis. Change them according to your needs.
Then, don't forget to use scale(0)
to set the base of the rectangles. In that d3noob code I'm sharing that would be:
return y(0) - y(d.sales);
And this is the result:
var csv = `salesperson,sales
Bob,33
Robin,12
Anne,41
Mark,16
Joe,59
Eve,38`;
const horPadding = 30;
const vertPadding = 30;
var margin = {
top: 20,
right: 20,
bottom: 30,
left: 40
},
width = 600 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
var x = d3.scaleBand()
.range([0, width])
.padding(0.1);
var y = d3.scaleLinear()
.range([height, 0]);
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 + ")");
const data = d3.csvParse(csv, d3.autoType);
x.domain(data.map(function(d) {
return d.salesperson;
}))
.paddingOuter(horPadding / x.step());
y.domain([-(d3.max(data, function(d) {
return d.sales;
}) * vertPadding / height), d3.max(data, function(d) {
return d.sales;
})])
svg.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) {
return x(d.salesperson);
})
.attr("width", x.bandwidth())
.attr("y", function(d) {
return y(d.sales);
})
.attr("height", function(d) {
return y(0) - y(d.sales);
});
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
svg.append("g")
.call(d3.axisLeft(y));
.bar {
fill: steelblue;
}
<script src="//d3js.org/d3.v4.min.js"></script>
Upvotes: 2