Skip to content

Instantly share code, notes, and snippets.

@gilsonviana
Last active May 31, 2021 18:12
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gilsonviana/1e92c718ea79cdbbc1375749538d185c to your computer and use it in GitHub Desktop.
Save gilsonviana/1e92c718ea79cdbbc1375749538d185c to your computer and use it in GitHub Desktop.
React Native + Reanimated + PanGestureHandler + TapGestureHandler
import React, { useState } from 'react';
import { StyleSheet, View, Dimensions } from 'react-native';
import Animated, {
useSharedValue,
useAnimatedGestureHandler,
withSpring,
useAnimatedStyle,
withTiming,
useDerivedValue,
runOnJS
} from 'react-native-reanimated';
import {
PanGestureHandler,
TapGestureHandler,
GestureEvent,
TapGestureHandlerEventPayload
} from 'react-native-gesture-handler';
import StarIcon from '@src/assets/icons/favourite.svg';
const { width } = Dimensions.get('screen');
export const HomeScreen: React.FC = () => {
const dragX = useSharedValue(0);
const offsetX = useSharedValue(0);
const opacity = useSharedValue(1);
const scale = useSharedValue(1);
const scaleDown = useSharedValue(1);
const opacityTooltip = useSharedValue(0);
const [numStars, setNumStars] = useState(Array.from({ length: 1 }));
const recordStar = (result: number) => {
setNumStars(() => Array.from({ length: result }));
};
useDerivedValue(() => {
const countStars =
dragX.value < width * 0.33 ? 1 : dragX.value >= width * 0.66 ? 3 : 2;
runOnJS(recordStar)(countStars);
}, [dragX]);
const onTapHandler = (event: GestureEvent<TapGestureHandlerEventPayload>) => {
dragX.value = withTiming(event.nativeEvent.x);
};
const gestureHandler = useAnimatedGestureHandler({
onStart: () => {
offsetX.value = dragX.value;
scale.value = withSpring(1.2);
opacity.value = withTiming(0.65);
scaleDown.value = withTiming(0.98);
opacityTooltip.value = withTiming(1);
},
onActive: (event) => {
dragX.value = offsetX.value + event.translationX;
},
onEnd: () => {
offsetX.value = dragX.value;
scale.value = withSpring(1);
opacity.value = withTiming(1);
scaleDown.value = withTiming(1);
opacityTooltip.value = withTiming(0);
}
});
const scaleDownStyle = useAnimatedStyle(
() => ({
transform: [
{
scale: scaleDown.value
}
]
}),
[scaleDown]
);
const trackStyle = useAnimatedStyle(
() => ({
width: dragX.value,
opacity: opacity.value
}),
[dragX, opacity]
);
const thumbStyle = useAnimatedStyle(
() => ({
opacity: opacity.value,
transform: [
{
translateX: dragX.value
},
{
scale: scale.value
}
]
}),
[dragX, opacity, scale]
);
const tooltipStyle = useAnimatedStyle(() => {
return {
width:
dragX.value < width * 0.33 ? 42 : dragX.value >= width * 0.66 ? 86 : 64,
opacity: opacityTooltip.value,
transform: [
{
translateX: withSpring(dragX.value)
}
]
};
}, [dragX]);
return (
<View style={{ flex: 1, marginHorizontal: 32, justifyContent: 'center' }}>
{/* CONTAINER */}
<Animated.View style={[s.progressBar]}>
{/* BAR */}
<TapGestureHandler onHandlerStateChange={onTapHandler} maxDist={20}>
<Animated.View style={[s.bar, scaleDownStyle]}>
{/* TRACK */}
<Animated.View style={[s.track, trackStyle, scaleDownStyle]} />
</Animated.View>
</TapGestureHandler>
<PanGestureHandler
maxPointers={1}
onGestureEvent={gestureHandler}
onHandlerStateChange={gestureHandler}
>
{/* THUMB */}
<Animated.View style={[s.thumb, thumbStyle]} />
</PanGestureHandler>
</Animated.View>
{/* TOOLTIP */}
<Animated.View style={[s.tooltipContainer, tooltipStyle]}>
<View style={s.tooltipArrow} />
<View style={s.tooltipContent}>
{numStars.map((_, i) => (
<StarIcon key={i} width={16} height={16} />
))}
</View>
</Animated.View>
</View>
);
};
const s = StyleSheet.create({
progressBar: {
position: 'relative'
},
bar: {
height: 4,
backgroundColor: '#ddd',
flexDirection: 'row',
alignItems: 'center',
borderRadius: 2,
shadowOffset: {
width: 0,
height: 0.5
},
shadowOpacity: 0.3,
shadowRadius: 0.5
},
thumb: {
width: 16,
height: 16,
backgroundColor: 'tomato',
borderRadius: 8,
position: 'absolute',
top: -6,
left: -8
},
track: {
position: 'absolute',
height: 4,
width: 200,
backgroundColor: 'tomato',
borderTopLeftRadius: 2,
borderBottomLeftRadius: 2
},
tooltipContainer: {
backgroundColor: '#fff',
padding: 8,
height: 52,
position: 'relative',
top: 16,
left: -21,
borderRadius: 3,
shadowOffset: {
width: 0,
height: 0.5
},
shadowOpacity: 0.3,
shadowRadius: 0.5
},
tooltipArrow: {
width: 12,
height: 12,
position: 'absolute',
top: -5,
left: 15,
backgroundColor: '#fff',
transform: [
{
rotate: '45deg'
}
]
},
tooltipContent: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
flexDirection: 'row'
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment