D3.js 伪 3D 条形图
D3.js pseudo 3D bar chart
我开始研究 d3 并尝试创建具有 3D 效果的直方图。正确计算了所有面孔的位置,但它们只在浏览器的检查器中清晰可见。我的代码有什么问题? (参见方法“updateChart”)
以上,某些东西干扰了显示。但我想不通到底是什么
class BarChart extends React.Component {
//skip code...
updateChart() {
this.updateScales();
const { data, width, height, connectFauxDOM } = this.props;
const { xAxisLength, yAxisLength } = this.getAxisLength();
const faux = connectFauxDOM('div', 'chart');
const svg = d3
.select(faux)
.append('svg')
.attr('viewBox', `0 0 ${width} ${height}`)
.attr('preserveAspectRatio', 'xMinYMin');
const xAxis = d3.axisBottom().scale(this.xScale);
const yAxis = d3.axisLeft().scale(this.yScale);
svg
.append('g')
.attr('class', 'x-axis')
.attr(
'transform',
`translate(${this.margin},${height - this.margin})`,
)
.call(xAxis);
svg
.append('g')
.attr('class', 'y-axis')
.attr(
'transform',
`translate(${this.margin},${this.margin})`,
)
.call(yAxis);
svg
.selectAll('g.y-axis g.tick')
.append('line')
.classed('grid-line', true)
.attr('x1', 0)
.attr('y1', 0)
.attr('x2', xAxisLength)
.attr('y2', 0);
const g = svg.append('g').attr('class', 'body').attr(
'transform',
`translate(${this.margin}, 0 )`,
);
g.selectAll('svg.bar')
.data(data)
.enter()
.append('svg')
.attr('class', 'bar');
const angel = 45;
const rectWidth = () =>
Math.floor(xAxisLength / data.length) - this.padding;
const rectHeight = (value) => yAxisLength - this.yScale(value);
const rectX = (value) => this.xScale(value);
const rectY = (value) => this.yScale(value) + this.margin;
const bars = g
.selectAll('svg.bar')
.data(data)
.attr('x', (d) => rectX(d.label))
.attr('y', (d) => rectY(d.value));
bars
.data(data)
.append('rect')
.attr('class', 'forward-bar')
.attr('height', (d) => rectHeight(d.value))
.attr('width', () => rectWidth());
// side
bars
.data(data)
.append('rect')
.attr('class', 'side-bar')
.attr('width', rectWidth() / 2)
.attr('height', (d) => rectHeight(d.value))
.attr('transform', `translate (${rectWidth()}, 0) skewY(${-angel})`);
bars
.data(data)
.append('rect')
.attr('class', 'top-bar')
.attr('width', rectWidth())
.attr('height', rectWidth() / 2)
.attr(
'transform',
`translate (${rectWidth() / 2},${-rectWidth() / 2}) skewX(${-angel})`,
);
}
render() {
const { chart, classes } = this.props;
return <div className={classes.svg}>{chart}</div>;
}
}
我认为歪斜的<rect>
s不行,<path>
更适合你的任务。
这是一个简单的函数 add3DBar:
const add3DBar = (parent, xPos, yPos, width, height, depth) => {
const g = parent.append('g').attr('transform', `translate(${xPos}, ${yPos})`);
g.append('path').attr('d', `M 0,0 V ${-height} H ${width} V 0 H 0 Z`).style('fill', '#000080');
g.append('path').attr('d', `M 0,${-height} L ${depth},${-height-depth} H ${depth + width} L ${width},${-height} Z`).style('fill', '#0000FF');
g.append('path').attr('d', `M ${width},0 L ${width + depth},${-depth}, V ${-height-depth} L ${width} ${-height} Z`).style('fill', '#0000C0');
}
,其中
xPos 和 yPos - 条形图左下角的坐标
宽度、高度、深度 - 条的尺寸
在 fiddle 中查看其工作原理:
const add3DBar = (parent, xPos, yPos, width, height, depth) => {
const g = parent.append('g').attr('transform', `translate(${xPos}, ${yPos})`);
g.append('path').attr('d', `M 0,0 V ${-height} H ${width} V 0 H 0 Z`).style('fill', '#000080');
g.append('path').attr('d', `M 0,${-height} L ${depth},${-height-depth} H ${depth + width} L ${width},${-height} Z`).style('fill', '#0000FF');
g.append('path').attr('d', `M ${width},0 L ${width + depth},${-depth}, V ${-height-depth} L ${width} ${-height} Z`).style('fill', '#0000C0');
}
const svg = d3.select('svg');
add3DBar(svg, 30, 150, 30, 100, 10);
add3DBar(svg, 70, 150, 30, 70, 10);
add3DBar(svg, 110, 150, 30, 120, 10);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg/>
我开始研究 d3 并尝试创建具有 3D 效果的直方图。正确计算了所有面孔的位置,但它们只在浏览器的检查器中清晰可见。我的代码有什么问题? (参见方法“updateChart”)
以上,某些东西干扰了显示。但我想不通到底是什么
class BarChart extends React.Component {
//skip code...
updateChart() {
this.updateScales();
const { data, width, height, connectFauxDOM } = this.props;
const { xAxisLength, yAxisLength } = this.getAxisLength();
const faux = connectFauxDOM('div', 'chart');
const svg = d3
.select(faux)
.append('svg')
.attr('viewBox', `0 0 ${width} ${height}`)
.attr('preserveAspectRatio', 'xMinYMin');
const xAxis = d3.axisBottom().scale(this.xScale);
const yAxis = d3.axisLeft().scale(this.yScale);
svg
.append('g')
.attr('class', 'x-axis')
.attr(
'transform',
`translate(${this.margin},${height - this.margin})`,
)
.call(xAxis);
svg
.append('g')
.attr('class', 'y-axis')
.attr(
'transform',
`translate(${this.margin},${this.margin})`,
)
.call(yAxis);
svg
.selectAll('g.y-axis g.tick')
.append('line')
.classed('grid-line', true)
.attr('x1', 0)
.attr('y1', 0)
.attr('x2', xAxisLength)
.attr('y2', 0);
const g = svg.append('g').attr('class', 'body').attr(
'transform',
`translate(${this.margin}, 0 )`,
);
g.selectAll('svg.bar')
.data(data)
.enter()
.append('svg')
.attr('class', 'bar');
const angel = 45;
const rectWidth = () =>
Math.floor(xAxisLength / data.length) - this.padding;
const rectHeight = (value) => yAxisLength - this.yScale(value);
const rectX = (value) => this.xScale(value);
const rectY = (value) => this.yScale(value) + this.margin;
const bars = g
.selectAll('svg.bar')
.data(data)
.attr('x', (d) => rectX(d.label))
.attr('y', (d) => rectY(d.value));
bars
.data(data)
.append('rect')
.attr('class', 'forward-bar')
.attr('height', (d) => rectHeight(d.value))
.attr('width', () => rectWidth());
// side
bars
.data(data)
.append('rect')
.attr('class', 'side-bar')
.attr('width', rectWidth() / 2)
.attr('height', (d) => rectHeight(d.value))
.attr('transform', `translate (${rectWidth()}, 0) skewY(${-angel})`);
bars
.data(data)
.append('rect')
.attr('class', 'top-bar')
.attr('width', rectWidth())
.attr('height', rectWidth() / 2)
.attr(
'transform',
`translate (${rectWidth() / 2},${-rectWidth() / 2}) skewX(${-angel})`,
);
}
render() {
const { chart, classes } = this.props;
return <div className={classes.svg}>{chart}</div>;
}
}
我认为歪斜的<rect>
s不行,<path>
更适合你的任务。
这是一个简单的函数 add3DBar:
const add3DBar = (parent, xPos, yPos, width, height, depth) => {
const g = parent.append('g').attr('transform', `translate(${xPos}, ${yPos})`);
g.append('path').attr('d', `M 0,0 V ${-height} H ${width} V 0 H 0 Z`).style('fill', '#000080');
g.append('path').attr('d', `M 0,${-height} L ${depth},${-height-depth} H ${depth + width} L ${width},${-height} Z`).style('fill', '#0000FF');
g.append('path').attr('d', `M ${width},0 L ${width + depth},${-depth}, V ${-height-depth} L ${width} ${-height} Z`).style('fill', '#0000C0');
}
,其中
xPos 和 yPos - 条形图左下角的坐标
宽度、高度、深度 - 条的尺寸
在 fiddle 中查看其工作原理:
const add3DBar = (parent, xPos, yPos, width, height, depth) => {
const g = parent.append('g').attr('transform', `translate(${xPos}, ${yPos})`);
g.append('path').attr('d', `M 0,0 V ${-height} H ${width} V 0 H 0 Z`).style('fill', '#000080');
g.append('path').attr('d', `M 0,${-height} L ${depth},${-height-depth} H ${depth + width} L ${width},${-height} Z`).style('fill', '#0000FF');
g.append('path').attr('d', `M ${width},0 L ${width + depth},${-depth}, V ${-height-depth} L ${width} ${-height} Z`).style('fill', '#0000C0');
}
const svg = d3.select('svg');
add3DBar(svg, 30, 150, 30, 100, 10);
add3DBar(svg, 70, 150, 30, 70, 10);
add3DBar(svg, 110, 150, 30, 120, 10);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg/>