Comment ajouter une couleur de dégradé à ce graphique natif React?

Sep 12 2020

Mise en œuvre actuelle:

Ceci est un graphique utilisant la bibliothèque react-native-svg avec une info-bulle ajoutée. Je veux faire de la couleur de la ligne un dégradé plutôt qu'une seule couleur

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: Au lieu que le graphique ait juste du gris, je veux que la couleur du graphique soit rouge lorsqu'il dépasse un seuil spécifique, jaune pour une autre valeur et vert pour la valeur la plus basse.

Comme ça un peu:

Ce que j'ai trouvé:

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>
    )
  }

Je ne suis pas en mesure d'intégrer les deux. Veuillez aider.

Réponses

3 BasvanderLinden Sep 21 2020 at 08:17

C'est assez compliqué mais nous pouvons créer notre propre CustomLineChartcomposant qui hérite du LineChartkit react-native-chart-kit fourni:

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}
    />
  );
}

La plupart du code dans le rendu le même que le LineChartcomposant officiel (https://github.com/indiespirit/react-native-chart-kit/blob/master/src/line-chart/LineChart.tsx).

Les parties importantes de ce code sont le dégradé linéaire qui est ajouté et cette ligne dans le chartConfig:

color: (opacity = 1) => 'url(#grad)'

La couleur fait référence au que LinearGradientnous avons défini par son identifiant grad.


Le résultat ressemble à ceci:


Vous pouvez également ajouter plus de couleurs et jouer avec l' offsetaccessoire de chacun.

Vous pouvez également utiliser des pourcentages pour le dégradé, mais il est recommandé d'utiliser des valeurs exactes car cela présente des avantages en termes de performances par rapport à l'utilisation de pourcentages selon la documentation: https://github.com/react-native-community/react-native-svg#lineargradient.


Il est important de noter que cette implémentation n'a pas d' fillShadowGradientarrière-plan et n'implémente pas l'ombre. J'ai également omis certains de vos autres codes, comme le code de l'infobulle, qui n'est pas pertinent pour la question.