javascript - 简单的 D3.js 条形图:组“g”位置和尺寸搞砸了
问题描述
我是 D3.js 的新手,试图制作一个简单的条形图。这是我的 POC:
<!DOCTYPE html>
<html>
<head></head>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.9.1/d3.js"></script>
<script type="text/javascript">
dataSet = [
{ 'year': 1851, 'data': -5.0 },
{ 'year': 1861, 'data': -4.5 },
{ 'year': 1871, 'data': -3.2 },
{ 'year': 1881, 'data': -2.8 },
{ 'year': 1891, 'data': -1.7 },
{ 'year': 1901, 'data': -1.0 },
{ 'year': 1911, 'data': 1.2 },
{ 'year': 1921, 'data': 2.5 },
{ 'year': 1931, 'data': 3.0 },
{ 'year': 1941, 'data': 3.9 },
{ 'year': 1951, 'data': 4.7 },
];
console.log(dataSet);
const widthSvg = 1024;
const heightSvg = 768;
const svg = d3.select('body')
.append('svg')
.attr('width', widthSvg)
.attr('height', heightSvg);
const outerAxisPadding = 70;
const yAxisWidth = 20;
const xAxisHeight = 20;
const margins = {
left: outerAxisPadding + yAxisWidth,
top: outerAxisPadding,
bottom: outerAxisPadding + xAxisHeight,
right: outerAxisPadding
};
const graphArea = {
left: margins.left,
top: margins.top,
right: widthSvg - margins.right,
bottom: heightSvg - margins.bottom,
width: 0,
height: 0
};
graphArea.width = graphArea.right - graphArea.left;
graphArea.height = graphArea.bottom - graphArea.top;
const dataGroup = svg.append('g')
.attr('transform', 'translate(' + graphArea.left + ',' + graphArea.top + ')')
.attr('width', graphArea.width - 10)
.attr('height', graphArea.height - 10);
const xScale = d3.scaleLinear()
.domain([
d3.min(
dataSet.map(d => d.year)
) - 15,
d3.max(
dataSet.map(d => d.year)
) + 15
])
.range([0, graphArea.width]);
const yScaleForAxis = d3.scaleLinear()
.domain([
d3.min(dataSet.map(d => d.data)),
d3.max(dataSet.map(d => d.data))
])
.nice()
.range([graphArea.height, 0]);
const yScaleForValues = d3.scaleLinear()
.domain([
d3.min(dataSet.map(d => Math.abs(d.data))),
d3.max(dataSet.map(d => Math.abs(d.data)))
])
.nice()
.range([0, graphArea.height / 2]);
// for debug:
dataSet.forEach(d => console.log(d.year + ' ' + d.data + ' ' + yScaleForValues(d.data)));
const xAxis = d3.axisBottom(xScale);
const yAxis = d3.axisLeft(yScaleForAxis);
// add yAxis nodes
svg.append('g')
.attr('transform', 'translate(' + outerAxisPadding + ',' + graphArea.top + ')')
.call(yAxis);
// add xAxis nodes
svg.append('g')
.attr('transform', 'translate(' + graphArea.left + ',' + (heightSvg - outerAxisPadding) + ')')
.call(xAxis);
dataGroup.selectAll('rect')
.data(dataSet)
.enter()
.append('rect')
.text((d, i) => d.year + ' ' + d.data) // just for information / debugging
.attr('fill', 'blue')
.attr('x', (d, i) => xScale(d.year))
.attr('y', (d, i) => {
if (d.data > 0) {
return graphArea.height - yScaleForValues(0) - yScaleForValues(d.data);
} else {
return graphArea.height - yScaleForValues(0);
}
})
.attr('width', '15')
.attr('height', (d, i) => yScaleForValues(Math.abs(d.data)));
</script>
</body>
</html>
我不知道它是否结构良好,或良好的做法。这里的目标是将dataSet数组注入到rects中,years字段是x轴,data字段是y轴。
但是,如您所见,问题在于应该包含矩形的dataGroup的位置和尺寸错误。
谢谢
解决方案
终于让它工作了,完全不同的版本:
<!DOCTYPE html>
<html>
<head></head>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.9.1/d3.js"></script>
<script type="text/javascript">
dataSet = [
{ 'year': 1831, 'data': -6.9 },
{ 'year': 1841, 'data': -5.7 },
{ 'year': 1851, 'data': -5.0 },
{ 'year': 1861, 'data': -4.5 },
{ 'year': 1871, 'data': -3.2 },
{ 'year': 1881, 'data': -2.8 },
{ 'year': 1891, 'data': -1.7 },
{ 'year': 1901, 'data': -1.0 },
{ 'year': 1911, 'data': 1.2 },
{ 'year': 1921, 'data': 2.5 },
{ 'year': 1931, 'data': 3.0 },
{ 'year': 1941, 'data': 3.9 },
{ 'year': 1951, 'data': 4.7 },
];
console.log(dataSet);
const widthSvg = 1024;
const heightSvg = 768;
const svg = d3.select('body')
.append('svg')
.attr('width', widthSvg)
.attr('height', heightSvg);
const outerAxisPadding = 70;
const yAxisWidth = 20;
const xAxisHeight = 20;
const margins = {
left: outerAxisPadding + yAxisWidth,
top: outerAxisPadding,
bottom: outerAxisPadding + xAxisHeight,
right: outerAxisPadding
};
const graphArea = {
left: margins.left,
top: margins.top,
right: widthSvg - margins.right,
bottom: heightSvg - margins.bottom,
width: 0,
height: 0
};
graphArea.width = graphArea.right - graphArea.left;
graphArea.height = graphArea.bottom - graphArea.top;
const dataGroup = svg.append('g')
.attr('transform', 'translate(' + graphArea.left + ',' + graphArea.top + ')')
.attr('width', graphArea.width - 10)
.attr('height', graphArea.height - 10);
const xScale = d3.scaleLinear()
.domain([
d3.min(
dataSet.map(d => d.year)
) - 15,
d3.max(
dataSet.map(d => d.year)
) + 15
])
.range([0, graphArea.width]);
const yScaleForAxis = d3.scaleLinear()
.domain([
d3.min(dataSet.map(d => d.data)),
d3.max(dataSet.map(d => d.data))
])
.nice()
.range([graphArea.height, 0]);
const yScaleForValues = d3.scaleLinear()
.domain([
d3.min(dataSet.map(d => d.data)),
d3.max(dataSet.map(d => d.data))
])
.nice()
.range([0, graphArea.height]);
// for debug:
dataSet.forEach(d => console.log(d.year + ' ' + d.data + ' ' + yScaleForValues(d.data)));
const xAxis = d3.axisBottom(xScale);
const yAxis = d3.axisLeft(yScaleForAxis);
// add yAxis nodes
svg.append('g')
.attr('transform', 'translate(' + outerAxisPadding + ',' + graphArea.top + ')')
.call(yAxis);
// add gridlines
svg.append('g')
.attr('transform', 'translate(' + margins.left + ',' + graphArea.top + ')')
.call(yAxis.tickFormat(data => '').tickSize(-graphArea.width));
// add xAxis nodes
svg.append('g')
.attr('transform', 'translate(' + graphArea.left + ',' + (heightSvg - outerAxisPadding) + ')')
.call(xAxis);
dataGroup.selectAll('rect')
.data(dataSet)
.enter()
.append('rect')
.text((d, i) => d.year + ' ' + d.data) // just for information / debugging
.attr('fill', 'blue')
.attr('x', (d, i) => xScale(d.year))
.attr('y', (d, i) => {
if (d.data > 0) {
return graphArea.height - yScaleForValues(0) - (yScaleForValues(d.data) - yScaleForValues(0)); // can shorten maths :p
} else {
return graphArea.height - yScaleForValues(0);
}
})
.attr('width', '15')
.attr('height', (d, i) => {
if (d.data > 0) {
return yScaleForValues(d.data) - yScaleForValues(0);
} else {
return yScaleForValues(0) - yScaleForValues(d.data);
}
});
</script>
</body>
</html><!DOCTYPE html>
<html>
<head></head>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.9.1/d3.js"></script>
<script type="text/javascript">
dataSet = [
{ 'year': 1831, 'data': -6.9 },
{ 'year': 1841, 'data': -5.7 },
{ 'year': 1851, 'data': -5.0 },
{ 'year': 1861, 'data': -4.5 },
{ 'year': 1871, 'data': -3.2 },
{ 'year': 1881, 'data': -2.8 },
{ 'year': 1891, 'data': -1.7 },
{ 'year': 1901, 'data': -1.0 },
{ 'year': 1911, 'data': 1.2 },
{ 'year': 1921, 'data': 2.5 },
{ 'year': 1931, 'data': 3.0 },
{ 'year': 1941, 'data': 3.9 },
{ 'year': 1951, 'data': 4.7 },
];
console.log(dataSet);
const widthSvg = 1024;
const heightSvg = 768;
const svg = d3.select('body')
.append('svg')
.attr('width', widthSvg)
.attr('height', heightSvg);
const outerAxisPadding = 70;
const yAxisWidth = 20;
const xAxisHeight = 20;
const margins = {
left: outerAxisPadding + yAxisWidth,
top: outerAxisPadding,
bottom: outerAxisPadding + xAxisHeight,
right: outerAxisPadding
};
const graphArea = {
left: margins.left,
top: margins.top,
right: widthSvg - margins.right,
bottom: heightSvg - margins.bottom,
width: 0,
height: 0
};
graphArea.width = graphArea.right - graphArea.left;
graphArea.height = graphArea.bottom - graphArea.top;
const dataGroup = svg.append('g')
.attr('transform', 'translate(' + graphArea.left + ',' + graphArea.top + ')')
.attr('width', graphArea.width - 10)
.attr('height', graphArea.height - 10);
const xScale = d3.scaleLinear()
.domain([
d3.min(
dataSet.map(d => d.year)
) - 15,
d3.max(
dataSet.map(d => d.year)
) + 15
])
.range([0, graphArea.width]);
const yScaleForAxis = d3.scaleLinear()
.domain([
d3.min(dataSet.map(d => d.data)),
d3.max(dataSet.map(d => d.data))
])
.nice()
.range([graphArea.height, 0]);
const yScaleForValues = d3.scaleLinear()
.domain([
d3.min(dataSet.map(d => d.data)),
d3.max(dataSet.map(d => d.data))
])
.nice()
.range([0, graphArea.height]);
// for debug:
dataSet.forEach(d => console.log(d.year + ' ' + d.data + ' ' + yScaleForValues(d.data)));
const xAxis = d3.axisBottom(xScale);
const yAxis = d3.axisLeft(yScaleForAxis);
// add yAxis nodes
svg.append('g')
.attr('transform', 'translate(' + outerAxisPadding + ',' + graphArea.top + ')')
.call(yAxis);
// add gridlines
svg.append('g')
.attr('transform', 'translate(' + margins.left + ',' + graphArea.top + ')')
.call(yAxis.tickFormat(data => '').tickSize(-graphArea.width));
// add xAxis nodes
svg.append('g')
.attr('transform', 'translate(' + graphArea.left + ',' + (heightSvg - outerAxisPadding) + ')')
.call(xAxis);
dataGroup.selectAll('rect')
.data(dataSet)
.enter()
.append('rect')
.text((d, i) => d.year + ' ' + d.data) // just for information / debugging
.attr('fill', 'blue')
.attr('x', (d, i) => xScale(d.year))
.attr('y', (d, i) => {
if (d.data > 0) {
return graphArea.height - yScaleForValues(0) - (yScaleForValues(d.data) - yScaleForValues(0)); // can shorten maths :p
} else {
return graphArea.height - yScaleForValues(0);
}
})
.attr('width', '15')
.attr('height', (d, i) => {
if (d.data > 0) {
return yScaleForValues(d.data) - yScaleForValues(0);
} else {
return yScaleForValues(0) - yScaleForValues(d.data);
}
});
</script>
</body>
</html>
我仍然对任何评论/批评感兴趣。
推荐阅读
- sql - 为什么没有找到使用此 SQL 代码的特定组的平均值?
- python - 将视图添加到 Django
- java - 如何将gif拆分为png文件?
- python - ModuleNotFoundError 但已安装模块
- java - EditText 无法获取数据,给出空字符串
- android - 您的应用容易受到新 google play 控制台中未找到的 Intent 重定向警报选项卡的影响
- sqlalchemy - AttributeError:“连接”对象没有属性“is_connected”
- java - 如何在不删除子实体的情况下删除父实体?
- c# - 用鼠标悬停时如何更改WPF中的背景前景和边框颜色
- flutter - 滑动动画结束后如何只渲染pageView页面