Home > Net >  stacked barchart with svg (no 3rd party library)
stacked barchart with svg (no 3rd party library)

Time:11-21

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.

enter image description here

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>

  • Related