Reputation: 47
I started looking into d3 and tried to create a histogram with 3D effect. Correctly calculated the position of all faces, but they are visible only cleanly and only in the browser's inspector. What's wrong with my code? (See method "updateChart")
Above, something interferes with the display. But I can't figure out what exactly
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>;
}
}
Upvotes: 1
Views: 523
Reputation: 7210
I think skewed <rect>
s will not work, <path>
is much more suitable for your task.
Here is a simple function 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');
}
, where
xPos and yPos - coordinates of the bottom-left corner of the bar
width, height, depth - dimensions of the bar
See how it works in the 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/>
Upvotes: 1