I am trying to create a stacked barchart with svg and html and not using any 3rd party library. Unfortunately, there is not a single document online which shows how to create a stacked bar chart using plain svg.
I have created a codepen and i am midway to achieving that stacked barchart. Can anyone please let me know what else is needed to make this a stacked barchart.
CodePudding user response:
To stack barchart, you need to calculate the current columns and space widths. Wrap the svg to div, also offset the text in to div and centered with display:flex
.
Add the y key to the bars, where:
- start point = passed = 0
- middle point = skipped = passed value
- end point = failed = passed value skipped value
y: key === 'passed' ? 0 : key === 'skipped' ? d['passed'] : d['skipped'] d['passed'],
// Basic style
const newCardStyle = {
display: 'flex',
};
const contentStyle = {
display: 'flex',
flexFlow: 'column',
alignItems: 'center',
};
// multiply 50 (width) * 3 (columns) 10 (space width) * 2 ( space between columns)
const width = 50 * 3 10 * 3;
function App() {
const data = [
{
name: 'Transit',
passed: 2,
skipped: 5,
failed: 22,
},
{
name: 'Access',
passed: 7,
skipped: 2,
failed: 11,
},
];
// Basic style
const newCardStyle = {
display: 'flex',
};
const contentStyle = {
display: 'flex',
flexFlow: 'column',
alignItems: 'center',
};
// multiply 50 (width) * 3 (columns) 10 (space width) * 2 ( space between columns)
const width = 50 * 3 10 * 3;
const colors = ['#30D158', '#005EA7', '#FF453A'];
const entries = data.map(d => ({
name: d.name,
total: ['passed', 'skipped', 'failed'].reduce(
(acc, key) => acc d[key],
0
),
bars: ['passed', 'skipped', 'failed']
.map((key, i) => ({
value: d[key],
color: colors[i],
y:
key === 'passed'
? 0
: key === 'skipped'
? d['passed']
: d['skipped'] d['passed'],
}))
.filter(bar => bar.value),
}));
const rows = entry => {
return entry.bars.map((bar, index) => {
const height = (bar.value / entry.total) * 100;
const y = (bar.y / entry.total) * 100;
return (
<g key={Math.random()}>
<rect
width={50}
height={`${height}%`}
fill={bar.color}
x={60} // multiply with the width (50) 10 for space
y={`${y}%`}
/>
</g>
);
});
};
return (
<div className="new-card" style={newCardStyle}>
{entries.map(entry => (
<div style={contentStyle} key={Math.random()}>
<svg
viewBox={`0, 0, ${width}, ${500}`}
height={500}
width={width}
style={{ transform: `rotateX(180deg)` }}
>
{rows(entry)}
</svg>
{entry.name}
</div>
))}
</div>
);
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
<script src="https://unpkg.com/react@17/umd/react.production.min.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js" crossorigin></script>
<div id="root"></div>
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>