Current Implementation:
This is a Chart using react-native-svg library with a tool-tip added to it. I want to make the color of the line into a gradient rather than a single color
Code:
import React, { useState } from 'react'
import { View, Text, Dimensions } from 'react-native'
import { LineChart } from 'react-native-chart-kit'
import { Rect, Text as TextSVG, Svg } from "react-native-svg";
const Charts = () => {
let [tooltipPos, setTooltipPos] = useState({ x: 0, y: 0, visible: false, value: 0 })
return (
<View>
<LineChart
data={{
labels: ["January", "February", "March", "April", "May", "June"],
datasets: [
{
data: [
100, 110, 90, 130, 80, 103
]
}
]
}}
width={Dimensions.get("window").width}
height={250}
yAxisLabel="$"
yAxisSuffix="k"
yAxisInterval={1}
chartConfig={{
backgroundColor: "white",
backgroundGradientFrom: "#fbfbfb",
backgroundGradientTo: "#fbfbfb",
decimalPlaces: 2,
color: (opacity = 1) => `rgba(0, 0, 0, ${opacity})`,
labelColor: (opacity = 1) => `rgba(0, 0, 0, ${opacity})`,
style: {
borderRadius: 0
},
propsForDots: {
r: "6",
strokeWidth: "0",
stroke: "#fbfbfb"
}
}}
bezier
style={{
marginVertical: 8,
borderRadius: 6
}}
decorator={() => {
return tooltipPos.visible ? <View>
<Svg>
<Rect x={tooltipPos.x - 15}
y={tooltipPos.y + 10}
width="40"
height="30"
fill="black" />
<TextSVG
x={tooltipPos.x + 5}
y={tooltipPos.y + 30}
fill="white"
fontSize="16"
fontWeight="bold"
textAnchor="middle">
{tooltipPos.value}
</TextSVG>
</Svg>
</View> : null
}}
onDataPointClick={(data) => {
let isSamePoint = (tooltipPos.x === data.x
&& tooltipPos.y === data.y)
isSamePoint ? setTooltipPos((previousState) => {
return {
...previousState,
value: data.value,
visible: !previousState.visible
}
})
:
setTooltipPos({ x: data.x, value: data.value, y: data.y, visible: true });
}}
/>
</View>
)
}
export default Charts
QUESTION: Instead of the chart having just grey, I want the color of the chart as red when it goes above a specific threshold, yellow for another value and green for the lowest value.
What I found:
render() {
const data = [ 50, 10, 40, 95, -4, -24, 85, 91, 35, 53, -53, 24, 50, -20, -80 ]
const Gradient = () => (
<Defs key={'gradient'}>
<LinearGradient id={'gradient'} x1={'0'} y={'0%'} x2={'100%'} y2={'0%'}>
<Stop offset={'0%'} stopColor={'rgb(134, 65, 244)'}/>
<Stop offset={'100%'} stopColor={'rgb(66, 194, 244)'}/>
</LinearGradient>
</Defs>
)
return (
<LineChart
style={ { height: 200 } }
data={ data }
contentInset={ { top: 20, bottom: 20 } }
svg={{
strokeWidth: 2,
stroke: 'url(#gradient)',
}}
>
<Grid/>
<Gradient/>
</LineChart>
)
}
I am not able to integrate the two. Please help.
It's quite involved but we can create our own CustomLineChart
component that inherits from the LineChart
react-native-chart-kit provides:
class CustomLineChart extends LineChart {
render() {
const {
width,
height,
data,
withScrollableDot = false,
withShadow = true,
withDots = true,
withInnerLines = true,
withOuterLines = true,
withHorizontalLines = true,
withVerticalLines = true,
withHorizontalLabels = true,
withVerticalLabels = true,
style = {},
decorator,
onDataPointClick,
verticalLabelRotation = 0,
horizontalLabelRotation = 0,
formatYLabel = (yLabel) => yLabel,
formatXLabel = (xLabel) => xLabel,
segments,
transparent = false,
chartConfig,
} = this.props;
const {scrollableDotHorizontalOffset} = this.state;
const {labels = []} = data;
const {
borderRadius = 0,
paddingTop = 16,
paddingRight = 64,
margin = 0,
marginRight = 0,
paddingBottom = 0,
} = style;
const config = {
width,
height,
verticalLabelRotation,
horizontalLabelRotation,
};
const datas = this.getDatas(data.datasets);
let count = Math.min(...datas) === Math.max(...datas) ? 1 : 4;
if (segments) {
count = segments;
}
const legendOffset = this.props.data.legend ? height * 0.15 : 0;
return (
<View style={style}>
<Svg
height={height + paddingBottom + legendOffset}
width={width - margin * 2 - marginRight}>
<Defs>
<LinearGradient id="grad" x1="0" y1="0" x2="0" y2="1">
<Stop offset="0" stopColor="red" stopOpacity="1" />
<Stop offset="1" stopColor="blue" stopOpacity="1" />
</LinearGradient>
</Defs>
<Rect
width="100%"
height={height + legendOffset}
rx={borderRadius}
ry={borderRadius}
fill="white"
fillOpacity={transparent ? 0 : 1}
/>
{this.props.data.legend &&
this.renderLegend(config.width, legendOffset)}
<G x="0" y={legendOffset}>
<G>
{withHorizontalLines &&
(withInnerLines
? this.renderHorizontalLines({
...config,
count: count,
paddingTop,
paddingRight,
})
: withOuterLines
? this.renderHorizontalLine({
...config,
paddingTop,
paddingRight,
})
: null)}
</G>
<G>
{withHorizontalLabels &&
this.renderHorizontalLabels({
...config,
count: count,
data: datas,
paddingTop: paddingTop,
paddingRight: paddingRight,
formatYLabel,
decimalPlaces: chartConfig.decimalPlaces,
})}
</G>
<G>
{withVerticalLines &&
(withInnerLines
? this.renderVerticalLines({
...config,
data: data.datasets[0].data,
paddingTop: paddingTop,
paddingRight: paddingRight,
})
: withOuterLines
? this.renderVerticalLine({
...config,
paddingTop: paddingTop,
paddingRight: paddingRight,
})
: null)}
</G>
<G>
{withVerticalLabels &&
this.renderVerticalLabels({
...config,
labels,
paddingTop: paddingTop,
paddingRight: paddingRight,
formatXLabel,
})}
</G>
<G>
{this.renderLine({
...config,
...chartConfig,
paddingRight: paddingRight,
paddingTop: paddingTop,
data: data.datasets,
})}
</G>
<G>
{withDots &&
this.renderDots({
...config,
data: data.datasets,
paddingTop: paddingTop,
paddingRight: paddingRight,
onDataPointClick,
})}
</G>
<G>
{withScrollableDot &&
this.renderScrollableDot({
...config,
...chartConfig,
data: data.datasets,
paddingTop: paddingTop,
paddingRight: paddingRight,
onDataPointClick,
scrollableDotHorizontalOffset,
})}
</G>
<G>
{decorator &&
decorator({
...config,
data: data.datasets,
paddingTop,
paddingRight,
})}
</G>
</G>
</Svg>
{withScrollableDot && (
<ScrollView
style={StyleSheet.absoluteFill}
contentContainerStyle={{width: width * 2}}
showsHorizontalScrollIndicator={false}
scrollEventThrottle={16}
onScroll={Animated.event([
{
nativeEvent: {
contentOffset: {x: scrollableDotHorizontalOffset},
},
},
])}
horizontal
bounces={false}
/>
)}
</View>
);
}
}
function App() {
return (
<CustomLineChart
data={{
labels: ['January', 'February', 'March', 'April', 'May', 'June'],
datasets: [
{
data: [100, 110, 90, 130, 80, 103],
},
],
}}
width={Dimensions.get('window').width}
height={250}
chartConfig={{
backgroundGradientFrom: '#fbfbfb',
backgroundGradientTo: '#fbfbfb',
color: (opacity = 1) => 'url(#grad)',
labelColor: (opacity = 1) => `rgba(0, 0, 0, ${opacity})`,
}}
bezier
withInnerLines={false}
withOuterLines={false}
/>
);
}
Most of the code in the render the same as the official LineChart
component (https://github.com/indiespirit/react-native-chart-kit/blob/master/src/line-chart/LineChart.tsx).
The important parts in this code are the linear gradient that is added and this line in the chartConfig
:
color: (opacity = 1) => 'url(#grad)'
The color refers to the LinearGradient
we've defined by its id grad
.
The result looks something like this:
You can also add more colors and play around with the offset
prop of each.
You can also use percentages for the gradient, but it's recommended to use exact values as it has performance advantages over using percentages according to the documentation: https://github.com/react-native-community/react-native-svg#lineargradient.
Important to note is that this implementation doesn't have a fillShadowGradient
as a background and it doesn't implement the shadow. I've also left out some of your other code like the tooltip code that isn't relevant to the question.