Open phantom-factotum opened 6 months ago
I took a look at the source code and useAnimatedGestureHandler is deprecated. While you could use useEvent and useHandler to recreate useAnimatedGestureHandler; using Gesure.Pan in combination with GestureDetector is easier:
import * as React from 'react';
import { StyleSheet, ViewStyle } from 'react-native';
import Animated, {
useAnimatedStyle,
withSpring,
withTiming,
runOnJS,
useSharedValue,
} from 'react-native-reanimated';
import {
GestureDetector,
Gesture,
GestureHandlerRootView,
} 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,
step = 1,
width = 350,
height = 30,
borderRadius = 5,
maximumTrackTintColor = '#3F2DA5',
minimumTrackTintColor = '#77ADE6',
disabled = false,
onChange = () => {},
onComplete = () => {},
value: currentValue = 0,
showIndicator = false,
renderIndicatorHeight = 40,
renderIndicator = () => null,
containerStyle = {},
sliderStyle = {},
}: SliderProps) => {
let point = useSharedValue<number>(currentValue);
const gesture = Gesture.Pan()
.onStart((evt) => {
if (disabled) return;
let value = calculateValue(evt.y, min, max, step, height);
point.value = withSpring(value, {
damping: 13,
});
})
.onChange((evt) => {
if (disabled) return;
let value = calculateValue(evt.y, min, max, step, height);
point.value = withTiming(value, { duration: 50 });
runOnJS(onChange)(value);
})
.onEnd((evt) => {
if (disabled) return;
runOnJS(onComplete)(calculateValue(evt.y, min, max, step, height));
});
// All the dynamic style calculations
const baseViewStyle = useAnimatedStyle<ViewStyle>(
() => ({
width,
height,
borderRadius,
backgroundColor: maximumTrackTintColor,
}),
[point.value, maximumTrackTintColor, width, height, borderRadius]
);
// slider style
const sliderBaseStyle = useAnimatedStyle<ViewStyle>(
() => ({
// Convert the value to height in number
height: `${(point.value / max) * 100}%`,
backgroundColor: minimumTrackTintColor,
borderBottomLeftRadius: borderRadius,
borderBottomRightRadius: borderRadius,
// if percentage > 97 , then show border top right anf left radius
...((point.value / max) * 100 > 97
? {
borderTopLeftRadius: borderRadius,
borderTopRightRadius: borderRadius,
}
: {
borderTopLeftRadius: 0,
borderTopRightRadius: 0,
}),
}),
[point.value, minimumTrackTintColor, borderRadius, height, max]
);
// indicator style
const indicatorStyle = useAnimatedStyle<ViewStyle>(() => {
if (!showIndicator) return {};
let bottom = (point.value / max) * height;
// Adjust the bottom value not to go beyond the height or less than 0
bottom = Math.min(Math.max(bottom, 0), height - renderIndicatorHeight);
return {
bottom,
};
}, [showIndicator, point.value, sliderBaseStyle]);
return (
<GestureHandlerRootView>
<GestureDetector gesture={gesture}>
<Animated.View style={[baseViewStyle, containerStyle]}>
<Animated.View
style={[styles.slider, sliderBaseStyle, sliderStyle]}
/>
<Animated.View style={[styles.slider, indicatorStyle]}>
{renderIndicator(point.value)}
</Animated.View>
</Animated.View>
</GestureDetector>
</GestureHandlerRootView>
);
};
const styles = StyleSheet.create({
slider: {
position: 'absolute',
bottom: 0,
borderRadius: 0,
width: '100%',
},
});
export default VerticalSlider;
No matter what I do onComplete is never called when the gesture ends:
https://snack.expo.dev/g8-IGyiOx73T5kzMxqjNJ?platform=web