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!
Advertisement
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;