Replace reanimated for tabs selector

zio/stable
Paul Frazee 2022-12-07 17:08:49 -06:00
parent efbef238a8
commit 273e6d2973
2 changed files with 60 additions and 54 deletions

View File

@ -1,6 +1,7 @@
import React, {createRef, useRef, useMemo, useEffect, useState} from 'react' import React, {createRef, useRef, useMemo, useState} from 'react'
import {observer} from 'mobx-react-lite' import {observer} from 'mobx-react-lite'
import { import {
Animated,
ScrollView, ScrollView,
Share, Share,
StyleSheet, StyleSheet,
@ -9,20 +10,13 @@ import {
View, View,
} from 'react-native' } from 'react-native'
import {useSafeAreaInsets} from 'react-native-safe-area-context' import {useSafeAreaInsets} from 'react-native-safe-area-context'
import Animated, {
interpolate,
SharedValue,
useSharedValue,
useAnimatedStyle,
withTiming,
runOnJS,
} from 'react-native-reanimated'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import Swipeable from 'react-native-gesture-handler/Swipeable' import Swipeable from 'react-native-gesture-handler/Swipeable'
import {useStores} from '../../../state' import {useStores} from '../../../state'
import {s, colors} from '../../lib/styles' import {s, colors} from '../../lib/styles'
import {toShareUrl} from '../../../lib/strings' import {toShareUrl} from '../../../lib/strings'
import {match} from '../../routes' import {match} from '../../routes'
import {useAnimatedValue} from '../../lib/useAnimatedValue'
const TAB_HEIGHT = 42 const TAB_HEIGHT = 42
@ -33,7 +27,7 @@ export const TabsSelector = observer(
onClose, onClose,
}: { }: {
active: boolean active: boolean
tabMenuInterp: SharedValue<number> tabMenuInterp: Animated.Value
onClose: () => void onClose: () => void
}) => { }) => {
const store = useStores() const store = useStores()
@ -41,7 +35,7 @@ export const TabsSelector = observer(
const [closingTabIndex, setClosingTabIndex] = useState<number | undefined>( const [closingTabIndex, setClosingTabIndex] = useState<number | undefined>(
undefined, undefined,
) )
const closeInterp = useSharedValue<number>(0) const closeInterp = useAnimatedValue(0)
const tabsRef = useRef<ScrollView>(null) const tabsRef = useRef<ScrollView>(null)
const tabRefs = useMemo( const tabRefs = useMemo(
() => () =>
@ -51,11 +45,16 @@ export const TabsSelector = observer(
[store.nav.tabs.length], [store.nav.tabs.length],
) )
const wrapperAnimStyle = useAnimatedStyle(() => ({ const wrapperAnimStyle = {
transform: [ transform: [
{translateY: interpolate(tabMenuInterp.value, [0, 1.0], [320, 0])}, {
translateY: tabMenuInterp.interpolate({
inputRange: [0, 1.0],
outputRange: [320, 0],
}),
},
], ],
})) }
// events // events
// = // =
@ -76,13 +75,16 @@ export const TabsSelector = observer(
store.nav.setActiveTab(tabIndex) store.nav.setActiveTab(tabIndex)
onClose() onClose()
} }
const doCloseTab = (index: number) => store.nav.closeTab(index)
const onCloseTab = (tabIndex: number) => { const onCloseTab = (tabIndex: number) => {
setClosingTabIndex(tabIndex) setClosingTabIndex(tabIndex)
closeInterp.value = 0 closeInterp.setValue(0)
closeInterp.value = withTiming(1, {duration: 300}, () => { Animated.timing(closeInterp, {
runOnJS(setClosingTabIndex)(undefined) toValue: 1,
runOnJS(doCloseTab)(tabIndex) duration: 300,
useNativeDriver: false,
}).start(() => {
setClosingTabIndex(undefined)
store.nav.closeTab(tabIndex)
}) })
} }
const onLayout = () => { const onLayout = () => {
@ -107,11 +109,11 @@ export const TabsSelector = observer(
} }
const currentTabIndex = store.nav.tabIndex const currentTabIndex = store.nav.tabIndex
const closingTabAnimStyle = useAnimatedStyle(() => ({ const closingTabAnimStyle = {
height: TAB_HEIGHT * (1 - closeInterp.value), height: Animated.multiply(TAB_HEIGHT, Animated.subtract(1, closeInterp)),
opacity: 1 - closeInterp.value, opacity: Animated.subtract(1, closeInterp),
marginBottom: 4 * (1 - closeInterp.value), marginBottom: Animated.multiply(4, Animated.subtract(1, closeInterp)),
})) }
if (!active) { if (!active) {
return <View /> return <View />

View File

@ -1,7 +1,8 @@
import React, {useState, useEffect, useRef} from 'react' import React, {useState, useEffect, useRef} from 'react'
import {observer} from 'mobx-react-lite' import {observer} from 'mobx-react-lite'
import { import {
Animated as RNAnimated, Animated,
Easing,
FlatList, FlatList,
GestureResponderEvent, GestureResponderEvent,
SafeAreaView, SafeAreaView,
@ -16,13 +17,6 @@ import {
import {ScreenContainer, Screen} from 'react-native-screens' import {ScreenContainer, Screen} from 'react-native-screens'
import LinearGradient from 'react-native-linear-gradient' import LinearGradient from 'react-native-linear-gradient'
import {useSafeAreaInsets} from 'react-native-safe-area-context' import {useSafeAreaInsets} from 'react-native-safe-area-context'
import {
Easing,
useSharedValue,
useAnimatedStyle,
withTiming,
runOnJS,
} from 'react-native-reanimated'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {IconProp} from '@fortawesome/fontawesome-svg-core' import {IconProp} from '@fortawesome/fontawesome-svg-core'
import {TABS_ENABLED} from '../../../build-flags' import {TABS_ENABLED} from '../../../build-flags'
@ -119,8 +113,8 @@ export const MobileShell: React.FC = observer(() => {
const scrollElRef = useRef<FlatList | undefined>() const scrollElRef = useRef<FlatList | undefined>()
const winDim = useWindowDimensions() const winDim = useWindowDimensions()
const swipeGestureInterp = useAnimatedValue(0) const swipeGestureInterp = useAnimatedValue(0)
const tabMenuInterp = useSharedValue<number>(0) const tabMenuInterp = useAnimatedValue(0)
const newTabInterp = useSharedValue<number>(0) const newTabInterp = useAnimatedValue(0)
const [isRunningNewTabAnim, setIsRunningNewTabAnim] = useState(false) const [isRunningNewTabAnim, setIsRunningNewTabAnim] = useState(false)
const colorScheme = useColorScheme() const colorScheme = useColorScheme()
const safeAreaInsets = useSafeAreaInsets() const safeAreaInsets = useSafeAreaInsets()
@ -139,22 +133,29 @@ export const MobileShell: React.FC = observer(() => {
// tab selector animation // tab selector animation
// = // =
const closeTabsSelector = () => setTabsSelectorActive(false)
const toggleTabsMenu = (active: boolean) => { const toggleTabsMenu = (active: boolean) => {
if (active) { if (active) {
// will trigger the animation below // will trigger the animation below
setTabsSelectorActive(true) setTabsSelectorActive(true)
} else { } else {
tabMenuInterp.value = withTiming(0, {duration: 100}, () => { Animated.timing(tabMenuInterp, {
toValue: 0,
duration: 100,
useNativeDriver: false,
}).start(() => {
// hide once the animation has finished // hide once the animation has finished
runOnJS(closeTabsSelector)() setTabsSelectorActive(false)
}) })
} }
} }
useEffect(() => { useEffect(() => {
if (isTabsSelectorActive) { if (isTabsSelectorActive) {
// trigger the animation once the tabs selector is rendering // trigger the animation once the tabs selector is rendering
tabMenuInterp.value = withTiming(1, {duration: 100}) Animated.timing(tabMenuInterp, {
toValue: 1,
duration: 100,
useNativeDriver: false,
}).start()
} }
}, [isTabsSelectorActive]) }, [isTabsSelectorActive])
@ -171,13 +172,16 @@ export const MobileShell: React.FC = observer(() => {
store.nav.tab.setIsNewTab(false) store.nav.tab.setIsNewTab(false)
setIsRunningNewTabAnim(false) setIsRunningNewTabAnim(false)
} }
newTabInterp.value = withTiming( Animated.timing(newTabInterp, {
1, toValue: 1,
{duration: 250, easing: Easing.out(Easing.exp)}, duration: 250,
() => runOnJS(reset)(), easing: Easing.out(Easing.exp),
) useNativeDriver: false,
}).start(() => {
reset()
})
} else { } else {
newTabInterp.value = 0 newTabInterp.setValue(0)
} }
}, [isRunningNewTabAnim]) }, [isRunningNewTabAnim])
@ -190,7 +194,7 @@ export const MobileShell: React.FC = observer(() => {
} }
const swipeTransform = { const swipeTransform = {
transform: [ transform: [
{translateX: RNAnimated.multiply(swipeGestureInterp, winDim.width * -1)}, {translateX: Animated.multiply(swipeGestureInterp, winDim.width * -1)},
], ],
} }
const swipeOpacity = { const swipeOpacity = {
@ -199,12 +203,12 @@ export const MobileShell: React.FC = observer(() => {
outputRange: [0, 0.6, 0], outputRange: [0, 0.6, 0],
}), }),
} }
const tabMenuTransform = useAnimatedStyle(() => ({ const tabMenuTransform = {
transform: [{translateY: tabMenuInterp.value * -320}], transform: [{translateY: Animated.multiply(tabMenuInterp.value, -320)}],
})) }
const newTabTransform = useAnimatedStyle(() => ({ const newTabTransform = {
transform: [{scale: newTabInterp.value}], transform: [{scale: newTabInterp}],
})) }
if (!store.session.hasSession) { if (!store.session.hasSession) {
return ( return (
@ -250,12 +254,12 @@ export const MobileShell: React.FC = observer(() => {
key={key} key={key}
style={[StyleSheet.absoluteFill]} style={[StyleSheet.absoluteFill]}
activityState={current ? 2 : previous ? 1 : 0}> activityState={current ? 2 : previous ? 1 : 0}>
<RNAnimated.View <Animated.View
style={ style={
current ? [styles.screenMask, swipeOpacity] : undefined current ? [styles.screenMask, swipeOpacity] : undefined
} }
/> />
<RNAnimated.View <Animated.View
style={[ style={[
s.flex1, s.flex1,
styles.screen, styles.screen,
@ -273,7 +277,7 @@ export const MobileShell: React.FC = observer(() => {
visible={current} visible={current}
scrollElRef={current ? scrollElRef : undefined} scrollElRef={current ? scrollElRef : undefined}
/> />
</RNAnimated.View> </Animated.View>
</Screen> </Screen>
) )
}, },