Làm cách nào để thêm Gradient Color vào biểu đồ React Native này?

Sep 12 2020

Thực hiện hiện tại:

Đây là một Biểu đồ sử dụng thư viện react-native-svg với một mẹo công cụ được thêm vào nó. Tôi muốn biến màu của đường thành một gradient thay vì một màu duy nhất

Mã:

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

HỎI: Thay vì biểu đồ chỉ có màu xám, tôi muốn màu của biểu đồ là màu đỏ khi nó vượt qua một ngưỡng cụ thể, màu vàng cho giá trị khác và màu xanh lục cho giá trị thấp nhất.

Như thế này phần nào:

Những gì tôi tìm thấy:

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

Tôi không thể tích hợp cả hai. Hãy giúp tôi.

Trả lời

3 BasvanderLinden Sep 21 2020 at 08:17

Nó khá liên quan nhưng chúng tôi có thể tạo CustomLineChartthành phần của riêng mình kế thừa từ bộ LineChartphản ứng-native-chart-kit cung cấp:

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

Hầu hết mã trong kết xuất giống với LineChartthành phần chính thức (https://github.com/indiespirit/react-native-chart-kit/blob/master/src/line-chart/LineChart.tsx).

Các phần quan trọng trong mã này là gradient tuyến tính được thêm vào và dòng này trong chartConfig:

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

Màu đề cập đến LinearGradientchúng tôi đã xác định bởi id của nó grad.


Kết quả trông giống như sau:


Bạn cũng có thể thêm nhiều màu hơn và chơi xung quanh với giá offsetđỡ của mỗi màu.

Bạn cũng có thể sử dụng tỷ lệ phần trăm cho gradient, nhưng bạn nên sử dụng các giá trị chính xác vì nó có lợi thế về hiệu suất so với việc sử dụng tỷ lệ phần trăm theo tài liệu: https://github.com/react-native-community/react-native-svg#lineargradient.


Điều quan trọng cần lưu ý là triển khai này không có fillShadowGradientlàm nền và nó không triển khai bóng đổ. Tôi cũng đã bỏ sót một số mã khác của bạn như mã chú giải công cụ không liên quan đến câu hỏi.