Я новичок в D3.js, пытаюсь сделать простую гистограмму. Вот мой ПОК:
<!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.info(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.info(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>Я не знаю, хорошо ли это структурировано или это хорошая практика. Цель здесь состоит в том, чтобы внедрить массив набор данных в прямоугольники, при этом поле years является осью x, а поле данных — осью y.
Однако, как видите, проблема в том, что группа данных, который должен содержать прямоугольники, имеет неправильное положение и размеры.
Спасибо



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Наконец-то заработало, совсем другая версия:
<!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.info(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.info(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.info(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.info(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>Мне по-прежнему интересны любые комментарии/критика.