Improve slider animation performance

This commit is contained in:
Danish Ahmed Mirza 2022-06-16 03:19:28 +05:30 committed by Danish
parent 79975710e4
commit 497e631510
1 changed files with 40 additions and 31 deletions

View File

@ -1,11 +1,11 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { View, StyleProp, ViewStyle } from 'react-native'; import { View, StyleProp, ViewStyle } from 'react-native';
import { Gesture, GestureDetector } from 'react-native-gesture-handler'; import { Gesture, GestureDetector } from 'react-native-gesture-handler';
import Touchable from 'react-native-platform-touchable'; import Animated, { useSharedValue, useAnimatedStyle, interpolate, withTiming } from 'react-native-reanimated';
import Animated, { useSharedValue, useAnimatedStyle, interpolate } from 'react-native-reanimated';
import styles, { SLIDER_THUMB_RADIUS } from '../../styles'; import styles, { SLIDER_THUMB_RADIUS } from '../../styles';
import { useTheme } from '../../../../theme'; import { useTheme } from '../../../../theme';
// import { debounce } from '../../../../lib/methods/helpers';
interface ISliderProps { interface ISliderProps {
value: number; value: number;
@ -32,14 +32,15 @@ const Slider = React.memo(
}: ISliderProps) => { }: ISliderProps) => {
const { colors } = useTheme(); const { colors } = useTheme();
const [sliderWidth, setSliderWidth] = useState<number>(0); const [sliderWidth, setSliderWidth] = useState<number>(0);
const position = useSharedValue(0); const [isSliding, setSliding] = useState<boolean>(false);
const sliderThumbWidth = useSharedValue(2 * SLIDER_THUMB_RADIUS);
const currentValue = useSharedValue(0); const currentValue = useSharedValue(0);
useEffect(() => { useEffect(() => {
if (value !== currentValue.value) { if (!isSliding) {
currentValue.value = value; currentValue.value = value;
} }
}, [value]); }, [value, isSliding]);
const clamp = (value: number, min: number, max: number) => Math.min(Math.max(value, min), max); const clamp = (value: number, min: number, max: number) => Math.min(Math.max(value, min), max);
@ -49,20 +50,30 @@ const Slider = React.memo(
setSliderWidth(event.nativeEvent.layout.width); setSliderWidth(event.nativeEvent.layout.width);
}; };
const tapGesture = Gesture.Tap().onStart(e => { // const debouncedOnValueChange = debounce((value: number) => onValueChange(value), 50);
position.value = clamp(e.x, 0, sliderWidth);
currentValue.value = equivalentValue(position.value); const tapGesture = Gesture.Tap()
onValueChange(currentValue.value); .hitSlop({ vertical: 10 })
}); .onStart(e => {
currentValue.value = equivalentValue(clamp(e.x, 0, sliderWidth));
onValueChange(equivalentValue(clamp(e.x, 0, sliderWidth)));
});
const dragGesture = Gesture.Pan() const dragGesture = Gesture.Pan()
.onChange(e => { .hitSlop({ horizontal: 5, vertical: 20 })
position.value = clamp(e.x, 0, sliderWidth); .onStart(() => {
currentValue.value = equivalentValue(position.value); setSliding(true);
onValueChange(currentValue.value); sliderThumbWidth.value = withTiming(3 * SLIDER_THUMB_RADIUS, { duration: 100 });
}) })
.onEnd(() => { .onChange(e => {
onValueChange(currentValue.value); currentValue.value = equivalentValue(clamp(e.x, 0, sliderWidth));
onValueChange(equivalentValue(clamp(e.x, 0, sliderWidth)));
})
.onEnd(e => {
sliderThumbWidth.value = withTiming(2 * SLIDER_THUMB_RADIUS, { duration: 100 });
currentValue.value = equivalentValue(clamp(e.x, 0, sliderWidth));
onValueChange(equivalentValue(clamp(e.x, 0, sliderWidth)));
setSliding(false);
}); });
const animatedThumbStyles = useAnimatedStyle(() => ({ const animatedThumbStyles = useAnimatedStyle(() => ({
@ -70,7 +81,9 @@ const Slider = React.memo(
{ {
translateX: interpolate(currentValue.value, [0, maximumValue], [0, sliderWidth]) - SLIDER_THUMB_RADIUS translateX: interpolate(currentValue.value, [0, maximumValue], [0, sliderWidth]) - SLIDER_THUMB_RADIUS
} }
] ],
width: sliderThumbWidth.value,
height: sliderThumbWidth.value
})); }));
const animatedTrackStyles = useAnimatedStyle(() => ({ const animatedTrackStyles = useAnimatedStyle(() => ({
@ -81,20 +94,16 @@ const Slider = React.memo(
return ( return (
<View style={[styles.sliderContainer, containerStyle]}> <View style={[styles.sliderContainer, containerStyle]}>
<Touchable onPress={() => {}}> <GestureDetector gesture={gesture}>
<GestureDetector gesture={gesture}> <View style={[styles.track, { backgroundColor: maximumTrackTintColor || colors.buttonBackground }]} onLayout={onLayout}>
<View <Animated.View
style={[styles.track, { backgroundColor: maximumTrackTintColor || colors.buttonBackground }]} style={[styles.sliderThumb, { backgroundColor: thumbTintColor || colors.tintColor }, animatedThumbStyles]}
onLayout={onLayout}> />
<Animated.View <Animated.View
style={[styles.sliderThumb, { backgroundColor: thumbTintColor || colors.tintColor }, animatedThumbStyles]} style={[styles.activeTrack, { backgroundColor: minimumTrackTintColor || colors.tintColor }, animatedTrackStyles]}
/> />
<Animated.View </View>
style={[styles.activeTrack, { backgroundColor: minimumTrackTintColor || colors.tintColor }, animatedTrackStyles]} </GestureDetector>
/>
</View>
</GestureDetector>
</Touchable>
</View> </View>
); );
} }