FormidableLabs/victory-native-xl

Can you get the position of the last xposition value I drew on the chart?

Opened this issue · 5 comments

Question

When the chart is loaded (before the first touch happens)

스크린샷 2024-05-13 오전 10 37 54

The custom tooltip should be displayed as shown in the picture.

I was able to draw the picture because I got the firstTouch.x.position by doing the first touch.

Can I get the xposition value like the title

zibs commented

Is your question related to this issue here @LESANF #253 ?

@zibs Thank you for commenting on my issue

It seems to be a different matter from the issue.

When Line Chart was loaded and drawn

I need xPosition Value for the last value of the data I put in the chart

zibs commented

The render prop value of points here would have that information -- is that useful?

The render prop value of points here would have that information -- is that useful?

I am managing the current value like the code below.

When the chart is rendered, you want the ActiveValueIndicator rendered with the value of showTooltip to be on the far right of the line.

This is why the last xPosition value was required when it was loaded

However, there is no solution to the points value because it has returned to undefined.

<CartesianChart
    data={GLUCOSE_DATA}
    xKey="rcdTime"
    padding={{ top: 70, right: 20, left: 10 }}
    yKeys={['glucoseVal']}
    domain={{ y: [0, 230] }}
    chartPressState={[firstTouch]}
    axisOptions={{
        font,
        axisSide: { x: 'bottom', y: 'right' },
        tickCount: { x: 4, y: 2 },
        labelOffset: { x: 4, y: 12 },
        formatXLabel: value => convertMinutesToTime(value),
    }}
    renderOutside={({ chartBounds, points }) => (
        <>
            {isFirstPressActive && (
                <>
                    <ActiveValueIndicator
                        xPosition={firstTouch.x.position}
                        yPosition={firstTouch.y.glucoseVal.position}
                        bottom={chartBounds.bottom}
                        top={chartBounds.top}
                        firstTouch={firstTouch}
                        activeValue={firstTouch.y.glucoseVal.value}
                        textColor={'#fafafa'}
                        lineColor={'#71717a'}
                        indicatorColor={'#7ee17e'}
                    />
                </>
            )}
            {showTooltip && (
                <>
                    <ActiveValueIndicator
                        xInitialPosition={chartBounds.right}
                        xPosition={firstTouch.x.position}
                        yPosition={firstTouch.y.glucoseVal.position}
                        bottom={chartBounds.bottom}
                        top={chartBounds.top}
                        firstTouch={firstTouch}
                        activeValue={firstTouch.y.glucoseVal.value}
                        firstTouchFlag={firstTouchFlag}
                        textColor={'#fafafa'}
                        lineColor={'#71717a'}
                        indicatorColor={'#7ee17e'}
                        xPositionTime={firstTouch.x.value.value}
                        lastGluData={GLUCOSE_LAST_DATA}
                        lastGluTime={GLUCOSE_LAST_TIME}
                    />
                </>
            )}
            {
                <Rect
                    x={10}
                    y={150}
                    height={60}
                    width={chartBounds.right - 10}
                    color={'rgba(237, 248, 255, 0.5)'}
                ></Rect>
            }
        </>
    )}
>... </CartesianChart>

@LESANF By using useSharedValue for managing the x and y positions, you ensure that these values are always up-to-date, even when the points value is undefined initially.

import { useSharedValue } from 'react-native-reanimated';
const chartBoundsV = useSharedValue<ChartBounds>({ bottom: 0, left: 0, right: 0, top: 0 });
const positionX = useSharedValue(0);
const positionY = useSharedValue(0);

Here's how you can manage the ActiveValueIndicator based on the active state and ensure the tooltip follows the touch position and moves to the end when the touch ends:

renderOutside={({ chartBounds }) => {
  return (
    <ActiveValueIndicator
      xPosition={isActive ? state.x.position : positionX}
      yPosition={isActive ? state.y.close?.position : positionY}
      chartBounds={chartBounds}
      lineColor={colors.action}
      indicatorColor={colors.action}>
      {activeValueIndicatorChildren(chartBounds)}
    </ActiveValueIndicator>
  );
}}
{({ chartBounds, points }) => {
  chartBoundsV.value = chartBounds;
  positionX.value = points.close[points.close.length - 1].x;
  positionY.value = points.close[points.close.length - 1].y ?? 0;

  return <GradientArea color={colors.action} points={points.close} chartBounds={chartBounds} />;
}}

If you want to add an external tooltip that both follows the position you press and moves to the end when you finish pressing, you can do something like this. I found this solution, and I hope it helps.

const closePositionYRect = useDerivedValue(() => {
  return Math.min(
    (isActive ? state.y.close?.position.value : positionY.value) - RECT_TOP_MARGIN,
    chartBoundsV.value.bottom - TOTAL_MARGIN - FONT_DEFAULT_HEIGHT * 2,
  );
}, [isActive, state.y.close?.position.value, positionY.value, chartBoundsV.value.bottom]);

const activeValueIndicatorChildren = (chartBounds: ChartBounds) => {
  const rectX = chartBounds.right + MARGIN_BETWEEN_CHART_AND_BOX;

  return (
    <>
      <RoundedRect
        color={'#00A2E8'}
        width={activeCloseRectWidth}
        height={RECT_HEIGHT}
        r={RECT_RADIUS}
        x={rectX}
        y={closePositionYRect}
      />
    </>
  );
};