From e221041e34a4cfddf5f60dba6073e36d09954ba6 Mon Sep 17 00:00:00 2001 From: kiro-tagama Date: Sat, 10 Aug 2024 16:15:20 -0300 Subject: [PATCH 1/7] package config --- package.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/package.json b/package.json index 395c942..1e788cc 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "nativewind": "^4.0.1", "react": "18.2.0", "react-native": "0.73.6", + "react-native-gesture-handler": "^2.18.1", "react-native-reanimated": "~3.6.2", "react-native-svg": "^14.1.0", "react-native-vector-icons": "^10.0.3", @@ -30,6 +31,7 @@ }, "devDependencies": { "@babel/core": "^7.23.9", + "@types/lodash.debounce": "^4.0.9", "@trivago/prettier-plugin-sort-imports": "^4.3.0", "@types/react": "~18.2.55", "@types/react-native-vector-icons": "^6.4.18", From 3b538514a04bbb43bafec80a054e1a6e875829c4 Mon Sep 17 00:00:00 2001 From: kiro-tagama Date: Sat, 10 Aug 2024 16:26:50 -0300 Subject: [PATCH 2/7] up --- App.tsx | 17 +++++- components/Slider.tsx | 137 ++++++++++++++++++++++++++++++++++++++++++ package.json | 3 +- 3 files changed, 155 insertions(+), 2 deletions(-) create mode 100644 components/Slider.tsx diff --git a/App.tsx b/App.tsx index 8f6cf70..4ebbdef 100644 --- a/App.tsx +++ b/App.tsx @@ -1,6 +1,6 @@ /* eslint-disable prettier/prettier */ import { CircleUser, CreditCard, Settings } from 'lucide-react-native'; -import { useState } from 'react'; +import React, { useState } from 'react'; import { Alert, ScrollView, Text, TouchableOpacity, View } from 'react-native'; import { Avatar, AvatarFallback, AvatarImage } from './components/Avatar'; @@ -32,6 +32,7 @@ import { RadioGroupLabel, } from './components/RadioGroup'; import { Skeleton } from './components/Skeleton'; +import { Slider } from './components/Slider'; import { Switch } from './components/Switch'; import { Tabs, TabsContent, TabsList, TabsTrigger } from './components/Tabs'; import { ToastProvider, ToastVariant, useToast } from './components/Toast'; @@ -41,6 +42,8 @@ export default function App() { const [inputText, onChangeText] = useState(''); const [isEnabled, setIsEnabled] = useState(false); + const [sliderValue, setSliderValeu] = useState(50); + return ( @@ -261,6 +264,18 @@ export default function App() { + + Slider + + setSliderValeu(v)} + // thumbVisible={false} + /> + + diff --git a/components/Slider.tsx b/components/Slider.tsx new file mode 100644 index 0000000..b70ae77 --- /dev/null +++ b/components/Slider.tsx @@ -0,0 +1,137 @@ +import debounce from 'lodash.debounce'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import { Text, View } from 'react-native'; +import { + Gesture, + GestureDetector, + GestureHandlerRootView, +} from 'react-native-gesture-handler'; +import Animated, { + useAnimatedStyle, + useSharedValue, +} from 'react-native-reanimated'; + +import { cn } from '../lib/utils'; + +interface SliderProps { + minimumValue: number; + maximumValue: number; + value: number; + onValueChange?: (value: number) => void; + thumbVisible?: boolean; +} + +function Slider({ + value, + onValueChange, + minimumValue = 0, + maximumValue = 100, + thumbVisible = true, +}: SliderProps) { + const [sliderWidth, setSliderWidth] = useState(0); + + const calcPosition = useMemo(() => { + return (v: number) => + (sliderWidth * (v - minimumValue)) / (maximumValue - minimumValue); + }, [sliderWidth, maximumValue, minimumValue]); + + // Shared values para animação e estado do gesto + const translationX = useSharedValue(0); + const prevTranslationX = useSharedValue(0); + const isDragging = useSharedValue(false); + + useEffect(() => { + translationX.value = calcPosition(value); + }, [value, calcPosition]); + + const animatedStyles = useAnimatedStyle(() => ({ + transform: [{ translateX: translationX.value }], + })); + const sizeAnimatedStyles = useAnimatedStyle(() => ({ + width: translationX.value, + })); + + // Debounce para a função de mudança de valor + const debounceOnValueChange = useCallback( + debounce((value: number) => { + if (onValueChange) onValueChange(value); + }, 100), + [onValueChange] + ); + + useEffect(() => { + return () => debounceOnValueChange.cancel(); + }, [debounceOnValueChange]); + + const panGesture = useMemo( + () => + Gesture.Pan() + .minDistance(1) + .onStart(() => { + prevTranslationX.value = calcPosition(value); + isDragging.value = true; + }) + .onUpdate(event => { + const positionValue = prevTranslationX.value + event.translationX; + translationX.value = Math.min( + Math.max(positionValue, 0), + sliderWidth + ); + const calcReturn = + ((maximumValue - minimumValue) * translationX.value) / sliderWidth; + + if (onValueChange) debounceOnValueChange(calcReturn); + }) + .onEnd(() => { + isDragging.value = false; + }) + .runOnJS(true), + [calcPosition, sliderWidth, value, debounceOnValueChange] + ); + + return ( + <> + + setSliderWidth(e.nativeEvent.layout.width)} + className={cn( + 'mx-3 h-2 rounded-xl w-auto bg-foreground/20 justify-center' + )} + > + + + + + + + {/* Debugging info - remove or hide in production */} + + sliderWidth:{sliderWidth.toFixed(2)} + value:{value.toFixed(2)} + max:{maximumValue} + translationX.value:{translationX.value.toFixed(2)} + prevTranslationX.value:{prevTranslationX.value.toFixed(2)} + + + ); +} + +export { Slider }; diff --git a/package.json b/package.json index 1e788cc..a92742d 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "nativewind": "^4.0.1", "react": "18.2.0", "react-native": "0.73.6", - "react-native-gesture-handler": "^2.18.1", + "react-native-gesture-handler": "~2.16.1", "react-native-reanimated": "~3.6.2", "react-native-svg": "^14.1.0", "react-native-vector-icons": "^10.0.3", @@ -33,6 +33,7 @@ "@babel/core": "^7.23.9", "@types/lodash.debounce": "^4.0.9", "@trivago/prettier-plugin-sort-imports": "^4.3.0", + "@types/lodash.debounce": "^4.0.9", "@types/react": "~18.2.55", "@types/react-native-vector-icons": "^6.4.18", "eslint": "^8.56.0", From a46bc27c258a03af6ec9a1d6ed3a06070cf56c0e Mon Sep 17 00:00:00 2001 From: kiro-tagama Date: Sat, 10 Aug 2024 21:03:20 -0300 Subject: [PATCH 3/7] base --- components/Slider.tsx | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/components/Slider.tsx b/components/Slider.tsx index b70ae77..446fbe6 100644 --- a/components/Slider.tsx +++ b/components/Slider.tsx @@ -35,7 +35,6 @@ function Slider({ (sliderWidth * (v - minimumValue)) / (maximumValue - minimumValue); }, [sliderWidth, maximumValue, minimumValue]); - // Shared values para animação e estado do gesto const translationX = useSharedValue(0); const prevTranslationX = useSharedValue(0); const isDragging = useSharedValue(false); @@ -44,14 +43,6 @@ function Slider({ translationX.value = calcPosition(value); }, [value, calcPosition]); - const animatedStyles = useAnimatedStyle(() => ({ - transform: [{ translateX: translationX.value }], - })); - const sizeAnimatedStyles = useAnimatedStyle(() => ({ - width: translationX.value, - })); - - // Debounce para a função de mudança de valor const debounceOnValueChange = useCallback( debounce((value: number) => { if (onValueChange) onValueChange(value); @@ -89,6 +80,13 @@ function Slider({ [calcPosition, sliderWidth, value, debounceOnValueChange] ); + const animatedStyles = useAnimatedStyle(() => ({ + transform: [{ translateX: translationX.value }], + })); + const sizeAnimatedStyles = useAnimatedStyle(() => ({ + width: translationX.value, + })); + return ( <> Date: Sat, 10 Aug 2024 22:20:52 -0300 Subject: [PATCH 4/7] update animaation and calcs --- App.tsx | 2 +- components/Slider.tsx | 59 +++++++++++++++++++++++-------------------- 2 files changed, 33 insertions(+), 28 deletions(-) diff --git a/App.tsx b/App.tsx index 4ebbdef..36249d2 100644 --- a/App.tsx +++ b/App.tsx @@ -258,7 +258,7 @@ export default function App() { - + Progress diff --git a/components/Slider.tsx b/components/Slider.tsx index 446fbe6..3b68ea2 100644 --- a/components/Slider.tsx +++ b/components/Slider.tsx @@ -21,6 +21,9 @@ interface SliderProps { thumbVisible?: boolean; } +const clamp = (value: number, min: number, max: number) => + Math.max(min, Math.min(value, max)); + function Slider({ value, onValueChange, @@ -30,19 +33,16 @@ function Slider({ }: SliderProps) { const [sliderWidth, setSliderWidth] = useState(0); - const calcPosition = useMemo(() => { - return (v: number) => - (sliderWidth * (v - minimumValue)) / (maximumValue - minimumValue); - }, [sliderWidth, maximumValue, minimumValue]); + const calcPosition = useCallback( + (v: number) => + (sliderWidth * (v - minimumValue)) / (maximumValue - minimumValue), + [sliderWidth, minimumValue, maximumValue] + ); - const translationX = useSharedValue(0); + const translationX = useSharedValue(calcPosition(value)); const prevTranslationX = useSharedValue(0); const isDragging = useSharedValue(false); - useEffect(() => { - translationX.value = calcPosition(value); - }, [value, calcPosition]); - const debounceOnValueChange = useCallback( debounce((value: number) => { if (onValueChange) onValueChange(value); @@ -51,41 +51,45 @@ function Slider({ ); useEffect(() => { + translationX.value = calcPosition(value); return () => debounceOnValueChange.cancel(); - }, [debounceOnValueChange]); + }, [value, calcPosition, debounceOnValueChange]); const panGesture = useMemo( () => Gesture.Pan() - .minDistance(1) + .minDistance(0) .onStart(() => { prevTranslationX.value = calcPosition(value); isDragging.value = true; }) - .onUpdate(event => { + .onUpdate(async event => { const positionValue = prevTranslationX.value + event.translationX; - translationX.value = Math.min( - Math.max(positionValue, 0), - sliderWidth - ); - const calcReturn = - ((maximumValue - minimumValue) * translationX.value) / sliderWidth; - - if (onValueChange) debounceOnValueChange(calcReturn); + const clampedPosition = clamp(positionValue, 0, sliderWidth); + translationX.value = clampedPosition; }) - .onEnd(() => { + .onEnd(async () => { isDragging.value = false; + const calcReturn = + ((maximumValue - minimumValue) * translationX.value) / sliderWidth; + if (onValueChange) await debounceOnValueChange(calcReturn); }) .runOnJS(true), [calcPosition, sliderWidth, value, debounceOnValueChange] ); - const animatedStyles = useAnimatedStyle(() => ({ - transform: [{ translateX: translationX.value }], - })); - const sizeAnimatedStyles = useAnimatedStyle(() => ({ - width: translationX.value, - })); + const animatedStyles = useAnimatedStyle( + () => ({ + transform: [{ translateX: translationX.value }], + }), + [translationX] + ); + const sizeAnimatedStyles = useAnimatedStyle( + () => ({ + width: translationX.value, + }), + [translationX] + ); return ( <> @@ -93,6 +97,7 @@ function Slider({ style={{ backgroundColor: 'red', height: 20, + paddingVertical: 18, justifyContent: 'center', }} > From 99e9e3568114a41e547b5cc65b5a291f004c5d8f Mon Sep 17 00:00:00 2001 From: kiro-tagama Date: Sun, 11 Aug 2024 12:26:58 -0300 Subject: [PATCH 5/7] slider beta 1 --- components/Slider.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/components/Slider.tsx b/components/Slider.tsx index 3b68ea2..4b1173f 100644 --- a/components/Slider.tsx +++ b/components/Slider.tsx @@ -95,7 +95,6 @@ function Slider({ <> {/* Debugging info - remove or hide in production */} - + {/* sliderWidth:{sliderWidth.toFixed(2)} value:{value.toFixed(2)} max:{maximumValue} translationX.value:{translationX.value.toFixed(2)} prevTranslationX.value:{prevTranslationX.value.toFixed(2)} - + */} ); } From fd75ede22e87fba3390ef554b9b687446d3ff540 Mon Sep 17 00:00:00 2001 From: kiro-tagama Date: Sun, 18 Aug 2024 22:23:49 -0300 Subject: [PATCH 6/7] remove comments --- components/Slider.tsx | 60 ++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 35 deletions(-) diff --git a/components/Slider.tsx b/components/Slider.tsx index 4b1173f..488e2b8 100644 --- a/components/Slider.tsx +++ b/components/Slider.tsx @@ -1,6 +1,6 @@ import debounce from 'lodash.debounce'; import React, { useCallback, useEffect, useMemo, useState } from 'react'; -import { Text, View } from 'react-native'; +import { View } from 'react-native'; import { Gesture, GestureDetector, @@ -92,47 +92,37 @@ function Slider({ ); return ( - <> - + setSliderWidth(e.nativeEvent.layout.width)} + className={cn( + 'mx-3 h-2 rounded-xl w-auto bg-foreground/20 justify-center' + )} > - setSliderWidth(e.nativeEvent.layout.width)} + + /> + - - - - - - {/* Debugging info - remove or hide in production */} - {/* - sliderWidth:{sliderWidth.toFixed(2)} - value:{value.toFixed(2)} - max:{maximumValue} - translationX.value:{translationX.value.toFixed(2)} - prevTranslationX.value:{prevTranslationX.value.toFixed(2)} - */} - + + + ); } From dc89a9119c06faf7d64d18c4a7a6c965e8fc897b Mon Sep 17 00:00:00 2001 From: kiro-tagama Date: Sun, 18 Aug 2024 22:59:26 -0300 Subject: [PATCH 7/7] edited value of prevTranslation --- components/Slider.tsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/components/Slider.tsx b/components/Slider.tsx index 488e2b8..d4c8a90 100644 --- a/components/Slider.tsx +++ b/components/Slider.tsx @@ -40,7 +40,7 @@ function Slider({ ); const translationX = useSharedValue(calcPosition(value)); - const prevTranslationX = useSharedValue(0); + const prevTranslationX = useSharedValue(translationX.value); const isDragging = useSharedValue(false); const debounceOnValueChange = useCallback( @@ -52,6 +52,7 @@ function Slider({ useEffect(() => { translationX.value = calcPosition(value); + prevTranslationX.value = translationX.value; return () => debounceOnValueChange.cancel(); }, [value, calcPosition, debounceOnValueChange]); @@ -60,10 +61,10 @@ function Slider({ Gesture.Pan() .minDistance(0) .onStart(() => { - prevTranslationX.value = calcPosition(value); + prevTranslationX.value = translationX.value; isDragging.value = true; }) - .onUpdate(async event => { + .onUpdate(event => { const positionValue = prevTranslationX.value + event.translationX; const clampedPosition = clamp(positionValue, 0, sliderWidth); translationX.value = clampedPosition; @@ -114,7 +115,7 @@ function Slider({ />