react-native-svg-charts icon indicating copy to clipboard operation
react-native-svg-charts copied to clipboard

How can we make charts interactive and a feedback vertical line? (See the gif in the description)

Open salmandayal opened this issue 3 years ago • 8 comments

I want to achieve behavior as you can see in the gif. How to add that vertical line (light gray line in the gif ), which moves with user's gesture and gives the feedback on the graph value where is our gesture or tap is placed.. graphExample

salmandayal avatar Mar 14 '21 03:03 salmandayal

Interested in the same thing, please @salmandayal keep me posted if you find a reasonable solution

mikevercoelen avatar Apr 22 '21 12:04 mikevercoelen

@mikevercoelen The only solution seems to work is to right custom component which will work with the GestureHandler and determine the X and Y of the Tap position and update the highlighted values. I'm still working on it.

salmandayal avatar Apr 25 '21 11:04 salmandayal

Use PanResponse to get the gesture, calculate the select positon by the moveX. Then add some <G/> under the chart

452MJ avatar May 06 '21 04:05 452MJ

I came up with this! Let me know if there is a better way!

import React, {useState} from 'react';
import {View, StyleSheet} from 'react-native';
import {LineChart as SVGLineChart} from 'react-native-svg-charts';
import {Line} from 'react-native-svg';

export const LineChart = ({data, hasTooltip = false}) => {
	const {theme, styles} = useStyles();
	
	const [locationX, setX] = useState(null);
	const [trigger, setTrigger] = useState(false);	

	const getColor = (values = []) => {
		const filteredValues = values.filter(item => item);// Remove the NULL
		const color = filteredValues.slice(-1).pop() > filteredValues[0] ? theme.green : theme.red;
		return color;
	}

    const VerticalLine = ({ x, width }) => (
        <Line
            key={ 'zero-axis' }
            y1={ '0%' }
            y2={ '100%' }
            x1={ width ? x(Math.floor(locationX*data.length/width)) : x(0)}
            x2={ width ? x(Math.floor(locationX*data.length/width)) : x(0)}
            stroke={ 'grey' }
            strokeDasharray={ [ 4, 8 ] }
            strokeWidth={ 2 }
        />
    )

  	const PriceText = ({x, y, width}) => {
  		const value = width ? data[Math.floor(locationX*data.length/width)] : '--';
  		const changeValue = data?.[0] ? '(' + ((value/data[0] - 1)*100).toFixed(2)+'%)'  : '--'
  		const color = value != '--' ? value >= data[0] ? theme.green : theme.red : theme.text;
  		return (
  			<View style={styles.priceTextContainer}>
  				<StyledText style={[styles.priceText, {color}]}>{value}</StyledText>
  				<StyledText style={[styles.priceText, {marginLeft: WP(1), color}]}>{changeValue}</StyledText>
			</View>
		)
  	}

	return (
		<View style={styles.chartContainer} 
		 	onMoveShouldSetResponder={hasTooltip ? (evt) => {setTrigger(true); return true} : false}
		 	onResponderTerminate={hasTooltip ? (evt) => setTimeout(() => setTrigger(false), 5000) : false}
		 	onResponderRelease={hasTooltip ? (evt) => setTimeout(() => setTrigger(false), 5000) : false}
			onResponderMove={hasTooltip ? (evt) => setX(evt.nativeEvent.locationX) : false}
		>
			<SVGLineChart
	            style={styles.chart]}
	            data={ data }
	            svg={{ stroke: getColor(data) }}
	            contentInset={ { bottom: 0 } }
	           
	        >
	        {hasTooltip && trigger && <VerticalLine />}
	        {hasTooltip && trigger && <PriceText />}
	        </SVGLineChart>
        </View>
	);
}

shivchawla avatar Aug 27 '21 23:08 shivchawla

I make a snack example, I hope it can help you . Interactive Chart

452MJ avatar Aug 31 '21 08:08 452MJ

I've been looking for something that can do what this demo does for like 3 days, thank you thank you thank you for sharing!

aymather avatar Feb 25 '22 00:02 aymather

@shivchawla's solution works, but we randomly get the vertical line rendered twice while dragging, for a few milliseconds. Then the wrong vertical line disappears and only the correct one (i.e. the one under the user's finger) stays visible.

Is anyone experiencing the same? This seems to happen more often if you drag slowly, less frequently if you drag quickly from side to side.

39otrebla avatar Aug 23 '22 15:08 39otrebla

@39otrebla Have your tried using fixed value for chart color or stabilizing color with React.useMemo

const color = React.useMemo(() => {
    const filteredValues = data.filter((item) => item); // Remove the NULL
    return filteredValues.slice(-1).pop() > filteredValues[0] ? 'green' : 'red';
  }, [data]);

<SVGLineChart
        style={styles.chart}
        data={data}
        svg={{ stroke: color }}
        contentInset={{ bottom: 0 }}>
        {(hasTooltip && trigger) && <VerticalLine />}
        {(hasTooltip && trigger) && <PriceText />}
      </SVGLineChart>

shivchawla avatar Aug 29 '22 02:08 shivchawla