javascript - D3.js V5 - 为什么我的条形图轴没有缩放?
问题描述
我正在使用以下数据在 D3 中制作条形图:[-76, -55, -51, -44, -35, 27]
Y 轴应该从 -100 到 100。
这是代码:
var data = [-76, -55, -51, -44, -35, 27];
var leftMargin = 50; // Space to the left of first bar; accomodates y-axis labels
var rightMargin = 10; // Space to the right of last bar
var margin = {left: leftMargin, right: rightMargin, top: 10, bottom: 10};
var barWidth = 30; // Width of the bars
var chartHeight = height; // Height of chart, from x-axis (ie. y=0)
var chartWidth = margin.left + data.length * barWidth + margin.right;
/* This scale produces negative output for negatve input */
var yScale = d3.scaleLinear()
.domain([0, d3.max(data)])
.range([0, chartHeight]);
/*
* We need a different scale for drawing the y-axis. It needs
* a reversed range, and a larger domain to accomodate negaive values.
*/
var yAxisScale = d3.scaleLinear()
.domain([-100, 100])
.range([chartHeight - yScale(d3.min(data)), 0 ]);
svg2
.attr('height', chartHeight + 100)
.attr('width', chartWidth)
.style('border', '1px solid');
svg2
.selectAll("rect")
.data(data)
.enter()
.append("rect")
.attr("x", function(d, i) { return margin.left + i * barWidth; })
.attr("y", function(d, i) { return chartHeight - Math.max(0, yScale(d));})
.attr("height", function(d) { return Math.abs(yScale(d)); })
.attr("width", barWidth)
.style("fill", "grey")
.style("stroke", "black")
.style("stroke-width", "1px")
.style("opacity", function(d, i) { return 1 /*- (i * (1/data.length)); */});
var yAxis = d3.axisLeft(yAxisScale);
svg2.append('g')
.attr('transform', function(d) {
return 'translate(' + margin.left + ', 0)';
})
.call(yAxis);
这是输出的图表——你可以看到比例关闭并且图像没有缩放到 SVG 容器——负条超出了 svg 的底部。
有人可以帮我正确格式化这个条形图吗?
解决方案
/* * 我们需要不同的比例来绘制 y 轴。它需要 * 一个反向范围和一个更大的域来容纳负值。*/
这不是必需的,并且会产生更多的复杂性。一个刻度就足够了,并且始终将您的数据与您的轴相匹配。一旦为绘制的轴和数据创建不同的比例,您就会发现两者之间存在更大的冲突可能性。
相反,让我们在这里创建一个比例yScale
:
var yScale = d3.scaleLinear()
.domain([-100,100]) // min & max of input values
.range([chartHeight,0]) // mapped to the bottom and top of the plot area
对于 0 以下的值,负条,条的顶部将位于yScale(0)
。矩形的底部将位于yScale(value)
。两者之差,即高度,将等于yScale(value) - yScale(0)
。
对于大于 0 的值,条形的底部将位于yScale(0)
,条形的顶部将位于yScale(value)
。要找到我们使用的高度yScale(0)-yScale(value)
。
由于高度只是两者之间的绝对差yScale(0)
,yScale(value)
我们可以用它Math.abs(yScale(0)-yScale(value))
来找到所有矩形的高度。
考虑到上述情况,我已经对您的示例代码进行了改造:
var data = [-76, -55, -51, -44, -35, 27];
var height = 250;
var width = 500;
var margin = {left: 50, right: 10, top: 20, bottom: 20};
var svg = d3.select("body").append("svg")
.attr('height', height)
.attr('width', width)
.style('border', '1px solid')
.append("g")
// apply the margins:
.attr("transform","translate("+[margin.left+","+margin.top]+")");
var barWidth = 30; // Width of the bars
// plot area is height - vertical margins.
var chartHeight = height-margin.top-margin.left;
// set the scale:
var yScale = d3.scaleLinear()
.domain([-100, 100])
.range([chartHeight, 0]);
// draw some rectangles:
svg
.selectAll("rect")
.data(data)
.enter()
.append("rect")
.attr("x", function(d, i) { return i * barWidth; })
.attr("y", function(d,i) {
if(d < 0) {
return yScale(0); // if the value is under zero, the top of the bar is at yScale(0);
}
else {
return yScale(d); // otherwise the rectangle' top is above yScale(0) at yScale(d);
}
})
.attr("height", function(d) {
// the height of the rectangle is the difference between the scale value and yScale(0);
return Math.abs(yScale(0) - yScale(d));
})
.attr("width", barWidth)
.style("fill", "grey")
.style("stroke", "black")
.style("stroke-width", "1px")
var yAxis = d3.axisLeft(yScale);
svg.append('g')
.call(yAxis);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
此外,我们可以进一步简化一点。yScale(value)
和yScale(0)
(接近 SVG 顶部的值)的最小值将是矩形的顶部,无论矩形表示的值如何。
var data = [-76, -55, -51, -44, -35, 27];
var height = 250;
var width = 500;
var margin = {left: 50, right: 10, top: 20, bottom: 20};
var svg = d3.select("body").append("svg")
.attr('height', height)
.attr('width', width)
.style('border', '1px solid')
.append("g")
// apply the margins:
.attr("transform","translate("+[margin.left+","+margin.top]+")");
var barWidth = 30; // Width of the bars
// plot area is height - vertical margins.
var chartHeight = height-margin.top-margin.left;
// set the scale:
var yScale = d3.scaleLinear()
.domain([-100, 100])
.range([chartHeight, 0]);
// draw some rectangles:
svg
.selectAll("rect")
.data(data)
.enter()
.append("rect")
.attr("x", function(d, i) { return i * barWidth; })
.attr("y", function(d,i) {
return Math.min(yScale(0),yScale(d))
})
.attr("height", function(d) {
// the height of the rectangle is the difference between the scale value and yScale(0);
return Math.abs(yScale(0) - yScale(d));
})
.attr("width", barWidth)
.style("fill", "grey")
.style("stroke", "black")
.style("stroke-width", "1px")
var yAxis = d3.axisLeft(yScale);
svg.append('g')
.call(yAxis);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
通过使用两者中的最小值,它给了我们更多的自由:我们可以通过只改变尺度的域来翻转图形(如果出于某种原因我们想将负值表示为正值......)
推荐阅读
- python - 我不明白如何让 displayGPA() 干净地处理 ZeroDivisionError
- excel - 将市场数据接收到 Excel 中
- android-studio - 更改 Android Studio 预览窗口的默认缩放
- apple-watch - 在 watchOS 中请求位置及其对电池的影响
- django - Djagno:如何将电子邮件作为字典中的键传递给模板
- c# - 返回空字符串而不是 Null
- javascript - Ember 3.2.2 未将请求路由到 .NET Core 2.1 JSON Web API
- r - 如何在基地R中绘制带有南中国海的中国地图
- mysql - 每天列出每个类别中最畅销的商品,包括总销量和收入
- angular - Angular DataTables:$(...).DataTable 不是函数