如何向此 React Native 图表添加渐变颜色?
How do I add Gradient Colour to this React Native Chart?
当前实施:
这是一个使用 react-native-svg 库并添加了工具提示的图表。我想把线条的颜色做成渐变而不是单一的颜色
代码:
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
问题:
当图表超过特定阈值时,我希望图表的颜色不是只有灰色,而是红色,另一个值是黄色,最低值是绿色。
有点像这样:
我发现了什么:
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>
)
}
我无法将两者结合起来。请帮忙。
它非常复杂,但我们可以创建自己的 CustomLineChart
组件,它继承自 LineChart
react-native-chart-kit 提供的:
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}
/>
);
}
渲染中的大部分代码与官方 LineChart
组件相同 (https://github.com/indiespirit/react-native-chart-kit/blob/master/src/line-chart/LineChart.tsx)。
此代码中的重要部分是添加的线性渐变和 chartConfig
:
中的这一行
color: (opacity = 1) => 'url(#grad)'
颜色指的是我们用它的id grad
定义的LinearGradient
。
结果看起来像这样:
您还可以添加更多颜色并使用每个颜色的 offset
道具。
您也可以使用百分比作为渐变,但建议使用精确值,因为根据文档,它比使用百分比具有性能优势:https://github.com/react-native-community/react-native-svg#lineargradient.
需要注意的重要一点是,此实现没有 fillShadowGradient
作为背景,也没有实现阴影。我还遗漏了一些您的其他代码,例如与问题无关的工具提示代码。
当前实施:
这是一个使用 react-native-svg 库并添加了工具提示的图表。我想把线条的颜色做成渐变而不是单一的颜色
代码:
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
问题: 当图表超过特定阈值时,我希望图表的颜色不是只有灰色,而是红色,另一个值是黄色,最低值是绿色。
有点像这样:
我发现了什么:
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>
)
}
我无法将两者结合起来。请帮忙。
它非常复杂,但我们可以创建自己的 CustomLineChart
组件,它继承自 LineChart
react-native-chart-kit 提供的:
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}
/>
);
}
渲染中的大部分代码与官方 LineChart
组件相同 (https://github.com/indiespirit/react-native-chart-kit/blob/master/src/line-chart/LineChart.tsx)。
此代码中的重要部分是添加的线性渐变和 chartConfig
:
color: (opacity = 1) => 'url(#grad)'
颜色指的是我们用它的id grad
定义的LinearGradient
。
结果看起来像这样:
您还可以添加更多颜色并使用每个颜色的 offset
道具。
您也可以使用百分比作为渐变,但建议使用精确值,因为根据文档,它比使用百分比具有性能优势:https://github.com/react-native-community/react-native-svg#lineargradient.
需要注意的重要一点是,此实现没有 fillShadowGradient
作为背景,也没有实现阴影。我还遗漏了一些您的其他代码,例如与问题无关的工具提示代码。