trying to create line down a graph in nivo at set time intervals



I’m using nivo charts to visualize some sick datasets.

The example is like this,

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',
            tickSize: 5,
            tickPadding: 5,
            tickRotation: 0,
            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, 0, 0, .5)',
                effects: [
                    {
                        on: 'hover',
                        style: {
                            itemBackground: 'rgba(0, 0, 0, .03)',
                            itemOpacity: 1
                        }
                    }
                ]
            }
        ]}
    />
)

With the data simply like this,

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

I want to really simply add in a at time 10, 14, 18 a line for when i exercise. Ideally, I’d like to be able to shade the area beneath that line for 4 hours afterwards, in a sort of parabola (or a specific shape really), having the shading finish at 4 hours after.

I’m quite lost on how to achieve this with Nivo Charts. I suppose this isn’t a normal functionality but was wondering if there was something I was missing that I could work in?

A good example of what I’m trying to achieve is like this sandbox,

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

I’d be happy to use this sandboxes code if it could be a bit more visually appealing, ideally like to stick with nivo though!

Thanks!

Answer

You can do this using a custom area layer. https://nivo.rocks/storybook/?path=/story/line–custom-layers

Here is a working example based on your initial example. The added area layer parts are commented:

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, y:0 },
      { x:2, y:0 },
      { x:3, y:0 },
      { x:4, y:0 },
      { x:5, y:0 },
      { x:6, y:0 },
      { x:7, y:0 },
      { x:8, y:0 },
      { x:9, y:0 },
      { x:10, y: 0 },
      { x:11, y: 0 },
      { x:12, y: 0 },
      { x:13, y: 0 },
      { x:14, y: 0 },
      { 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, y: repeatRoot(10, 2) - 1}},
      {data: {x: startingXCoordinate + 1.5, y: repeatRoot(10, 3) - 1}},
      {data: {x: startingXCoordinate + 2, y: repeatRoot(10, 4) - 1}},
      {data: {x: startingXCoordinate + 2.5, y: repeatRoot(10, 5) - 1}},
      {data: {x: startingXCoordinate + 3, y: repeatRoot(10, 6) - 1}},
      {data: {x: startingXCoordinate + 3.5, y: repeatRoot(10, 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',
            tickSize: 5,
            tickPadding: 5,
            tickRotation: 0,
            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, 0, 0, .5)',
              effects: [
                {
                  on: 'hover',
                  style: {
                    itemBackground: 'rgba(0, 0, 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;


Source: stackoverflow