diff --git a/src/app/FightRateApp.tsx b/src/app/FightRateApp.tsx index bba849d..c70be9e 100644 --- a/src/app/FightRateApp.tsx +++ b/src/app/FightRateApp.tsx @@ -18,6 +18,7 @@ import { import { useSafeAreaInsets } from 'react-native-safe-area-context'; import Animated, { Easing, + runOnJS, useAnimatedStyle, useSharedValue, withTiming, @@ -49,17 +50,38 @@ export function FightRateApp() { const insets = useSafeAreaInsets(); const monitor = useHeartRateMonitor(); const [activeTab, setActiveTab] = useState('capture'); - const opacity = useSharedValue(1); + const [prevTab, setPrevTab] = useState('capture'); + const progress = useSharedValue(1); + const direction = useSharedValue(1); useEffect(() => { - opacity.value = 0; - opacity.value = withTiming(1, { - duration: motion.duration.fast, - easing: Easing.out(Easing.quad), - }); - }, [activeTab, opacity]); + if (activeTab === prevTab) { + return; + } + const tabOrder: TabKey[] = ['capture', 'history', 'sensor', 'insights']; + direction.value = + tabOrder.indexOf(activeTab) > tabOrder.indexOf(prevTab) ? 1 : -1; + progress.value = 0; + progress.value = withTiming( + 1, + { + duration: motion.duration.fast, + easing: Easing.out(Easing.quad), + }, + (finished) => { + if (finished) { + runOnJS(setPrevTab)(activeTab); + } + }, + ); + }, [activeTab, prevTab, progress, direction]); - const fadeStyle = useAnimatedStyle(() => ({ opacity: opacity.value })); + const enterStyle = useAnimatedStyle(() => ({ + opacity: progress.value, + transform: [ + { translateX: (1 - progress.value) * 24 * direction.value }, + ], + })); const onCaptureAction = useCallback(async () => { if (monitor.isCapturing) { @@ -117,7 +139,7 @@ export function FightRateApp() { - + {activeTab === 'capture' ? (