Animated view translateX property doesn't seem to work - react-native

I have an Animated View to which in transform prop I'm setting the translateX values. It doesn't seem to be working though. I debugged the translateX value and it seems to be updating, but the View doesn't seem to.
react-native-reanimated version: 1.13.1, react-native-gesture-handler version: 1.10.3
function Cursor() {
const translateX = new Value(0)
const gestureState = new Value(State.UNDETERMINED)
const onGestureEvent = event([{
nativeEvent: {
translationX: translateX,
state: gestureState
}
}], { useNativeDriver: true })
return (
<PanGestureHandler {...{ onGestureEvent }} onHandlerStateChange={onGestureEvent}>
<Animated.View
style={{
...StyleSheet.absoluteFillObject,
width: height,
height: height,
borderRadius: height / 2,
backgroundColor: "white",
transform: [{ translateX }],
}}
/>
</PanGestureHandler>
)
}

Related

react native top tab bar navigator: indicator width to match text

I have three tabs in a top tab bar navigation with different width text. Is it possible to make the indicator width match the text? On a similar note, how can I make the tabs match the width of the text too without making it display weird. I've tried width auto but it doesn't stay center.
This is how it looks with auto width:
<Tab.Navigator
initialRouteName="Open"
tabBarOptions={{
style: {
backgroundColor: "white",
paddingTop: 20,
paddingHorizontal: 25
},
indicatorStyle: {
borderBottomColor: colorScheme.teal,
borderBottomWidth: 2,
width: '30%',
left:"9%"
},
tabStyle : {
justifyContent: "center",
width: tabBarWidth/3,
}
}}
>
<Tab.Screen
name="Open"
component={WriterRequestScreen}
initialParams={{ screen: 'Open' }}
options={{
tabBarLabel: ({focused}) => <Text style = {{fontSize: 18, fontWeight: 'bold', color: focused? colorScheme.teal : colorScheme.grey}}> Open </Text>,
}}
/>
<Tab.Screen
name="In Progress"
component={WriterRequestScreen}
initialParams={{ screen: 'InProgress' }}
options={{
tabBarLabel: ({focused}) => <Text style = {{fontSize: 18, fontWeight: 'bold', color: focused? colorScheme.teal : colorScheme.grey}}> In Progress </Text>}}
/>
<Tab.Screen
name="Completed"
component={WriterRequestScreen}
initialParams={{ screen: 'Completed' }}
options={{ tabBarLabel: ({focused}) => <Text style = {{fontSize: 18, fontWeight: 'bold', color: focused? colorScheme.teal : colorScheme.grey}}> Completed </Text>}}
/>
</Tab.Navigator>
I also needed to make the indicator fit the text size, a dynamic width for the labels, and a scrollable top bar because of long labels. The result looks like this:
tab bar with dynamic indicator width
If you don't care about the indicator width fitting the labels, you can simply use screenOptions.tabBarScrollEnabled: true in combination with width: "auto" in screenOptions.tabBarIndicatorStyle.
Otherwise, you'll need to make your own tab bar component and pass it to the tabBar property of your <Tab.Navigator>. I used a ScrollView but if you have only a few tabs with short labels, a View would be more simple. Here is the Typescript code for this custom TabBar component:
import { MaterialTopTabBarProps } from "#react-navigation/material-top-tabs";
import { useEffect, useRef, useState } from "react";
import {
Animated,
Dimensions,
View,
TouchableOpacity,
StyleSheet,
ScrollView,
I18nManager,
LayoutChangeEvent,
} from "react-native";
const screenWidth = Dimensions.get("window").width;
const DISTANCE_BETWEEN_TABS = 20;
const TabBar = ({
state,
descriptors,
navigation,
position,
}: MaterialTopTabBarProps): JSX.Element => {
const [widths, setWidths] = useState<(number | undefined)[]>([]);
const scrollViewRef = useRef<ScrollView>(null);
const transform = [];
const inputRange = state.routes.map((_, i) => i);
// keep a ref to easily scroll the tab bar to the focused label
const outputRangeRef = useRef<number[]>([]);
const getTranslateX = (
position: Animated.AnimatedInterpolation,
routes: never[],
widths: number[]
) => {
const outputRange = routes.reduce((acc, _, i: number) => {
if (i === 0) return [DISTANCE_BETWEEN_TABS / 2 + widths[0] / 2];
return [
...acc,
acc[i - 1] + widths[i - 1] / 2 + widths[i] / 2 + DISTANCE_BETWEEN_TABS,
];
}, [] as number[]);
outputRangeRef.current = outputRange;
const translateX = position.interpolate({
inputRange,
outputRange,
extrapolate: "clamp",
});
return Animated.multiply(translateX, I18nManager.isRTL ? -1 : 1);
};
// compute translateX and scaleX because we cannot animate width directly
if (
state.routes.length > 1 &&
widths.length === state.routes.length &&
!widths.includes(undefined)
) {
const translateX = getTranslateX(
position,
state.routes as never[],
widths as number[]
);
transform.push({
translateX,
});
const outputRange = inputRange.map((_, i) => widths[i]) as number[];
transform.push({
scaleX:
state.routes.length > 1
? position.interpolate({
inputRange,
outputRange,
extrapolate: "clamp",
})
: outputRange[0],
});
}
// scrolls to the active tab label when a new tab is focused
useEffect(() => {
if (
state.routes.length > 1 &&
widths.length === state.routes.length &&
!widths.includes(undefined)
) {
if (state.index === 0) {
scrollViewRef.current?.scrollTo({
x: 0,
});
} else {
// keep the focused label at the center of the screen
scrollViewRef.current?.scrollTo({
x: (outputRangeRef.current[state.index] as number) - screenWidth / 2,
});
}
}
}, [state.index, state.routes.length, widths]);
// get the label widths on mount
const onLayout = (event: LayoutChangeEvent, index: number) => {
const { width } = event.nativeEvent.layout;
const newWidths = [...widths];
newWidths[index] = width - DISTANCE_BETWEEN_TABS;
setWidths(newWidths);
};
// basic labels as suggested by react navigation
const labels = state.routes.map((route, index) => {
const { options } = descriptors[route.key];
const label = route.name;
const isFocused = state.index === index;
const onPress = () => {
const event = navigation.emit({
type: "tabPress",
target: route.key,
canPreventDefault: true,
});
if (!isFocused && !event.defaultPrevented) {
// The `merge: true` option makes sure that the params inside the tab screen are preserved
// eslint-disable-next-line
// #ts-ignore
navigation.navigate({ name: route.name, merge: true });
}
};
const inputRange = state.routes.map((_, i) => i);
const opacity = position.interpolate({
inputRange,
outputRange: inputRange.map((i) => (i === index ? 1 : 0.5)),
});
return (
<TouchableOpacity
key={route.key}
accessibilityRole="button"
accessibilityState={isFocused ? { selected: true } : {}}
accessibilityLabel={options.tabBarAccessibilityLabel}
onPress={onPress}
style={styles.button}
>
<View
onLayout={(event) => onLayout(event, index)}
style={styles.buttonContainer}
>
<Animated.Text style={[styles.text, { opacity }]}>
{label}
</Animated.Text>
</View>
</TouchableOpacity>
);
});
return (
<View style={styles.contentContainer}>
<Animated.ScrollView
horizontal
ref={scrollViewRef}
showsHorizontalScrollIndicator={false}
style={styles.container}
>
{labels}
<Animated.View style={[styles.indicator, { transform }]} />
</Animated.ScrollView>
</View>
);
};
const styles = StyleSheet.create({
button: {
alignItems: "center",
justifyContent: "center",
},
buttonContainer: {
paddingHorizontal: DISTANCE_BETWEEN_TABS / 2,
},
container: {
backgroundColor: "black",
flexDirection: "row",
height: 34,
},
contentContainer: {
height: 34,
marginTop: 30,
},
indicator: {
backgroundColor: "white",
bottom: 0,
height: 3,
left: 0,
position: "absolute",
right: 0,
// this must be 1 for the scaleX animation to work properly
width: 1,
},
text: {
color: "white",
fontSize: 14,
textAlign: "center",
},
});
export default TabBar;
I managed to make it work with a mix of:
react navigation example
react-native-tab-view original indicator
jsindos answer
Please let me know if you find a more convenient solution.
You have to add width:auto to tabStyle to make tab width flexible.
Then inside each tabBarLabel <Text> component add style textAlign: "center" and width: YOUR_WIDTH .
YOUR_WIDTH can be different for each tab and can be your text.length * 10 (if you want to make it depended on your text length) or get screen width from Dimensions and divide it by any other number to make it equal widths in screen. Example:
const win = Dimensions.get('window');
...
bigTab: {
fontFamily: "Mulish-Bold",
fontSize: 11,
color: "#fff",
textAlign: "center",
width: win.width/2 - 40
},
smallTab: {
fontFamily: "Mulish-Bold",
fontSize: 11,
color: "#fff",
textAlign: "center",
width: win.width / 5 + 10
}
Remove width from indicatorStyle and use flex:1
indicatorStyle: { borderBottomColor: colorScheme.teal,
borderBottomWidth: 2,
flex:1,
left:"9%"
},
I've achieved this using some hacks around onLayout, please note I've made this with the assumptions of two tabs, and that the second tabs width is greater than the first. It probably will need tweaking for other use cases.
import React, { useEffect, useState } from 'react'
import { createMaterialTopTabNavigator } from '#react-navigation/material-top-tabs'
import { Animated, Text, TouchableOpacity, View } from 'react-native'
const Stack = createMaterialTopTabNavigator()
const DISTANCE_BETWEEN_TABS = 25
function MyTabBar ({ state, descriptors, navigation, position }) {
const [widths, setWidths] = useState([])
const [transform, setTransform] = useState([])
const inputRange = state.routes.map((_, i) => i)
useEffect(() => {
if (widths.length === 2) {
const [startingWidth, transitionWidth] = widths
const translateX = position.interpolate({
inputRange,
outputRange: [0, startingWidth + DISTANCE_BETWEEN_TABS + (transitionWidth - startingWidth) / 2]
})
const scaleX = position.interpolate({
inputRange,
outputRange: [1, transitionWidth / startingWidth]
})
setTransform([{ translateX }, { scaleX }])
}
}, [widths])
return (
<View style={{ flexDirection: 'row' }}>
{state.routes.map((route, index) => {
const { options } = descriptors[route.key]
const label =
options.tabBarLabel !== undefined
? options.tabBarLabel
: options.title !== undefined
? options.title
: route.name
const isFocused = state.index === index
const onPress = () => {
const event = navigation.emit({
type: 'tabPress',
target: route.key,
canPreventDefault: true
})
if (!isFocused && !event.defaultPrevented) {
// The `merge: true` option makes sure that the params inside the tab screen are preserved
navigation.navigate({ name: route.name, merge: true })
}
}
const onLayout = event => {
const { width } = event.nativeEvent.layout
setWidths([...widths, width])
}
const opacity = position.interpolate({
inputRange,
outputRange: inputRange.map(i => (i === index ? 0.87 : 0.53))
})
return (
<TouchableOpacity
key={index}
accessibilityRole='button'
accessibilityState={isFocused ? { selected: true } : {}}
accessibilityLabel={options.tabBarAccessibilityLabel}
testID={options.tabBarTestID}
onPress={onPress}
style={{ marginRight: DISTANCE_BETWEEN_TABS }}
>
<Animated.Text
onLayout={onLayout}
style={{
opacity,
color: '#000',
fontSize: 18,
fontFamily: 'OpenSans-Bold',
marginBottom: 15
}}
>
{label}
</Animated.Text>
</TouchableOpacity>
)
})}
<View style={{ backgroundColor: '#DDD', height: 2, position: 'absolute', bottom: 0, left: 0, right: 0 }} />
<Animated.View style={{ position: 'absolute', bottom: 0, left: 0, width: widths.length ? widths[0] : 0, backgroundColor: '#222', height: 2, transform }} />
</View>
)
}
export default () => {
return (
<>
<Stack.Navigator tabBar={props => <MyTabBar {...props} />} style={{ paddingHorizontal: 25 }}>
<Stack.Screen name='Orders' component={() => <Text>A</Text>} />
<Stack.Screen name='Reviews' component={() => <Text>B</Text>} />
</Stack.Navigator>
</>
)
}
Update:
If the menu names are static, it is probably a more robust solution to hard code the widths inside of widths, although this is a little more costly to maintain.
Resources:
https://reactnavigation.org/docs/material-top-tab-navigator/#tabbar
https://github.com/facebook/react-native/issues/13107
In the screenOptions, add following props for
tabBarScrollEnabled: true
tabBarItemStyle: {{width: "auto", minWidht: "100"}}
minWidth is just to keep the design consistent.
Please note, I am using react-navigation 6.x and Camille Hg answer was really helpful.
I had the same issue and I was finally able to make the indicator takes exactly the text size.
I don't know in which version this was possible .. but apparently you can add a custom indicator component (beside the ability to add a custom tabBar component)
when creating the TopTabNavigator it is important to add the properties as described in the code under
// assuming that you want to add paddingHorizontal: 10 for each item!
const TAB_BAR_ITEM_PADDING = 10;
const Tab = createMaterialTopTabNavigator();
function TopTabNavigator() {
return (
<Tab.Navigator
.....
... .
screenOptions={{
....
...
tabBarItemStyle: {
// these properties are important for this method to work !!
width: "auto",
marginHorizontal: 0, // this is to make sure that the spacing of the item comes only from the paddingHorizontal!.
paddingHorizontal: TAB_BAR_ITEM_PADDING, // the desired padding for the item .. stored in a constant to be passed in the custom Indicator
},
tabBarIndicator: props => {
return (
<CustomTabBarIndicator
// the default props
getTabWidth={props.getTabWidth}
jumpTo={props.jumpTo}
layout={props.layout}
navigationState={props.state}
position={props.position}
width={props.width}
style={{
left: TAB_BAR_ITEM_PADDING,
backgroundColor: Colors.primary,
}}
// this is an additional property we will need to make the indicator exactly
tabBarItemPadding={TAB_BAR_ITEM_PADDING}
/>
);
},
}}
>
<Tab.Screen .... />
<Tab.Screen ..... />
<Tab.Screen .... />
</Tab.Navigator>
);
}
now for the CustomTabBarIndIndicator component we simply go to the official github repository for react-native-tab-view and then go to TabBarIndicator.tsx and copy the component over in a file called CustomTabBarIndicator "just to be consistence with the example, but you can call it what ever you want", and don't forget to add the additional property to the Props type for tabBarItemPadding "if you are using typescript"
and now make this small change to the line that is highlighted in the image
change:
const outputRange = inputRange.map(getTabWidth);
to be:
const outputRange = inputRange.map(x => {
// this part is customized to get the indicator to be the same width like the label
// subtract the tabBarItemPadding from the tabWidth
// so that you indicator will be exactly the same size like the label text
return getTabWidth(x) - this.props.tabBarItemPadding * 2;
});
and that was it :)
P.S. I added the image because I didn't know how to exactly describe where to make the change
and if you don't want the typescript .. jsut remove all the types from the code and you are good to go :)

React Native animation can't change background color

Using Animated library, I'm trying to change background color when scrolling horizontally inside ScrollView.
But the background color didn't change.
const renderMultipleView = () => {
return ['Page 1', 'Page 2'].map(t => (
<View
style={{
flex: 1,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: 'transparent',
width: widthScreen,
height: '100%',
}}>
<Text style={{color: 'white'}}>{t.toString()}</Text>
</View>
));
};
const App = () => {
var [scrollX] = useState(new Animated.Value(0));
var backgroundColorChange = scrollX.interpolate({
inputRange: [0, widthScreen / 2, widthScreen],
outputRange: ['#b3006e', '#9a015f', '#380624'],
extrapolate: 'clamp',
});
return (
<Animated.ScrollView
horizontal={true}
onScroll={e => {
console.log('e: ' + e.nativeEvent.contentOffset.x);
Animated.event(
[
{
nativeEvent: {contentOffset: {x: scrollX}},
},
],
{useNativeDriver: true},
);
}}
scrollEventThrottle={16}
style={[styles.scrollView, {backgroundColor: backgroundColorChange}]}>
{renderMultipleView()}
</Animated.ScrollView>
);
};
Here is the result, I want it to change from pink to violet when scrolling from left to right and reverse but it's only pink.
Do I make anything wrong with the animation code or animation in RN can't change background. How can I fix that.
Thanks in advance!
Background Color is not supported by the native Animated module.
change your onScroll method to,
onScroll={Animated.event(
[
{
nativeEvent: {contentOffset: {x: scrollX}},
},
],
// {useNativeDriver: true},
)}

React Native Gesture Handler shows: undefined is not an object

I have setup a react-native 0.61 project which runs without any error but I'm getting this whenever I swipe. I am using react-native-gesture-handler: 1.3.0.
My
TypeError: Undefined is not an object (evaluating 'rectEvt[mappingKey]'). Please see the image for detail
I am using the following code:
const circleRadius = 30;
class Circle extends Component {
_touchX = new Animated.Value(windowWidth / 2 - circleRadius);
_onPanGestureEvent = Animated.event([{nativeEvent: {x: this._touchX}}, { useNativeDriver: true }]);
render() {
return (
<PanGestureHandler
onGestureEvent={this._onPanGestureEvent}>
<Animated.View style={{
height: 150,
justifyContent: 'center',
}}>
<Animated.View
style={[{
backgroundColor: '#42a5f5', borderRadius: circleRadius, height: circleRadius * 2, width: circleRadius * 2,
}, {
transform: [{translateX: Animated.add(this._touchX, new Animated.Value(-circleRadius))}]
}]}
/>
</Animated.View>
</PanGestureHandler>
);
}
}
You are passing the arguments to Animated.event in a wrong way:
_onPanGestureEvent = Animated.event([{nativeEvent: {x: this._touchX}}, { useNativeDriver: true }]);
The Animated.event should have the following structure:
_onPanGestureEvent=Animated.event(
[{nativeEvent: {x: this._touchX}}],
{ useNativeDriver: true }
{listener}, // Optional async listener
)

Flatlist won't scroll; cells rendering with extra space

I'm having this very strange problem. When I render a list of products with a FlatList, it's putting this giant space between my cells. (I've commented out the background image to speed loading, but it behaves the same either way)
ProductsListScreen.js
class ProductsListScreen extends Component<Props> {
render() {
return <WithFlatList products={this.props.products} />;
// return <WithMap products={this.props.products} />;
}
}
export default connect(({ productsReducer }) => ({
products: Object.values(productsReducer.products)
}))(ProductsListScreen);
const WithFlatList = ({ products }) => {
return (
<FlatList
data={products}
renderItem={({ item }) => <ProductListCellView product={item} />}
keyExtractor={item => `${item.id}`}
/>
);
};
const WithMap = ({ products }) => {
return (
<ScrollView contentContainerStyle={styles.container}>
{products.map(p => (
<ProductListCellView product={p} key={p.id} />
))}
</ScrollView>
);
};
const styles = {
container: {
flex: 1,
height: "100%"
}
};
ProductsListCellView.js
const ProductListCellView = ({ product }: Props) => {
return (
<View style={styles.cellContainer}>
<ImageBackground
// source={{ uri: product.images[0].src }}
style={styles.backgroundImage}
imageStyle={styles.imageStyle}
>
<View style={styles.textContainer}>
<NameText> {product.name} </NameText>
<PriceText> ${product.price} </PriceText>
</View>
</ImageBackground>
</View>
);
};
export default ProductListCellView;
const styles = {
cellContainer: {
borderBottomWidth: 0.5,
borderBottomColor: "grey",
width: "100%",
height: "50%",
borderWidth: 3,
backgroundColor: "lightblue"
},
backgroundImage: {
width: "100%",
height: "100%",
justifyContent: "center"
},
imageStyle: {
height: "140%",
width: "140%",
left: "-20%",
top: "-20%"
},
textContainer: {
backgroundColor: "black",
maxWidth: "50%",
padding: 5,
opacity: 0.75
}
};
const baseSize = 14;
const text = {
name: {
fontSize: baseSize + 8,
fontWeight: "bold",
color: "white"
},
price: { fontSize: baseSize + 4, color: "white" }
};
const NameText = props => <Text style={text.name}>{props.children}</Text>;
const PriceText = props => <Text style={text.price}>{props.children}</Text>;
It seems that whatever I set the height for cellContainer at, it renders the cell at that % of the screen (or of some container that seems based on screen height), and then the cell contents at the same % of the cell.
Also, the list isn't scrolling. I can see the next cell peeking out the bottom so the whole list is rendering, but it just bounces back when I try to scroll. I've tried wrapping various things in ScrollView with no luck. (I changed the cellContainer height to 15% in the screenshot below)
When I map the items manually (switching the return in the above code to use `, the height works fine, but the scrolling still doesn't work:
Has anybody else had this problem?
Rather than setting the height of cellContainer to a % value, set it to a static height, or using padding to automatically size each item.

Parallax/ animated Header on react native with scrollView onScroll

With this reference
Problems with parallax header in react native
The only solution found is just an hack that hide you refreshcomponent because contentContainerStyle don't interact with the refreshcomponent.
So, the only solution is to move the scrollview component, but moving it while you are scrolling is pretty laggy and staggering.
Any solution?
This is pretty common case, i mean..Facebook app and Twitter app have both this this type of home screen!
and example animation is:
animated header from play store app home
added snack:
snack esample of header animation
as you see, on Android, scrolling up and down start to stagger because the 2 animation (container and scroll) are concurrent: they don't mix, each one try to animate ..going mad.
UPDATE 3: snack solution good for android and ios
update: complete snack with gif like animation
I found a workaround for the first partial solution (absolute header with transform translate and contentContainerStyle with paddingTop)
The problem is only on the refresh component, so what to do?
const AnimatedFlatList = Animated.createAnimatedComponent(FlatList)
<AnimatedFlatList
data={data}
renderItem={item => this._renderRow(item.item, item.index)}
scrollEventThrottle={16}
onScroll={Animated.event([
{ nativeEvent: { contentOffset: { y: this.state.scrollAnim } }, },
], { useNativeDriver: true })}
refreshControl={
<RefreshControl
refreshing={this.state.refreshing}
onRefresh={() => {
this.setState({ refreshing: true });
setTimeout(() => this.setState({ refreshing: false }), 1000);
}}
// Android offset for RefreshControl
progressViewOffset={NAVBAR_HEIGHT}
/>
}
// iOS offset for RefreshControl
contentInset={{
top: NAVBAR_HEIGHT,
}}
contentOffset={{
y: -NAVBAR_HEIGHT,
}}
/>
This apply offset style on the refreshController, aligning it with the content.
UPDATE2:
there are some problems on ios.
UPDATE3:
fixed on ios too.
snack working both ios and android
You can try the react-spring library as it supports Parallax effects for react native.
Update: A working solution from your example
import React, { Component } from 'react';
import { Animated, Image, Platform, StyleSheet, View, Text, FlatList } from 'react-native';
const data = [
{
key: 'key',
name: 'name',
image: 'imageUrl',
},
];
const NAVBAR_HEIGHT = 90;
const STATUS_BAR_HEIGHT = Platform.select({ ios: 20, android: 24 });
const styles = StyleSheet.create({
fill: {
flex: 1,
},
navbar: {
position: 'absolute',
top: 0,
left: 0,
right: 0,
alignItems: 'center',
backgroundColor: 'white',
borderBottomColor: '#dedede',
borderBottomWidth: 1,
height: NAVBAR_HEIGHT,
justifyContent: 'center',
paddingTop: STATUS_BAR_HEIGHT,
},
contentContainer: {
flex: 1,
},
title: {
color: '#333333',
},
row: {
height: 300,
width: null,
marginBottom: 1,
padding: 16,
backgroundColor: 'transparent',
},
rowText: {
color: 'white',
fontSize: 18,
},
});
export default class App extends Component {
constructor(props) {
super(props);
const scrollAnim = new Animated.Value(0);
this._clampedScrollValue = 0;
this._offsetValue = 0;
this._scrollValue = 0;
this.state = {
scrollAnim,
};
}
_renderRow(rowData, rowId) {
return (
<View style={{ flex: 1 }}>
<Image key={rowId} style={styles.row} source={{ uri: rowData.image }} resizeMode="cover" />
<Text style={styles.rowText}>{rowData.title}</Text>
</View>
);
}
render() {
const { scrollAnim } = this.state;
const navbarTranslate = scrollAnim.interpolate({
inputRange: [0, NAVBAR_HEIGHT - STATUS_BAR_HEIGHT],
outputRange: [0, -(NAVBAR_HEIGHT - STATUS_BAR_HEIGHT)],
extrapolate: 'clamp',
});
const navbarOpacity = scrollAnim.interpolate({
inputRange: [0, NAVBAR_HEIGHT - STATUS_BAR_HEIGHT],
outputRange: [1, 0],
extrapolate: 'clamp',
});
return (
<View style={styles.fill}>
<View style={styles.contentContainer}>
<FlatList
data={data}
renderItem={item => this._renderRow(item.item, item.index)}
scrollEventThrottle={16}
onScroll={Animated.event([
{ nativeEvent: { contentOffset: { y: this.state.scrollAnim } } },
])}
/>
</View>
<Animated.View style={[styles.navbar, { transform: [{ translateY: navbarTranslate }] }]}>
<Animated.Text style={[styles.title, { opacity: navbarOpacity }]}>PLACES</Animated.Text>
</Animated.View>
</View>
);
}
}
In case anybody else falls on this thread, I developed a new package, react-native-animated-screen, that does exactly what you need
Check it out
https://www.npmjs.com/package/react-native-animated-screen