【问题标题】:React with Svg vertical stacked barchart (no third party library)与 Svg 垂直堆叠条形图反应(无第三方库)
【发布时间】:2021-07-20 10:09:06
【问题描述】:

我有以下代码。我将 React 和 svg 用于条形图。我没有使用任何第三方库来制作图表。使用下面的这段代码,我可以获得条形图。但问题是我的条形图水平显示,我想垂直显示。我无法弄清楚如何让同一段代码用于垂直条形图。

我在网上看到一个视频https://egghead.io/lessons/javascript-build-a-bar-chart-with-svg-from-scratch-with-react 这家伙能够垂直实现条形图。我不确定我在显示它时哪里出错了。我所做或尝试的任何更改,它总是显示水平条形图。


export const ReleaseScopeCharts = () => {
const data = [
    {
        "name": "Transit",
        "passed": 2,
        "skipped": 5,
        "failed": 22,
    },
    {
        "name": "Access",
        "passed": 7,
        "skipped": 2,
        "failed": 11,
    }
]
const fontSize=14
const width=1000
const rowHeight=40
const colors = ["#30D158", "#005EA7", "#FF453A"];

const entries = data.map((d) => ({
    name: d.name,
    total: d.total,
    bars: ["passed", "skipped", "failed"].map((key, i) => ({
        value: d[key],
        portion: d[key] / 29,
        color: colors[i]
    }))
        .filter((bar) => bar.value)
}))
const heightPerRow = rowHeight;
const canvasHeight = entries.length * heightPerRow;
const canvasWidth = width;
const labelWidth = canvasWidth / 4;
const plotWidth = canvasWidth - labelWidth;
const verticalPadding = heightPerRow / 2;
const barHeight = heightPerRow - verticalPadding;
const horizontalPadding = 0;
const rows = entries.map((entry, i) => {
    const widths = entry.bars.map((bar) => plotWidth * bar.portion)
    const offsets = entry.bars.map((bar, i, array) => {
        const previous = array.slice(0, i);
        const offset = previous.map((bar) => bar.portion)
            .reduce((a, b) => a + b, 0)

        return offset + bar.portion / 2
    })

    const bars = entry.bars.map((bar, i) => {
        const barWidth = widths[i] - horizontalPadding;
        return (<g key={i} transform={`translate(${plotWidth * offsets[i]}, ${heightPerRow / 2})`}>
            <rect 
            rx={barHeight / 2} ry={barHeight / 2} width={barWidth} height={barHeight} fill={bar.color} x={-barWidth / 2} y={-barHeight / 2} />
            <text fill={"#fff"} fontSize={fontSize} textAnchor={"middle"} alignmentBaseline={"middle"}>{bar.value}</text>
        </g>)
    })
    return (
        <g key={i} transform={`translate(${labelWidth},${heightPerRow * i})`}>
            <g transform={`translate(${-labelWidth}, ${heightPerRow / 2})`}>
                <text
                    fontSize={fontSize}
                    textAnchor={"start"}
                    alignmentBaseline={"middle"}>{entry.name}</text>
            </g>
            {bars}
        </g>
    )
})
return (
    <div className="new-card">
        <div>
        </div>
        <svg viewBox={`0, 0, ${canvasWidth}, ${canvasHeight}`}
        height={canvasHeight}
        width={canvasWidth}
        >
            {rows}
        </svg>
    </div>
)}

谁能帮我弄清楚我哪里出错了。

【问题讨论】:

  • 你应该有一个固定的宽度和高度应该是你的计算。你做的完全相反。此外,您还需要基于 % 的高度并为不同的图表呈现不同的 svg。
  • @Yadab 可能是对的。我试图翻转这些值,但 xP 一无所获。无论如何,我玩了一点,得到了一个垂直的图表——不一样,它非常 WIP,但它是垂直的 -> codesandbox.io/s/… 。也许会有所帮助或提供一些想法,不幸的是我今天不能继续,这是一个有趣的“任务”
  • @Yadab - 如果可以,请分享一个工作示例

标签: javascript html reactjs svg charts


【解决方案1】:

我刚刚为您解决了这个问题。您可以进行其余的计算。

export 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>
    );
};

【讨论】:

  • 谢谢@Yadab。上面的代码提供了分组的垂直条形图。你能指出堆积条形图应该做的改变吗?
  • 你想要这样的东西吗? stackblitz.com/edit/…
  • @Yadab- No.. 实际上,在 tat 示例中,2010 年和 2011 年被并排分组,分别用于德国和美国。我希望 2010 年和 2011 年彼此重叠。所以德国是单条,美国是单条。
  • 喜欢这个? stackblitz.com/edit/…
  • 知道了,您想在一个栏中显示通过、跳过和失败?实现它需要一点时间。我今天休息。明天会看到这个。
【解决方案2】:

我将稍微断章取意地显示一个条形结果 - 可以在 SVG 中添加更多“条形”,这将留给您。

注意重要部分是&lt;rect width="20" height="75" y="25"&gt;&lt;/rect&gt;,其中widthheight 显示栏的“大小”。

为了好玩,我添加了一些鼠标悬停的 CSS。

.bar {
  fill: lime;
  /* changes the background */
  height: 10rem;
  transition: fill .3s ease;
  cursor: pointer;
  font-family: Helvetica, sans-serif;
}

.bar text {
  color: black;
  font-size: 0.75rem;
}

.bar:hover,
.bar:focus {
  fill: blue;
}

.bar:hover text,
.bar:focus text {
  fill: red;
}
<svg class="chart" width="200" height="200" aria-labelledby="title desc" role="img">
  <title id="title">A single bar chart</title>
  <desc id="desc">FunBar day</desc>
  <g class="bar">
    <rect width="20" height="75" y="25"></rect>
    <text x="0" y="115" dy=".25em">Fun</text>
  </g>
   <g class="bar">
    <rect width="20" height="25" y="75" x="30"></rect>
    <text x="30" y="115" dy=".25em">Goat</text>
  </g>
 </svg>

【讨论】:

  • 这更多的是静态数据,并没有展示如何使用堆积图。你能帮我处理我上面分享的数据吗?
猜你喜欢
  • 2021-12-30
  • 2013-12-07
  • 1970-01-01
  • 2022-01-16
  • 2021-11-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多