FormidableLabs / victory-native-xl

A charting library for React Native with a focus on performance and customization.
https://commerce.nearform.com/open-source/victory-native
680 stars 50 forks source link

multiple line charts with different tooltips combined in one chart #385

Open ammann29 opened 1 week ago

ammann29 commented 1 week ago

Question

Is it possible to have multiple line charts with different x and y values combined in one chart? I am using scatter on each line and on pressing scatter want to display data under the chart.

Right now, scatter is being displayed on data points but I am only able to use touch points on the first line chart only. I am only getting touch point data from first line. On pressing scatter on other lines i am getting NaN. Also, in "value" variable no matter i click on which line i am always getting ''isFirstPressActive'' as true.

"victory-native": "^41.0.1" "react-native": "^0.71.7" "@shopify/react-native-skia": "^1.4.2" "react-native-reanimated": "^3.15.4" "react-native-gesture-handler": "^2.20.0"

https://github.com/user-attachments/assets/a9028840-58fd-4f4c-ad82-786c119cc79a

const initChartPressState = {x: 0, y: {pump1: 0, pump2: 0}} as const;
const Tab = () => {
  const theme = useAppTheme();
  const styles = generateStyles(theme);
  const font = useFont(inter, 12);
  // const {state, isActive} = useChartPressState({
  //   x: 0,
  //   y: {pump1: 0, pump2: 0},
  // });
  const {state: firstTouch, isActive: isFirstPressActive} =
    useChartPressState(initChartPressState);

  const {state: secondTouch, isActive: isSecondPressActive} =
    useChartPressState(initChartPressState);

  const value = useDerivedValue(() => {
    if (isFirstPressActive) {
      console.log('first press');
      return firstTouch.y.pump1.value.value.toString();
    }
    if (isSecondPressActive) {
      console.log('second press');
      return secondTouch.y.pump2.value.value.toString();
    }
    return '';
  });

  const ToolTip = ({x, y}) => {
    return <Circle cx={x} cy={y} r={8} color="black" />;
  };

  return (
    <ScrollView style={[{width: width}]}>
      <View style={{height: 380, width: 380, paddingTop: 20}}>
        <CartesianChart
          data={DATA}
          domain={{x: [0, 2000]}}
          chartPressState={[firstTouch, secondTouch]}
          xKey="x"
          yKeys={['pump1', 'pump2', 'pump3', 'pump4']}
          axisOptions={{font}}>
          {({points}) => (
            <>
              <Line
                connectMissingData={true}
                points={points.pump1}
                yKeys={['pump1']}
                color="red"
                strokeWidth={3}
              />
              {isFirstPressActive && 
                <View>
                  <ToolTip
                    x={firstTouch.x.position}
                    y={firstTouch.y.pump1.position}
                  />
                  <Text
                    x={firstTouch.x.position}
                    y={firstTouch.y.pump1.position}
                    text={firstTouch.y.pump1.value.value}
                    font={font}
                  />
                </View>
              }
              <Scatter
                points={points.pump1}
                yKeys={['pump1']}
                shape="circle"
                radius={5}
                style="fill"
                color="red"
              />
              <Line
                connectMissingData={true}
                points={points.pump2}
                yKeys={['pump2']}
                color="blue"
                strokeWidth={3}
              />
              {isSecondPressActive &&
                <View>
                  <ToolTip
                    x={secondTouch.x.position}
                    y={secondTouch.y.pump2.position}
                  />
                  <Text
                    x={secondTouch.x.position}
                    y={secondTouch.y.pump2.position}
                    text={secondTouch.y.pump2.value.value}
                    font={font}
                  />
                </View>
              }
              <Scatter
                points={points.pump2}
                yKeys={['pump2']}
                shape="circle"
                radius={5}
                style="fill"
                color="blue"
              />
              <Line
                connectMissingData={true}
                points={points.pump3}
                yKeys={['pump3']}
                color="green"
                strokeWidth={3}
              />
              <Scatter
                points={points.pump3}
                yKeys={['pump3']}
                shape="circle"
                radius={5}
                style="fill"
                color="green"
              />
              <Line
                connectMissingData={true}
                points={points.pump4}
                yKeys={['pump4']}
                color="orange"
                strokeWidth={3}
              />
              <Scatter
                points={points.pump4}
                yKeys={['pump4']}
                shape="circle"
                radius={5}
                style="fill"
                color="orange"
              />
            </>
          )}
        </CartesianChart>
        <AnimatedText
          text={value}
          style={{fontSize: 24, color: 'black', alignSelf: 'center'}}
        />
      </View>
    </ScrollView>
  );
};

export default Tab;

const DATA = [
  {x: 0, pump1: 110, pump2: 110, pump3: 110, pump4: 110},
  {x: 100, pump1: 110, pump2: null, pump3: null, pump4: null},
  {x: 200, pump1: 106, pump2: 110, pump3: null, pump4: null},
  {x: 300, pump1: 100, pump2: null, pump3: 110, pump4: null},
  {x: 400, pump1: 85, pump2: 106, pump3: null, pump4: 110},
  {x: 500, pump1: 60, pump2: null, pump3: null, pump4: null},
  {x: 600, pump1: null, pump2: 100, pump3: 106, pump4: null},
  {x: 800, pump1: null, pump2: 85, pump3: null, pump4: 106},
  {x: 900, pump1: null, pump2: null, pump3: 100, pump4: null},
  {x: 1000, pump1: null, pump2: 60, pump3: null, pump4: null},
  {x: 1200, pump1: null, pump2: null, pump3: 85, pump4: 100},
  {x: 1500, pump1: null, pump2: null, pump3: 60, pump4: null},
  {x: 1600, pump1: null, pump2: null, pump3: null, pump4: 85},
  {x: 2000, pump1: null, pump2: null, pump3: null, pump4: 60},
];
zibs commented 5 days ago

Hey there, this is a bit tricky, but you'll want to open up the debugger and look at your values more closely and work from there.

I was able to get it to return and display the active y value using this:

const yValueDisplay = useDerivedValue(() => {
    const active = Object.entries(firstTouch.y).find(([key, v]) => {
      return !isNaN(v.value.value);
    });
    return active![1].value.value.toString();
  });

because not all the pumps have a value for where you are touching, so that is why it's NaN, and in your code you were always reaching for pump1s data primarily... It's pretty sensitive touch area, but try this out.