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

View File

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