coinjar / react-native-wagmi-charts

A sweet & simple chart library for React Native that will make us feel like We're All Gonna Make It.
MIT License
586 stars 116 forks source link

Chart path is slow to update #58

Open nandorojo opened 2 years ago

nandorojo commented 2 years ago

I'm having a slightly weird issue. The LineChart path is slow to update. I have transitions disabled. It may require looking closely. However, notice that when I change time ranges, the color changes a split second before the chart path does.

Video

https://www.loom.com/share/e5713040946c4145ba64433167b1dd73

Explanation

The color changes for a second before the actual path changes. It's bizarre.

To see it clearly: when I set the time range to 1D, notice that the horizontal line shows half a second before the chart path changes. I can't seem to think of what would cause this. My only guess is if internally there is some sort of state getting set inside of an effect? Or is there some sort of interpolation happening when it shouldn't?

I haven't tried to repro yet, but I will when I get the chance.

It's not like terrible but it makes the app seem laggy. There are no network requests being sent when you toggle the time range; it's all pre-fetched. It only has one state update.

Anything come to mind @jxom?

Thanks again!

nandorojo commented 2 years ago

I found the issue: it seems that it may be related to reanimated's animatedProps or something not rendering synchronously? In any case, using Path instead of AnimatedPath inside of LineChartPath solved it.

I opted to render Path directly when transitions are disabled.

import * as React from 'react';
import Animated, {
  useAnimatedProps,
  useAnimatedReaction,
  useSharedValue,
  withTiming,
} from 'react-native-reanimated';
import { Path, PathProps } from 'react-native-svg';

import { LineChartDimensionsContext } from './Chart';
import interpolatePath from './interpolatePath';
import { usePrevious } from '../../utils';

const AnimatedPath = Animated.createAnimatedComponent(Path);

export type LineChartPathProps = Animated.AnimateProps<PathProps> & {
  color?: string;
  width?: number;
  isInactive?: boolean;
  /**
   * Default: `true`.
   *
   * If `false`, changes in the chart's path will not animate.
   *
   * While this use case is rare, it may be useful on web, where animations might not work as well.
   *
   * **Example**
   *
   * ```tsx
   * <LineChart.Path
   *   pathProps={{ isTransitionEnabled: Platform.OS !== 'web' }}
   * />
   * ```
   */
  isTransitionEnabled?: boolean;
};

export function LineChartPath({
  color = 'black',
  width: strokeWidth = 3,
  isInactive,
  isTransitionEnabled = true,
  ...props
}: LineChartPathProps) {
  const { path } = React.useContext(LineChartDimensionsContext);

  ////////////////////////////////////////////////

  const transition = useSharedValue(0);

  const previousPath = usePrevious(path);

  useAnimatedReaction(
    () => {
      return path;
    },
    (_, previous) => {
      if (previous) {
        transition.value = 0;
        transition.value = withTiming(1);
      }
    },
    [path]
  );

  const animatedProps = useAnimatedProps(() => {
    let d = path || '';
    if (previousPath && isTransitionEnabled) {
      const pathInterpolator = interpolatePath(previousPath, path, null);
      d = pathInterpolator(transition.value);
    }
    return {
      d,
    };
  });

  const pathProps: React.ComponentProps<typeof Path> = {
    fill: 'transparent',
    stroke: color,
    strokeWidth,
    strokeOpacity: isInactive ? 0.2 : 1,
  }

  if (!isTransitionEnabled) {
    return <Path d={path || ''} {...pathProps} />
  }

  ////////////////////////////////////////////////

  return (
    <AnimatedPath
      animatedProps={animatedProps}
      {...pathProps}
      {...props}
    />
  );
}

Would you accept a PR for this?

honeybadger26 commented 6 months ago

Hi @nandorojo,

Thanks for raising this. I would be happy to look at a PR if you have one.