diff --git a/README.md b/README.md index d194a0e0..7beae3b8 100644 --- a/README.md +++ b/README.md @@ -280,7 +280,7 @@ You can also pass a boolean to enable lazy for all of the scenes: ```js ``` @@ -316,6 +316,10 @@ Callback which is called when the swipe gesture starts, i.e. the user touches th Callback which is called when the swipe gesture ends, i.e. the user lifts their finger from the screen after the swipe gesture. +##### `animateOnIndexChange` + +Function which determines whether the animation is executed on index change + ##### `initialLayout` Object containing the initial height and width of the screens. Passing this will improve the initial rendering performance. For most apps, this is a good default: diff --git a/example/src/App.tsx b/example/src/App.tsx index 8ce5347d..7eb707a4 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -22,6 +22,7 @@ import CustomIndicatorExample from './CustomIndicatorExample'; import CustomTabBarExample from './CustomTabBarExample'; import CoverflowExample from './CoverflowExample'; import TabBarGapExample from './TabBarGapExample'; +import CustomAnimationExample from './CustomAnimationExample'; type ExampleComponentType = React.ComponentType<{}> & { title: string; @@ -43,6 +44,7 @@ const EXAMPLE_COMPONENTS: ExampleComponentType[] = [ CustomTabBarExample, CoverflowExample, TabBarGapExample, + CustomAnimationExample, ]; const ExampleList = () => { diff --git a/example/src/CustomAnimationExample.tsx b/example/src/CustomAnimationExample.tsx new file mode 100644 index 00000000..5124cfb8 --- /dev/null +++ b/example/src/CustomAnimationExample.tsx @@ -0,0 +1,87 @@ +import * as React from 'react'; +import { StyleSheet } from 'react-native'; +import { + TabView, + TabBar, + SceneMap, + NavigationState, + SceneRendererProps, +} from 'react-native-tab-view'; +import Article from './Shared/Article'; +import Albums from './Shared/Albums'; +import Chat from './Shared/Chat'; +import Contacts from './Shared/Contacts'; + +type State = NavigationState<{ + key: string; + title: string; +}>; + +const animateOnIndexChange = (currentIndex: number, nextIndex: number) => { + return Math.abs(currentIndex - nextIndex) === 1; +}; + +const CustomAnimationExample = () => { + const [index, onIndexChange] = React.useState(1); + const [routes] = React.useState([ + { key: 'article', title: 'Article' }, + { key: 'contacts', title: 'Contacts' }, + { key: 'albums', title: 'Albums' }, + { key: 'chat', title: 'Chat' }, + ]); + + const renderTabBar = ( + props: SceneRendererProps & { navigationState: State } + ) => ( + + ); + + const renderScene = SceneMap({ + albums: Albums, + contacts: Contacts, + article: Article, + chat: Chat, + }); + + return ( + + ); +}; + +CustomAnimationExample.title = 'Custom Animation'; +CustomAnimationExample.backgroundColor = '#3f51b5'; +CustomAnimationExample.appbarElevation = 0; + +export default CustomAnimationExample; + +const styles = StyleSheet.create({ + tabbar: { + backgroundColor: '#3f51b5', + }, + tab: { + width: 'auto', + }, + indicator: { + backgroundColor: '#ffeb3b', + }, + label: { + fontWeight: '400', + }, +}); diff --git a/src/PagerViewAdapter.tsx b/src/PagerViewAdapter.tsx index b858cb48..8891287d 100644 --- a/src/PagerViewAdapter.tsx +++ b/src/PagerViewAdapter.tsx @@ -40,6 +40,7 @@ export default function PagerViewAdapter({ onSwipeStart, onSwipeEnd, children, + animateOnIndexChange, style, ...rest }: Props) { @@ -58,13 +59,22 @@ export default function PagerViewAdapter({ navigationStateRef.current = navigationState; }); - const jumpTo = React.useCallback((key: string) => { - const index = navigationStateRef.current.routes.findIndex( - (route: { key: string }) => route.key === key - ); - - pagerRef.current?.setPage(index); - }, []); + const jumpTo = React.useCallback( + (key: string) => { + const index = navigationStateRef.current.routes.findIndex( + (route: { key: string }) => route.key === key + ); + if ( + animateOnIndexChange && + !animateOnIndexChange(indexRef.current, index) + ) { + pagerRef.current?.setPageWithoutAnimation(index); + } else { + pagerRef.current?.setPage(index); + } + }, + [animateOnIndexChange] + ); React.useEffect(() => { if (keyboardDismissMode === 'auto') { diff --git a/src/PanResponderAdapter.tsx b/src/PanResponderAdapter.tsx index 3d1964a9..bcd455cc 100644 --- a/src/PanResponderAdapter.tsx +++ b/src/PanResponderAdapter.tsx @@ -57,6 +57,7 @@ export default function PanResponderAdapter({ onSwipeStart, onSwipeEnd, children, + animateOnIndexChange, style, }: Props) { const { routes, index } = navigationState; @@ -80,23 +81,30 @@ export default function PanResponderAdapter({ const offset = -index * layoutRef.current.width; const { timing, ...transitionConfig } = DefaultTransitionSpec; - - Animated.parallel([ - timing(panX, { - ...transitionConfig, - toValue: offset, - useNativeDriver: false, - }), - ]).start(({ finished }) => { - if (finished) { - onIndexChangeRef.current(index); - pendingIndexRef.current = undefined; - } - }); + if ( + animateOnIndexChange && + !animateOnIndexChange(currentIndexRef.current, index) + ) { + onIndexChangeRef.current(index); + pendingIndexRef.current = undefined; + } else { + Animated.parallel([ + timing(panX, { + ...transitionConfig, + toValue: offset, + useNativeDriver: false, + }), + ]).start(({ finished }) => { + if (finished) { + onIndexChangeRef.current(index); + pendingIndexRef.current = undefined; + } + }); + } pendingIndexRef.current = index; }, - [panX] + [animateOnIndexChange, panX] ); React.useEffect(() => { diff --git a/src/TabView.tsx b/src/TabView.tsx index 9734440f..63d45240 100644 --- a/src/TabView.tsx +++ b/src/TabView.tsx @@ -51,6 +51,7 @@ export default function TabView({ style, swipeEnabled = true, tabBarPosition = 'top', + animateOnIndexChange, }: Props) { const [layout, setLayout] = React.useState({ width: 0, @@ -87,6 +88,7 @@ export default function TabView({ onSwipeEnd={onSwipeEnd} onIndexChange={jumpToIndex} style={pagerStyle} + animateOnIndexChange={animateOnIndexChange} > {({ position, render, addEnterListener, jumpTo }) => { // All of the props here must not change between re-renders diff --git a/src/types.tsx b/src/types.tsx index 164ef37b..401d51dc 100644 --- a/src/types.tsx +++ b/src/types.tsx @@ -55,4 +55,5 @@ export type PagerProps = Omit< swipeEnabled?: boolean; onSwipeStart?: () => void; onSwipeEnd?: () => void; + animateOnIndexChange?: (currentIndex: number, nextIndex: number) => boolean; };