From f16e4bab4698eceb9ab6a2a723a7922224c3281e Mon Sep 17 00:00:00 2001 From: Adrian Furman Date: Sun, 7 May 2023 22:54:08 +0200 Subject: [PATCH] Add POC for above canvas draggable value indicator --- .../components/LineChart/LineChart.tsx | 10 +- package.json | 6 +- src/LineChart/LineChart.tsx | 183 ++++++++++++++---- src/LineChart/types.ts | 3 + yarn.lock | 25 +-- 5 files changed, 164 insertions(+), 63 deletions(-) diff --git a/example/src/screens/components/LineChart/LineChart.tsx b/example/src/screens/components/LineChart/LineChart.tsx index 8bc29b6..750950b 100644 --- a/example/src/screens/components/LineChart/LineChart.tsx +++ b/example/src/screens/components/LineChart/LineChart.tsx @@ -14,6 +14,7 @@ const LineChartScreen = () => { const [horizontalLinesShown, setHorizontalLinesShown] = useState(false); const [verticalLinesShown, setVerticalLinesShown] = useState(false); const [showArrows, setShowArrows] = useState(false); + const [exactValue, setExactValue] = useState(undefined); const xLabels = utils.linspace(0, 100, 10); const yLabels = utils.linspace(0, 100, 20); @@ -59,10 +60,17 @@ const LineChartScreen = () => { }, }, }} - font={LatoRegular}> + font={LatoRegular} + onIndicatorChange={v => setExactValue(v.y)}> + + Value: + {exactValue ? ( + {exactValue} + ) : null} + Show horizontal lines: { const padding = ensureDefaults(customPadding, defaultPadding); const xAxisHeight = xAxis?.height || 30; @@ -44,6 +53,10 @@ const LineChart = ({ }; }; + // const mapCanvasToDomainX = (canvasX: number): number => { + // return (canvasX / contentWidth) * (xDomain[1] - xDomain[0]); + // }; + const dataSeries = extractProps(children); const paths = dataSeries.map(({ data, color, strokeWidth }) => { @@ -60,49 +73,135 @@ const LineChart = ({ ); }); + const indicatorX = useSharedValue(0); + const indicatorScale = useSharedValue(1); + + const pan = Gesture.Pan() + .onStart(() => { + indicatorScale.value = withTiming(1.2, { duration: 200 }); + }) + .onChange(e => { + indicatorX.value += e.changeX; + const canvasX = indicatorX.value + 15; + const domainX = (canvasX / contentWidth) * (xDomain[1] - xDomain[0]); + let domainY = 0; + dataSeries[0]?.data.every((point, i, arr) => { + if (domainX === point.x) { + domainY = point.y; + return false; + } + if (domainX > point.x && !!arr[i + 1] && domainX < arr[i + 1]!.x) { + const next = arr[i + 1]!; + const a = (point.y - next.y) / (point.x - next.x); + const b = point.y - a * point.x; + domainY = a * domainX + b; + return false; + } + + return true; + }); + + if (onIndicatorChange) { + runOnJS(onIndicatorChange)({ x: domainY, y: domainX }); + } + }) + .onEnd(() => { + indicatorScale.value = withTiming(1, { duration: 200 }); + }); + + const indicatorDotStyles = useAnimatedStyle(() => ({ + transform: [ + { translateX: indicatorX.value }, + { scale: indicatorScale.value }, + ], + })); + + const indicatorLineStyles = useAnimatedStyle(() => ({ + transform: [{ translateX: indicatorX.value }], + })); + + const [showIndicator, setShowIndicator] = useState(false); + const tap = Gesture.Tap().onStart(() => { + runOnJS(setShowIndicator)(!showIndicator); + }); + return ( - - - - {paths} - - - - - - + <> + + + + + + {paths} + + + + + + + + + {showIndicator ? ( + + + + + + + ) : null} + ); }; +const style = StyleSheet.create({ + container: { + position: 'absolute', + }, + indicator: { + position: 'absolute', + width: 30, + height: 30, + borderRadius: 15, + backgroundColor: 'red', + }, + line: { + position: 'absolute', + left: 14, + width: 2, + height: 300 - 30, + backgroundColor: 'red', + }, +}); + export default LineChart; diff --git a/src/LineChart/types.ts b/src/LineChart/types.ts index 6230278..0219b30 100644 --- a/src/LineChart/types.ts +++ b/src/LineChart/types.ts @@ -1,3 +1,5 @@ +import type { SkPoint } from '@shopify/react-native-skia'; + import type { XAxisConfig, YAxisConfig } from '../core/Axes/types'; import type { ArrowVariant } from '../core/AxisArrow/types'; import type { GridlinesConfig } from '../core/Gridlines/types'; @@ -28,4 +30,5 @@ export interface LineChartProps extends ChartBaseProps { children?: LineChartChildren; xAxis?: XAxisConfig; yAxis?: YAxisConfig; + onIndicatorChange?(value: SkPoint): void; } diff --git a/yarn.lock b/yarn.lock index 28f95f4..2802c11 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2146,26 +2146,17 @@ resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf" integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w== -"@types/react-native@0.70.0": - version "0.70.0" - resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.70.0.tgz#f8cdcdd542d36467d7591585b93d27e0563676e0" - integrity sha512-yBN7qJDfs0Vwr34NyfW1SWzalHQoYtpUWf0t4UJY9C5ft58BRr46+r92I0v+l3QX4VNsSRMHVAAWqLLCbIkM+g== +"@types/react-native@0.71.3": + version "0.71.3" + resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.71.3.tgz#537f669ed6b38b5ae47444bd9d253c4cff23bed7" + integrity sha512-0Uqw1YZ0qbVla0MMWFTANFm6W8KYWNvGQmYfucdecbXivLMcQ2v4PovuYFKr7bE6Bc5nDCUEaga962Y8gcDF7A== dependencies: "@types/react" "*" -"@types/react@*", "@types/react@17.0.21": - version "17.0.21" - resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.21.tgz#069c43177cd419afaab5ce26bb4e9056549f7ea6" - integrity sha512-GzzXCpOthOjXvrAUFQwU/svyxu658cwu00Q9ugujS4qc1zXgLFaO0kS2SLOaMWLt2Jik781yuHCWB7UcYdGAeQ== - dependencies: - "@types/prop-types" "*" - "@types/scheduler" "*" - csstype "^3.0.2" - -"@types/react@~17.0.21": - version "17.0.53" - resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.53.tgz#10d4d5999b8af3d6bc6a9369d7eb953da82442ab" - integrity sha512-1yIpQR2zdYu1Z/dc1OxC+MA6GR240u3gcnP4l6mvj/PJiVaqHsQPmWttsvHsfnhfPbU2FuGmo0wSITPygjBmsw== +"@types/react@*", "@types/react@~18.2.0": + version "18.2.6" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.6.tgz#5cd53ee0d30ffc193b159d3516c8c8ad2f19d571" + integrity sha512-wRZClXn//zxCFW+ye/D2qY65UsYP1Fpex2YXorHc8awoNamkMZSvBxwxdYVInsHOZZd2Ppq8isnSzJL5Mpf8OA== dependencies: "@types/prop-types" "*" "@types/scheduler" "*"