我是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>
我不知道它是结构良好的还是很好的实践。这里的目的是将数据集注入数组中的矩形,其中多年字段是x轴,数据字段为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>
我仍然对任何评论/批评感兴趣。