Visx Streamgraph 自定义类型

问题描述 投票:0回答:1

我正在使用 visx 为我的应用程序构建流图,但对 visx/d3 非常陌生。我可以使用简单的二维数字数组(如this示例中所示)生成图形,但在修改输入结构时遇到问题。在下面的示例中,目标是生成一个随着

t
增加而垂直向下运行的流图,其中
t
是以毫秒为单位的时间,
value
是数字
[0,1]
。您可以看到有必要修改输入数据类型以包含
t
,这可能会导致点之间的垂直距离发生变化。我相当确定访问器缺少一些东西,因为日志记录显示所有路径在传递到
NaN
子方法时都是
<Stack>
的。希望有人能指出我应该如何构建和访问这里的数据。谢谢!

// t represents time in milliseconds, value represents value at that time
type Datum = {t: number, value: number}[];

export type StreamGraphProps = {
  data: Datum[]
  width: number;
  height: number;
  animate?: boolean;
};

export default function Streamgraph({data, width, height, animate = true}: StreamGraphProps) {
  if (data.length === 0) return null

  // Should represent the different series
  const keys = _.range(data[0].length);

  // Maximum value of t
  const tMax = data.reduce((max, cur) => Math.max(max, ...cur.map(v => v.t)), 0)

  // Scales
  const xScale = scaleLinear<number>({
    domain: [0, 1],
    range: [0, width],
  });
  const yScale = scaleLinear<number>({
    domain: [tMax, 0],
    range: [height, 0]
  });
  const colorScale = scaleOrdinal<number, string>({
    domain: keys,
    range: ['#ffc409', '#f14702', '#262d97', 'white', '#036ecd', '#9ecadd'],
  });
  const patternScale = scaleOrdinal<number, string>({
    domain: keys,
    range: ['gor', 'gpo', 'gpb', 'gpr', 'gpur', 'gsp', 'gtb'],
  });

  // x getter
  const getX = (d: SeriesPoint<Datum>, i: number) => xScale(d.data[i].value)
  // y0/y1 getters
  const getY0 = (d: SeriesPoint<Datum>) => yScale(d.data[0].t)
  const getY1 = (d: SeriesPoint<Datum>) => yScale(d.data[1].t)

  if (width < 10) return null

  return (
    <svg width={width} height={height}>
      <GradientPinkBlue id={"gpb"}/>
      <GradientPurpleOrange id={"gpo"}/>
      <GradientOrangeRed id={"gor"}/>
      <GradientPinkRed id={"gpr"}/>
      <GradientPurpleRed id={"gpur"}/>
      <GradientSteelPurple id={"gsp"}/>
      <GradientTealBlue id={"gtb"}/>

      <Group>
        <Stack<Datum, number>
          data={data}
          keys={keys}
          offset="silhouette"
          order='insideout'
          color={colorScale}
          x={getX}
          y0={getY0}
          y1={getY1}
          curve={curveCatmullRom}
        >
          {({stacks, path}) => {
            return stacks.map((stack) => {
              const pathString = path(stack) || '';
              const tweened = animate ? useSpring({pathString}) : {pathString};
              const color = colorScale(stack.key);
              const pattern = patternScale(stack.key);
              return (
                <g key={`series-${stack.key}`}>
                  <animated.path d={tweened.pathString} fill={color}/>
                  <animated.path d={tweened.pathString} fill={`url(#${pattern})`}/>
                </g>
              );
            })
          }}
        </Stack>
      </Group>
    </svg>
  );
}
reactjs d3.js charts visualization visx
1个回答
0
投票

好吧,我发现我需要设置

value
访问器。这就是让
visx
从任意输入类型检索 y0,y1 值的原因。

// t represents time in milliseconds, value represents value at that time
type Datum = {t_start: number, t_end: number, features: Record<Features, number>};

export type StreamGraphProps = {
  data: Datum[]
  width: number
  height: number
  animate?: boolean
};

export default function Streamgraph({data, width, height, animate = true}: StreamGraphProps) {
  if (data.length === 0) return null

  // Should represent the different series
  const keys = _.uniq(data.flatMap(v => Object.keys(v.features))) as Features[]

  // Maximum value of t
  const tMax = Object.values(data).reduce((acc,v) => {
    return Math.max(v.t_end, acc)
  }, 0)

  // Scales
  const xScale = scaleLinear<number>({
    domain: [0, tMax],
    range: [0, width],
  });
  const yScale = scaleLinear<number>({
    domain: [-3.5, 3.5],
    range: [height, 0]
  });
  const colorScale = scaleOrdinal<Features, string>({
    domain: keys,
    range: ['#ffc409', '#f14702', '#262d97', 'white', '#036ecd', '#9ecadd', 'green'],
  });
  const patternScale = scaleOrdinal<Features, string>({
    domain: keys,
    range: ['gor', 'gpo', 'gpb', 'gpr', 'gpur', 'gsp', 'gtb'],
  });

  // x getter
  const getX = (d: SeriesPoint<Datum>) => d.data.t_start === 0 ? 0 :
    d.data.t_end === tMax ? tMax :
    xScale((d.data.t_end + d.data.t_start)/2)

  if (width < 10) return null

  return (
    <svg width={width} height={height}>
      <GradientPinkBlue id={"gpb"}/>
      <GradientPurpleOrange id={"gpo"}/>
      <GradientOrangeRed id={"gor"}/>
      <GradientPinkRed id={"gpr"}/>
      <GradientPurpleRed id={"gpur"}/>
      <GradientSteelPurple id={"gsp"}/>
      <GradientTealBlue id={"gtb"}/>

      <Group width={width} height={height}>
        <Stack<Datum, Features>
          data={Object.values(data)}
          keys={keys}
          offset="silhouette"
          order='insideout'
          color={colorScale}
          value={(d,k) => d.features[k]}
          x={getX}
          curve={curveCatmullRom}
          y0={v => yScale(v[0])}
          y1={v => yScale(v[1])}
        >
          {({stacks, path}) => {
            return stacks.map((stack) => {
              const pathString = path(stack) || '';
              const tweened = animate ? useSpring({pathString}) : {pathString};
              const color = colorScale(stack.key);
              const pattern = patternScale(stack.key);
              return (
                <g key={`series-${stack.key}`}>
                  <animated.path d={tweened.pathString} fill={color}/>
                  <animated.path d={tweened.pathString} fill={`url(#${pattern})`}/>
                </g>
              );
            })
          }}
        </Stack>
      </Group>
    </svg>
  );
}
© www.soinside.com 2019 - 2024. All rights reserved.