diff --git a/example/src/App.tsx b/example/src/App.tsx index d2d5d17..9956210 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -11,7 +11,7 @@ export default function App() { ; - gesture: PanGesture; -} - -export function usePanGesture({ enabled }: Config): Result { - const isPanGestureActive = useSharedValue(false); - - /** - * la gesture viene gestita dal thread js - * https://reactnative.dev/architecture/threading-model - */ - const panGesture = useMemo(() => { - // console.log(_WORKLET); - - return Gesture.Pan() - .runOnJS(true) - .enabled(enabled) - .onTouchesDown(() => (isPanGestureActive.value = true)) - .onTouchesUp(() => (isPanGestureActive.value = false)) - .onStart(() => { - isPanGestureActive.value = true; - }) - .onEnd(() => { - isPanGestureActive.value = false; - }); - }, [enabled, isPanGestureActive]); - - return useMemo( - () => ({ - gesture: panGesture, - isActive: isPanGestureActive, - }), - [isPanGestureActive, panGesture] - ); -} diff --git a/src/components/rheostat/double.tsx b/src/components/rheostat/double.tsx index c09c24e..0c1d1b4 100644 --- a/src/components/rheostat/double.tsx +++ b/src/components/rheostat/double.tsx @@ -1,6 +1,10 @@ -import React, { useMemo } from 'react'; +import React, { useCallback, useMemo } from 'react'; -import { Gesture, GestureDetector } from 'react-native-gesture-handler'; +import { + Gesture, + GestureDetector, + type GestureTouchEvent, +} from 'react-native-gesture-handler'; import { useDerivedValue, useSharedValue } from 'react-native-reanimated'; import { Canvas, Path, Skia } from '@shopify/react-native-skia'; @@ -10,19 +14,15 @@ import { DOT_MAGNETIC_AREA, } from './constant'; import type { BaseRheostatProps } from './types'; -import { getPosition, getValue } from './utils'; +import { getPosition, getValue, whichIsActive } from './utils'; import { SkiaDot } from '../skiaDot/skiaDot'; function DoubleRheostat({ enabled = true, - width, - height, - values: inputValues, - data, - onValuesUpdated, horizontalPadding = DOT_DEFAULT_RADIUS, ...props }: BaseRheostatProps) { + const { width, values: inputValues, height, data, onValuesUpdated } = props; const startX = useMemo(() => horizontalPadding, [horizontalPadding]); const endX = useMemo( () => width - horizontalPadding, @@ -35,6 +35,7 @@ function DoubleRheostat({ return p; }, [endX, height, startX]); + /** * DOT 1 */ @@ -69,36 +70,35 @@ function DoubleRheostat({ return p; }, [dot1ValuePosition.value, dot2ValuePosition.value, height]); - const isGestureActive = useSharedValue(undefined); - const gesture = Gesture.Pan() - .runOnJS(true) - .enabled(enabled) - .onTouchesDown((event) => { + /** + * -1 = nessuna gesture riconosciuta e gestita + */ + const isGestureActive = useSharedValue(-1); + const trackTouchDown = useCallback( + (event: GestureTouchEvent) => { const touch = event.changedTouches[0]?.x; + if (touch) { - const distance1 = Math.abs(touch - dot1ValuePosition.value); - const distance2 = Math.abs(touch - dot2ValuePosition.value); - - if (distance1 < DOT_MAGNETIC_AREA && distance2 < DOT_MAGNETIC_AREA) { - if (distance1 < distance2) { - isGestureActive.value = 0; - } else { - isGestureActive.value = 1; - } - - return; - } else { - if (distance1 < DOT_MAGNETIC_AREA) { - isGestureActive.value = 0; - } else if (distance2 < DOT_MAGNETIC_AREA) { - isGestureActive.value = 1; - } - } + isGestureActive.value = whichIsActive( + touch, + dot1ValuePosition, + dot2ValuePosition + ); } - }) - .onTouchesUp(() => (isGestureActive.value = undefined)) + }, + [dot1ValuePosition, dot2ValuePosition, isGestureActive] + ); + const trackTouchUp = useCallback( + () => (isGestureActive.value = -1), + [isGestureActive] + ); + const gesture = Gesture.Pan() + .runOnJS(true) + .enabled(enabled) + .onTouchesDown(trackTouchDown) + .onTouchesUp(trackTouchUp) .onChange((event) => { - if (isGestureActive.value === undefined) return; + if (isGestureActive.value === -1) return; if (onValuesUpdated) { onValuesUpdated({ @@ -125,6 +125,11 @@ function DoubleRheostat({ dot2ValuePosition.value = event.x; } } + }) + .onEnd(() => { + if (props.onSliderDragEnd) { + props.onSliderDragEnd(); + } }); return ( diff --git a/src/components/rheostat/index.tsx b/src/components/rheostat/index.tsx index ec42c47..d0d3d64 100644 --- a/src/components/rheostat/index.tsx +++ b/src/components/rheostat/index.tsx @@ -34,26 +34,23 @@ function RheostatImpl({ ); const checkedValues = useMemo(() => { - try { - if (double) { - if (values.length < 2) { - throw new Error('È necessario definire due valori'); - } else if (values.length > 2) { - return values.slice(0, 2); - } - } else { - if (values.length === 0) { - throw new Error('È necessario definire un valore'); - } else if (values.length > 1) { - return values.slice(0, 1); - } + if (double) { + if (values.length < 2) { + console.warn('È necessario definire due valori'); + return []; + } else if (values.length > 2) { + return values.slice(0, 2); + } + } else { + if (values.length === 0) { + console.warn('È necessario definire un valore'); + return []; + } else if (values.length > 1) { + return values.slice(0, 1); } - - return values; - } catch (error) { - console.warn(error); - return []; } + + return values; }, [double, values]); const snapPoints = useMemo(() => { diff --git a/src/components/rheostat/single.tsx b/src/components/rheostat/single.tsx index abae7db..bfb3223 100644 --- a/src/components/rheostat/single.tsx +++ b/src/components/rheostat/single.tsx @@ -1,29 +1,24 @@ -import React, { useMemo } from 'react'; -import { View } from 'react-native'; +import React, { useCallback, useMemo } from 'react'; -import { Gesture, GestureDetector } from 'react-native-gesture-handler'; +import { + Gesture, + GestureDetector, + type GestureTouchEvent, +} from 'react-native-gesture-handler'; import { useDerivedValue, useSharedValue } from 'react-native-reanimated'; import type { BaseRheostatProps } from './types'; -import { getPosition, getValue } from './utils'; -import { - DOT_DEFAULT_COLOR, - DOT_DEFAULT_RADIUS, - DOT_MAGNETIC_AREA, -} from './constant'; +import { getPosition, getValue, whichIsActive } from './utils'; +import { DOT_DEFAULT_COLOR, DOT_DEFAULT_RADIUS } from './constant'; import { Canvas, Group, Path, Skia } from '@shopify/react-native-skia'; import { SkiaDot } from '../skiaDot/skiaDot'; function SingleRheostat({ enabled = true, - width, - values: inputValues, - height, - data, - onValuesUpdated, horizontalPadding = DOT_DEFAULT_RADIUS, ...props }: BaseRheostatProps) { + const { width, values: inputValues, height, data, onValuesUpdated } = props; const startX = useMemo(() => horizontalPadding, [horizontalPadding]); const endX = useMemo( () => width - horizontalPadding, @@ -56,22 +51,26 @@ function SingleRheostat({ return p; }, [startX, horizontalPadding, dotValuePosition, height]); - const isGestureActive = useSharedValue(undefined); - const gesture = Gesture.Pan() - .runOnJS(true) - .enabled(enabled) - .onTouchesDown((event) => { + const isGestureActive = useSharedValue(-1); + const trackTouchDown = useCallback( + (event: GestureTouchEvent) => { const touch = event.changedTouches[0]?.x; if (touch) { - const distance = Math.abs(touch - dotValuePosition.value); - - if (distance < DOT_MAGNETIC_AREA) { - isGestureActive.value = 0; - } + isGestureActive.value = whichIsActive(touch, dotValuePosition); } - }) - .onTouchesUp(() => (isGestureActive.value = undefined)) + }, + [dotValuePosition, isGestureActive] + ); + const trackTouchUp = useCallback( + () => (isGestureActive.value = -1), + [isGestureActive] + ); + const gesture = Gesture.Pan() + .runOnJS(true) + .enabled(enabled) + .onTouchesDown(trackTouchDown) + .onTouchesUp(trackTouchUp) .onChange((event) => { if (isGestureActive.value === undefined) return; @@ -91,42 +90,40 @@ function SingleRheostat({ }); return ( - - - - - - - - - - - + + + + + + + + + ); } diff --git a/src/components/rheostat/types.ts b/src/components/rheostat/types.ts index 7866efe..7a38d80 100644 --- a/src/components/rheostat/types.ts +++ b/src/components/rheostat/types.ts @@ -77,4 +77,5 @@ export interface RheostatProps { */ verticalPadding?: number; onValuesUpdated?: (state: HandlersState) => void; + onSliderDragEnd?: () => void; } diff --git a/src/components/rheostat/utils.ts b/src/components/rheostat/utils.ts index 4cd98c3..f712183 100644 --- a/src/components/rheostat/utils.ts +++ b/src/components/rheostat/utils.ts @@ -1,3 +1,6 @@ +import type { SharedValue } from 'react-native-reanimated'; +import { DOT_MAGNETIC_AREA } from './constant'; + export function getPosition( value: number, data: number[], @@ -23,5 +26,25 @@ export function getValue( const min = Math.min(...data); const max = Math.max(...data); - return Math.round(((pos - minRange) / (maxRange - minRange)) * (max - min) + min); + return Math.round( + ((pos - minRange) / (maxRange - minRange)) * (max - min) + min + ); +} + +export function whichIsActive(touch: number, ...dots: SharedValue[]) { + if (dots.length === 0) return -1; + + const touchDotsDistance = dots + .map((d, index) => { + return { index, distance: Math.abs(d.value - touch) }; + }) + .filter((d) => d.distance <= DOT_MAGNETIC_AREA); + + if (touchDotsDistance.length === 0) return -1; + + const minimumDistanceDot = touchDotsDistance.reduce((state, currentValue) => { + return state.distance < currentValue.distance ? state : currentValue; + }); + + return minimumDistanceDot.index; }