From f983cd9101b91d1a64cc1deac5731a712ecad02f Mon Sep 17 00:00:00 2001 From: foyarash <11079152+foyarash@users.noreply.github.com> Date: Tue, 24 May 2022 22:49:25 +0200 Subject: [PATCH 1/2] Improve behavior for animated graph with gesture --- package.json | 3 ++- src/AnimatedLineGraph.tsx | 46 ++++++++++++++++++++++++++++----------- src/LineGraphProps.ts | 15 +++++++++++++ 3 files changed, 50 insertions(+), 14 deletions(-) diff --git a/package.json b/package.json index c30c744..2bde31e 100644 --- a/package.json +++ b/package.json @@ -126,7 +126,8 @@ "singleQuote": true, "tabWidth": 2, "trailingComma": "es5", - "useTabs": false + "useTabs": false, + "semi": false }, "react-native-builder-bob": { "source": "src", diff --git a/src/AnimatedLineGraph.tsx b/src/AnimatedLineGraph.tsx index 08f2bc4..a450d47 100644 --- a/src/AnimatedLineGraph.tsx +++ b/src/AnimatedLineGraph.tsx @@ -41,6 +41,9 @@ export function AnimatedLineGraph({ TopAxisLabel, BottomAxisLabel, selectionDotShadowColor, + gestureHoldDuration = 300, + initialIndex, + resetPositionOnRelease, ...props }: AnimatedLineGraphProps): React.ReactElement { const [width, setWidth] = useState(0) @@ -53,6 +56,7 @@ export function AnimatedLineGraph({ setWidth(Math.round(layout.width)) setHeight(Math.round(layout.height)) }, + // eslint-disable-next-line react-hooks/exhaustive-deps [] ) @@ -158,7 +162,9 @@ export function AnimatedLineGraph({ [interpolateProgress] ) - const { gesture, isActive, x } = useHoldOrPanGesture({ holdDuration: 300 }) + const { gesture, isActive, x } = useHoldOrPanGesture({ + holdDuration: gestureHoldDuration, + }) const circleX = useValue(0) const circleY = useValue(0) const pathEnd = useValue(0) @@ -193,17 +199,25 @@ export function AnimatedLineGraph({ damping: 50, velocity: 0, }) - if (!active) pathEnd.current = 1 + if (!active && resetPositionOnRelease) pathEnd.current = 1 if (active) onGestureStart?.() else onGestureEnd?.() }, - [circleRadius, onGestureEnd, onGestureStart, pathEnd] + [ + circleRadius, + onGestureEnd, + onGestureStart, + pathEnd, + resetPositionOnRelease, + ] ) useAnimatedReaction( - () => x.value, - (fingerX) => { - runOnJS(setFingerX)(fingerX) + () => [x.value, isActive.value], + ([fingerX, isActiveValue]) => { + if (isActiveValue) { + runOnJS(setFingerX)(fingerX as number) + } }, [isActive, setFingerX, width, x] ) @@ -215,16 +229,22 @@ export function AnimatedLineGraph({ [isActive, setIsActive] ) const positions = useDerivedValue( - () => [ - 0, - Math.min(0.15, pathEnd.current), - pathEnd.current, - pathEnd.current, - 1, - ], + () => [0, pathEnd.current, pathEnd.current, pathEnd.current, 1], [pathEnd] ) + useEffect(() => { + if ( + typeof initialIndex === 'number' && + initialIndex < points.length && + width + ) { + const xForIndex = Math.round((initialIndex * width) / points.length) + setFingerX(xForIndex) + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [points, initialIndex, width]) + return ( diff --git a/src/LineGraphProps.ts b/src/LineGraphProps.ts index bdfe411..16a6cc4 100644 --- a/src/LineGraphProps.ts +++ b/src/LineGraphProps.ts @@ -62,6 +62,21 @@ export type AnimatedLineGraphProps = BaseLineGraphProps & { * The element that gets rendered below the Graph (usually the "min" point/value of the Graph) */ BottomAxisLabel?: () => React.ReactElement | null + + /** + * Hold duration for the graph gesture + */ + gestureHoldDuration?: number + + /** + * Initial index shown on the graph + */ + initialIndex?: number + + /** + * Wether to reset or not the circle position when releasing the gesture + */ + resetPositionOnRelease?: boolean } export type LineGraphProps = From bab51f36137c27d75dbcca9e45d109abdc181f56 Mon Sep 17 00:00:00 2001 From: foyarash <11079152+foyarash@users.noreply.github.com> Date: Fri, 27 May 2022 13:42:11 +0200 Subject: [PATCH 2/2] Remove initial index, improve onPointSelected execution --- src/AnimatedLineGraph.tsx | 42 +++++++++++++++++++++------------------ src/LineGraphProps.ts | 5 ----- 2 files changed, 23 insertions(+), 24 deletions(-) diff --git a/src/AnimatedLineGraph.tsx b/src/AnimatedLineGraph.tsx index a450d47..e573609 100644 --- a/src/AnimatedLineGraph.tsx +++ b/src/AnimatedLineGraph.tsx @@ -14,6 +14,7 @@ import { Group, Shadow, PathCommand, + useValueEffect, } from '@shopify/react-native-skia' import type { AnimatedLineGraphProps } from './LineGraphProps' import { createGraphPath } from './CreateGraphPath' @@ -42,8 +43,7 @@ export function AnimatedLineGraph({ BottomAxisLabel, selectionDotShadowColor, gestureHoldDuration = 300, - initialIndex, - resetPositionOnRelease, + resetPositionOnRelease = true, ...props }: AnimatedLineGraphProps): React.ReactElement { const [width, setWidth] = useState(0) @@ -56,7 +56,6 @@ export function AnimatedLineGraph({ setWidth(Math.round(layout.width)) setHeight(Math.round(layout.height)) }, - // eslint-disable-next-line react-hooks/exhaustive-deps [] ) @@ -174,6 +173,15 @@ export function AnimatedLineGraph({ [circleRadius] ) + useValueEffect(pathEnd, (pathEndValue) => { + const index = Math.round(pathEndValue * points.length) + const pointIndex = Math.min(Math.max(index, 0), points.length - 1) + const dataPoint = points[Math.round(pointIndex)] + if (dataPoint != null && onPointSelected) { + runOnJS(onPointSelected)(dataPoint) + } + }) + const setFingerX = useCallback( (fingerX: number) => { const y = getYForX(commands.current, fingerX) @@ -183,13 +191,8 @@ export function AnimatedLineGraph({ circleX.current = fingerX } pathEnd.current = fingerX / width - - const index = Math.round((fingerX / width) * points.length) - const pointIndex = Math.min(Math.max(index, 0), points.length - 1) - const dataPoint = points[Math.round(pointIndex)] - if (dataPoint != null) onPointSelected?.(dataPoint) }, - [circleX, circleY, onPointSelected, pathEnd, points, width] + [circleX, circleY, pathEnd, width] ) const setIsActive = useCallback( (active: boolean) => { @@ -229,21 +232,22 @@ export function AnimatedLineGraph({ [isActive, setIsActive] ) const positions = useDerivedValue( - () => [0, pathEnd.current, pathEnd.current, pathEnd.current, 1], + () => [ + 0, + Math.min(0.15, pathEnd.current), + pathEnd.current, + pathEnd.current, + pathEnd.current, + 1, + ], [pathEnd] ) useEffect(() => { - if ( - typeof initialIndex === 'number' && - initialIndex < points.length && - width - ) { - const xForIndex = Math.round((initialIndex * width) / points.length) - setFingerX(xForIndex) + if (width != null) { + setFingerX(width) } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [points, initialIndex, width]) + }, [width, setFingerX]) return ( diff --git a/src/LineGraphProps.ts b/src/LineGraphProps.ts index 16a6cc4..9a3a898 100644 --- a/src/LineGraphProps.ts +++ b/src/LineGraphProps.ts @@ -68,11 +68,6 @@ export type AnimatedLineGraphProps = BaseLineGraphProps & { */ gestureHoldDuration?: number - /** - * Initial index shown on the graph - */ - initialIndex?: number - /** * Wether to reset or not the circle position when releasing the gesture */