Skip to content

Instantly share code, notes, and snippets.

@Karthik-B-06
Created May 8, 2024 05:30
Show Gist options
  • Save Karthik-B-06/27fadccaf3481df97c04ba6f46ed400e to your computer and use it in GitHub Desktop.
Save Karthik-B-06/27fadccaf3481df97c04ba6f46ed400e to your computer and use it in GitHub Desktop.
import React from 'react';
import { LayoutChangeEvent, StyleSheet } from 'react-native';
import { Gesture, GestureDetector } from 'react-native-gesture-handler';
import Animated, {
clamp,
Extrapolate,
interpolate,
runOnJS,
SharedValue,
useAnimatedReaction,
useAnimatedStyle,
useSharedValue,
withSpring,
WithSpringConfig,
} from 'react-native-reanimated';
import { tailwind } from '~/theme/tailwind';
const DefaultSpringConfig: WithSpringConfig = {
mass: 1,
damping: 18,
stiffness: 280,
};
type SliderProps = {
currentPosition: SharedValue<number>;
totalDuration: SharedValue<number>;
manualSeekTo: (manualSeekPosition: number) => void;
pauseAudio: () => void;
trackColor: string;
filledTrackColor: string;
knobStyle: string;
};
export const Slider = (props: SliderProps) => {
const {
currentPosition,
totalDuration,
manualSeekTo,
pauseAudio,
trackColor,
filledTrackColor,
knobStyle,
} = props;
const sliderActive = useSharedValue(0);
const sliderMaxWidth = useSharedValue(0);
const translationX = useSharedValue(0);
const context = useSharedValue({ x: 0 });
useAnimatedReaction(
() => currentPosition.value,
(next, _prev) => {
translationX.value = withSpring(
interpolate(next, [0, totalDuration.value], [0, sliderMaxWidth.value - 16]),
{
damping: 24,
stiffness: 200,
}
);
},
[currentPosition.value, totalDuration.value, sliderMaxWidth.value]
);
const panGesture = Gesture.Pan()
.onBegin(() => {
runOnJS(pauseAudio)();
sliderActive.value = withSpring(1, DefaultSpringConfig);
context.value = { x: translationX.value };
})
.onUpdate((event) => {
translationX.value = clamp(
event.translationX + context.value.x,
0,
sliderMaxWidth.value - 16 // because the knob width is 16
);
})
.onEnd(() => {
const seekToValue = interpolate(
translationX.value,
[0, sliderMaxWidth.value - 16],
[0, totalDuration.value],
Extrapolate.CLAMP
);
runOnJS(manualSeekTo)(seekToValue);
})
.onFinalize(() => (sliderActive.value = withSpring(0, DefaultSpringConfig)));
const handleLayout = (e: LayoutChangeEvent) =>
(sliderMaxWidth.value = e.nativeEvent.layout.width);
const animatedKnobOneStyle = useAnimatedStyle(
() => ({
transform: [
{
translateX: translationX.value,
},
],
borderWidth: sliderActive.value ? withSpring(2) : withSpring(0),
}),
[translationX.value, sliderActive.value]
);
const animatedFilledTrack = useAnimatedStyle(
() => ({
width: translationX.value + 8,
}),
[sliderMaxWidth.value]
);
return (
<Animated.View style={tailwind.style('flex flex-row flex-1 mx-1.5')}>
<Animated.View
onLayout={handleLayout}
style={tailwind.style('relative rounded-2xl flex-1 h-1', trackColor)}
/>
<Animated.View
style={[
tailwind.style('absolute rounded-2xl flex-1 w-1/2 h-1', filledTrackColor),
animatedFilledTrack,
]}
/>
<GestureDetector gesture={panGesture}>
<Animated.View
style={[
tailwind.style(
'absolute justify-center items-center h-4 w-4 bg-black rounded-full -bottom-1.5',
knobStyle
),
styles.knobShadow,
animatedKnobOneStyle,
]}
/>
</GestureDetector>
</Animated.View>
);
};
const styles = StyleSheet.create({
knobShadow: {
// box-shadow: 0px 1px 2px 0px #00000026;
// box-shadow: 0px 0px 1px 0px #00000066;
// box-shadow: [horizontal offset] [vertical offset] [blur radius] [optional spread radius] [color];
shadowColor: '#00000066',
shadowOffset: { width: 0, height: 0.35 },
shadowRadius: 4,
shadowOpacity: 0.6,
elevation: 2,
},
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment