尝试以设定的时间间隔在 nivo 中沿图表创建线条

问题描述

我正在使用 nivo 图表来可视化一些病态数据集。

例子是这样的,

import { ResponsiveLine } from '@nivo/line'

const MyResponsiveLine = ({ data /* see data tab */ }) => (
    <ResponsiveLine
        data={data}
        margin={{ top: 50,right: 110,bottom: 50,left: 60 }}
        xScale={{ type: 'point' }}
        yScale={{ type: 'linear',min: 'auto',max: 'auto',stacked: true,reverse: false }}
        yFormat=" >-.2f"
        axisTop={null}
        axisRight={null}
        axisBottom={{
            orient: 'bottom',tickSize: 5,tickPadding: 5,tickRotation: 0,legend: 'transportation',legendOffset: 36,legendPosition: 'middle'
        }}
        axisLeft={{
            orient: 'left',legend: 'count',legendOffset: -40,legendPosition: 'middle'
        }}
        pointSize={10}
        pointColor={{ theme: 'background' }}
        pointBorderWidth={2}
        pointBorderColor={{ from: 'serieColor' }}
        pointLabelYOffset={-12}
        useMesh={true}
        legends={[
            {
                anchor: 'bottom-right',direction: 'column',justify: false,translateX: 100,translateY: 0,itemsspacing: 0,itemDirection: 'left-to-right',itemWidth: 80,itemHeight: 20,itemOpacity: 0.75,symbolSize: 12,symbolShape: 'circle',symbolBorderColor: 'rgba(0,.5)',effects: [
                    {
                        on: 'hover',style: {
                            itemBackground: 'rgba(0,.03)',itemOpacity: 1
                        }
                    }
                ]
            }
        ]}
    />
)

就这么简单的数据,

0: 0
1: 0
2: 0
3: 0
4: 0
5: 0
6: 0
7: 0
8: 0
9: 0
10: 0
11: 0
12: 0
13: 0
14: 0
15: -4.1524
16: -2.1525
17: -3.12351
18: 5.123123
19: 3.123123
20: 0.6547929999999998
21: 0.414856
22: -1.1863169999999998
23: 0.7934469999999998

我真的想简单地在时间 10、14、18 添加一行,以便在我锻炼时使用。理想情况下,我希望能够在 4 小时后以抛物线(或实际上是特定形状)对这条线下方的区域进行着色,并在 4 小时后完成着色。

我完全不知道如何使用 Nivo Charts 实现这一目标。我想这不是一个正常的功能,但想知道我是否缺少可以使用的东西?

我想要实现的一个很好的例子就是这个沙箱,

https://codesandbox.io/s/simple-composed-chart-forked-b0bfi

如果这个沙箱代码在视觉上更吸引人,我很乐意使用它,但最好还是坚持使用 nivo!

谢谢!

解决方法

您可以使用自定义区域图层执行此操作。 https://nivo.rocks/storybook/?path=/story/line--custom-layers

这是一个基于您的初始示例的工作示例。添加的区域图层部分注释:

import { ResponsiveLine } from '@nivo/line'

/* Added these two imports */
import { Defs } from '@nivo/core'
import { area,curveMonotoneX } from 'd3-shape'


function App() {
  let data = [{
    id:"data",data: [
      { x:0,y:0 },{ x:1,{ x:2,{ x:3,{ x:4,{ x:5,{ x:6,{ x:7,{ x:8,{ x:9,{ x:10,y: 0 },{ x:11,{ x:12,{ x:13,{ x:14,{ x:15,y: -4.1524 },{ x:16,y: -2.1525 },{ x:17,y: -3.12351 },{ x:18,y: 5.123123 },{ x:19,y: 3.123123 },{ x:20,y: 0.6547929999999998 },{ x:21,y: 0.414856 },{ x:22,y: -1.1863169999999998 },{ x:23,y: 0.7934469999999998 }]
  }];

  /* Added this AreaLayer function */
  function createAreaLayer(startingXCoordinate) {
    let areaData = [
      {data: {x: startingXCoordinate,y: 5}},{data: {x: startingXCoordinate + 0.5,y: repeatRoot(10,1) - 1}},{data: {x: startingXCoordinate + 1,2) - 1}},{data: {x: startingXCoordinate + 1.5,3) - 1}},{data: {x: startingXCoordinate + 2,4) - 1}},{data: {x: startingXCoordinate + 2.5,5) - 1}},{data: {x: startingXCoordinate + 3,6) - 1}},{data: {x: startingXCoordinate + 3.5,7) - 1}},{data: {x: startingXCoordinate + 4,y: 0}},];

    function repeatRoot(number,times) {
      if (times === 1) {
        return Math.sqrt(number);
      } else {
        return Math.sqrt(repeatRoot(number,times - 1));
      }
    }

    function interpolatedXScale(xScale,x) {
      const floorX = Math.floor(x);
      const decimalPart = x - floorX;
      return xScale(floorX) + (xScale(floorX + 1) - xScale(floorX)) * decimalPart;
    }

    return function({series,xScale,yScale,innerHeight}) {
      const areaGenerator = area()
          .x(d => interpolatedXScale(xScale,d.data.x))
          .y0(yScale(0))
          .y1(d => yScale(d.data.y))
          .curve(curveMonotoneX);

      return (
          <>
            <Defs
                defs={[
                  {
                    id: 'pattern',type: 'patternLines',background: 'transparent',color: '#3daff7',lineWidth: 1,spacing: 6,rotation: -45,},]}
            />
            <path
                d={areaGenerator(areaData)}
                fill="url(#pattern)"
                fillOpacity={0.6}
                stroke="#3daff7"
                strokeWidth={2}
            />
          </>
      )
    };
  }

  return (
    <div  style={{height:"500px"}}>
      <ResponsiveLine
          data={data}
          margin={{
            top: 0,right: 50,bottom: 50,left: 50
          }}
          yScale={{
            type: "linear",stacked: false
          }}
          xScale={{ type: 'point' }}
          yFormat=" >-.2f"
          axisTop={null}
          axisRight={null}
          axisBottom={{
            orient: 'bottom',tickSize: 5,tickPadding: 5,tickRotation: 0,legend: 'transportation',legendOffset: 36,legendPosition: 'middle'
          }}
          axisLeft={{
            orient: 'left',legend: 'count',legendOffset: -40,legendPosition: 'middle'
          }}
          pointSize={10}
          pointColor={{ theme: 'background' }}
          pointBorderWidth={2}
          pointBorderColor={{ from: 'serieColor' }}
          pointLabelYOffset={-12}
          useMesh={true}
          legends={[
            {
              anchor: 'bottom-right',direction: 'column',justify: false,translateX: 100,translateY: 0,itemsSpacing: 0,itemDirection: 'left-to-right',itemWidth: 80,itemHeight: 20,itemOpacity: 0.75,symbolSize: 12,symbolShape: 'circle',symbolBorderColor: 'rgba(0,.5)',effects: [
                {
                  on: 'hover',style: {
                    itemBackground: 'rgba(0,.03)',itemOpacity: 1
                  }
                }
              ]
            },]}

          /* Added this layers attribute */
          layers={[
            'grid','markers','areas',createAreaLayer(10),createAreaLayer(14),createAreaLayer(18),'lines','slices','axes','points','legends',]}
      />


    </div>
  );
}

export default App;