Reputation: 21
I am using d3 to make a bar graph, and I am running into an issue where the x values in a rect
element seem to behave differently than the width values, same applies to y and height values. The strange thing is that I am passing the same values to the each counterpart.
I am testing out this issue and getting strange results, shown below with an image. Note that the yScale is being inverted when setting the range. My expectation is that the rectangle's four corners should touch the four circles, but that is not the case. Any reason for this behavior?
Image:
My code:
const xMax = dataset.length-1;
const yMax = d3.max(dataset, (d)=> d[1]);
const xScale = d3.scaleLinear()
.domain([0, xMax])
.range([padding, width-padding]);
const yScale = d3.scaleLinear()
.domain([0, yMax])
.range([height-padding,padding]);
svg.append('circle')
.attr('cx', xScale(0))
.attr('cy', yScale(0))
.attr('r', 10);
svg.append('circle')
.attr('cx', xScale(xMax))
.attr('cy', yScale(0))
.attr('r', 10);
svg.append('circle')
.attr('cx', xScale(xMax))
.attr('cy', yScale(yMax))
.attr('r', 10);
svg.append('circle')
.attr('cx', xScale(0))
.attr('cy', yScale(yMax))
.attr('r', 10);
svg.append('rect')
.style('fill', 'none')
.style('stroke', 'black')
.style('stroke-width', 2)
.attr('x', xScale(0))
.attr('y', yScale(yMax))
.attr('width', xScale(xMax))
.attr('height', yScale(yMax));
Upvotes: 1
Views: 60
Reputation: 102218
The reason why the rectangle doesn't go to the four points (the four circles) in your SVG is this: the rectangle ends in a position which is its width plus its x
(initial) position. The same goes for the vertical scale.
Right now, you have this:
const svg = d3.select("svg");
const xMax = 280,
yMax = 130,
padding = 20,
width = 300,
height = 150;
const xScale = d3.scaleLinear()
.domain([0, xMax])
.range([padding, width - padding]);
const yScale = d3.scaleLinear()
.domain([0, yMax])
.range([height - padding, padding]);
svg.append('circle')
.attr('cx', xScale(0))
.attr('cy', yScale(0))
.attr('r', 10);
svg.append('circle')
.attr('cx', xScale(xMax))
.attr('cy', yScale(0))
.attr('r', 10);
svg.append('circle')
.attr('cx', xScale(xMax))
.attr('cy', yScale(yMax))
.attr('r', 10);
svg.append('circle')
.attr('cx', xScale(0))
.attr('cy', yScale(yMax))
.attr('r', 10);
svg.append('rect')
.style('fill', 'none')
.style('stroke', 'black')
.style('stroke-width', 2)
.attr('x', xScale(0))
.attr('y', yScale(yMax))
.attr('width', xScale(xMax))
.attr('height', yScale(yMax));
svg {
background-color: wheat;
}
<script src="https://d3js.org/d3.v5.min.js"></script>
<svg></svg>
So, why doesn't the rectangle end at the circle on the right-hand side, if that circle is at xScale(xMax)
? Because you have to subtract the rectangle's x
position from the width, in order to have this:
rectangle's x + rectangle's width = circle's centre
And, as previously stated, the same goes for the rectangle's height.
That being said, this is the working demo:
const svg = d3.select("svg");
const xMax = 280,
yMax = 130,
padding = 20,
width = 300,
height = 150;
const xScale = d3.scaleLinear()
.domain([0, xMax])
.range([padding, width - padding]);
const yScale = d3.scaleLinear()
.domain([0, yMax])
.range([height - padding, padding]);
svg.append('circle')
.attr('cx', xScale(0))
.attr('cy', yScale(0))
.attr('r', 10);
svg.append('circle')
.attr('cx', xScale(xMax))
.attr('cy', yScale(0))
.attr('r', 10);
svg.append('circle')
.attr('cx', xScale(xMax))
.attr('cy', yScale(yMax))
.attr('r', 10);
svg.append('circle')
.attr('cx', xScale(0))
.attr('cy', yScale(yMax))
.attr('r', 10);
svg.append('rect')
.style('fill', 'none')
.style('stroke', 'black')
.style('stroke-width', 2)
.attr('x', xScale(0))
.attr('y', yScale(yMax))
.attr('width', xScale(xMax) - xScale(0))
.attr('height', yScale(0) - yScale(yMax));
svg {
background-color: wheat;
}
<script src="https://d3js.org/d3.v5.min.js"></script>
<svg></svg>
Upvotes: 1