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