Skip to content

Commit

Permalink
feat: enhance slider with dynamic value calculation and styling
Browse files Browse the repository at this point in the history
Refactor the slider component to improve its functionality and user interface. The value
is now determined based on the slider's position, minimum and maximum limits, step increment,
and the component's height. Gesture interactions are smoother thanks to the use of `withSpring`
and `withTiming` animations. Additionally, the track's color changes dynamically to reflect
the current value, providing a more intuitive and visually engaging user experience.
  • Loading branch information
Aswin Lakshmanan committed Nov 25, 2023
1 parent 17954bd commit 99b1f0b
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 29 deletions.
32 changes: 26 additions & 6 deletions example/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@ const renderIcon = (newVal: number) => {
color: '#FFFBF5',
};
if (newVal > 75) {
styles.name = 'heart-circle-outline';
styles.name = 'star';
} else if (newVal > 50) {
styles.name = 'help-circle-outline';
styles.name = 'ios-happy';
} else {
styles.name = 'heart-dislike-circle-outline';
styles.name = 'ios-sad';
}
return styles;
// eslint-disable-next-line react-hooks/exhaustive-deps
Expand All @@ -35,6 +35,26 @@ const renderIcon = (newVal: number) => {

const App: React.FC = () => {
const [value, setValue] = React.useState(1);
// Calculating color change based on value
const calculateColors = () => {
let minimumTrackTintColor = '#f3636b';
let maximumTrackTintColor = '#18122B';
if (value > 66) {
minimumTrackTintColor = '#48cbae';
} else if (value > 33) {
minimumTrackTintColor = '#f7d033';
}
return { minimumTrackTintColor, maximumTrackTintColor };
};
// Helper functions
const onChangeValue = (newValue: number) => {
console.log('🚀 ~ file: App.tsx:51 ~ onChangeValue ~ value:', value);
setValue(newValue);
};
const { maximumTrackTintColor, minimumTrackTintColor } = React.useMemo(
calculateColors,
[value]
);
return (
<GestureHandlerRootView style={styles.flexOne}>
<View style={styles.container}>
Expand All @@ -43,7 +63,7 @@ const App: React.FC = () => {
disabled={false}
min={0}
max={100}
onChange={setValue}
onChange={onChangeValue}
onComplete={(newValue: number) => {
console.log('COMPLETE', newValue);
}}
Expand All @@ -53,8 +73,8 @@ const App: React.FC = () => {
height={300}
step={1}
borderRadius={5}
maximumTrackTintColor="#18122B"
minimumTrackTintColor="#635985"
maximumTrackTintColor={maximumTrackTintColor}
minimumTrackTintColor={minimumTrackTintColor}
renderIndicator={renderIcon}
/>
</View>
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
"react": "18.1.0",
"react-native": "0.70.5",
"react-native-builder-bob": "^0.20.0",
"react-native-gesture-handler": "^2.14.0",
"react-native-reanimated": "^2.14.4",
"release-it": "^15.0.0",
"typescript": "^4.5.2"
Expand Down
43 changes: 20 additions & 23 deletions src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,32 @@
import * as React from 'react';
import { StyleSheet, ViewStyle } from 'react-native';
import Animated, {
useSharedValue,
useAnimatedGestureHandler,
useAnimatedStyle,
withSpring,
withTiming,
runOnJS,
useSharedValue,
} from 'react-native-reanimated';
import { PanGestureHandler } from 'react-native-gesture-handler';
import type { SliderProps } from './slider.types';

const calculateValue = (
position: number,
min: number,
max: number,
step: number,
height: number
): number => {
'worklet';
let sliderPosition = height - position;
sliderPosition = Math.min(Math.max(sliderPosition, 0), height);
let value = (sliderPosition / height) * (max - min) + min;
value = Math.round(value / step) * step;
value = Math.min(Math.max(value, min), max);
return value;
};

const VerticalSlider: React.FC<SliderProps> = ({
min = 0,
max = 100,
Expand All @@ -30,44 +46,25 @@ const VerticalSlider: React.FC<SliderProps> = ({
containerStyle = {},
sliderStyle = {},
}: SliderProps) => {
// Calculate the value of the slider
const calculateValue = (position: number): number => {
'worklet';
// Opposite of the current position
let sliderPosition = height - position;
// clamp the value between 0 and height
sliderPosition = Math.min(Math.max(sliderPosition, 0), height);
// Calculate the value of the slider
let value = (sliderPosition / height) * (max - min) + min;
// Round the value based on the step value and min and max
value = Math.round(value / step) * step;
// Clamp the value between min and max
value = Math.min(Math.max(value, min), max);
return value;
};
let point = useSharedValue<number>(currentValue);
// Gesture event handler
const gestureEvent = useAnimatedGestureHandler({
onStart: (evt, _ctx) => {
if (disabled) return;
let value = calculateValue(evt.y);
let value = calculateValue(evt.y, min, max, step, height);
point.value = withSpring(value, {
damping: 13,
});
},
onActive: (evt, _ctx) => {
if (disabled) return;
let value = calculateValue(evt.y);
let value = calculateValue(evt.y, min, max, step, height);
point.value = withTiming(value, { duration: 50 });
runOnJS(onChange)(value);
},
onEnd: (evt, _ctx) => {
if (disabled) return;
runOnJS(onComplete)(calculateValue(evt.y));
},
onFinish: (evt, _ctx) => {
if (disabled) return;
runOnJS(onComplete)(calculateValue(evt.y));
runOnJS(onComplete)(calculateValue(evt.y, min, max, step, height));
},
});
// All the dynamic style calculations
Expand Down

0 comments on commit 99b1f0b

Please sign in to comment.