【问题标题】:stacked barchart with svg (no 3rd party library)带 svg 的堆叠条形图(无 3rd 方库)
【发布时间】:2021-12-30 23:55:24
【问题描述】:

我正在尝试使用 svg 和 html 创建一个堆叠的条形图,而不是使用任何 3rd 方库。不幸的是,没有一个在线文档显示如何使用纯 svg 创建堆叠条形图。

我已经创建了一个 codepen,并且我正在实现那个堆叠的条形图。任何人都可以让我知道还需要什么来制作这个堆叠的条形图。

https://codepen.io/a166617/pen/qBXvzQd

这是我目前拥有的代码

const ReleaseScopeCharts = () => {
    const data = [
        {
            name: 'Transit',
            passed: 2,
            skipped: 5,
            failed: 22,
        },
        {
            name: 'Access',
            passed: 7,
            skipped: 2,
            failed: 11,
        },
    ];
    const width = 500;
    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],
        }))
            .filter((bar) => bar.value),
    }));

    const rows = (entry) => entry.bars.map((bar, index) => {
        const height = (bar.value / entry.total) * 100;
        return (
            <g key={index}>
                <rect
                    width={50}
                    height={`${height}%`}
                    fill={bar.color}
                    x={index * 60} // multiply with the width (50) + 10 for space
                />
            </g>
        );
    });

    return (
        <div className="new-card">
            <div />
            {entries.map((entry) => (
                <>
                    {entry.name}
                    <svg viewBox={`0, 0, ${width}, ${500}`}
                        height={500}
                        width={width}
                        style={{ transform: `rotateX(180deg)` }}
                    >
                        {rows(entry)}
                    </svg>
                </>
            ))}
        </div>
    );
};

对于堆积条形图,我的意思是显示一个在另一个之上。

【问题讨论】:

    标签: javascript html css reactjs svg


    【解决方案1】:

    要堆叠条形图,您需要计算当前列和空间宽度。将 svg 包裹到 div,同时将 text 偏移到 div 并以 display:flex 居中。

    y 键添加到 bars,其中:

    • 起点 = 通过 = 0
    • 中间点 = 跳过 = 通过值
    • 终点 = 失败 = 通过值 + 跳过值
    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>

    【讨论】:

    • 嗨安东,你能确认这是否在你的最后。我在 codepen 中添加了您的代码,我仍然看到常规条形图而不是堆叠的 codepen.io/a166617/pen/qBXvzQd
    • @Anton- 我添加了一张我试图实现的堆积条形图的图像。
    • 感谢您的图片,它帮助我正确理解了您的需求。更新了 sn-p。
    • @Anton- 谢谢。你能在这个问题上提供帮助吗? stackoverflow.com/questions/70055642/…
    猜你喜欢
    • 2021-07-20
    • 1970-01-01
    • 1970-01-01
    • 2022-01-16
    • 2023-01-31
    • 2016-03-11
    • 1970-01-01
    • 2012-05-11
    相关资源
    最近更新 更多