react-native : change view corresponding to scroll position - javascript

I am using Animation.view to change the height and the background of the header.
I set my height and the background settings like this:
const HeaderHeight = this.state.scrollY.interpolate({
inputRange:[0, Header_Max_Height - Header_Min_Height],
outputRange:[Header_Max_Height, Header_Min_Height],
extrapolate:'clamp'
})
const AnimateHeaderBackgroundColor = this.state.scrollY.interpolate({
inputRange: [ 0, ( Header_Max_Height - Header_Min_Height ) ],
outputRange: [ '#009688', '#00BCD4' ],
extrapolate: 'clamp'
})
This is my animated.view.
<Animated.View style={{width:'100%', height: HeaderHeight, backgroundColor:AnimateHeaderBackgroundColor}}></Animated.View>
Everything works well.
My question is there a way I could change the view like the height and the backgroundcolor?
For example, say I have two views:
//view1
<View style={{width:'100%',height:100, backgroundColor:'red'}}>
<Text>View1</Text>
</View>
//view2
<View style={{width:'100%',height:100, backgroundColor:'blue'}}>
<Text>View2</Text>
</View>
I want the view1 to show by default and show view2 as I scroll to the top of the screen. Placing the View in the outputRange would make this possible?

I guess there's no direct way in RN if you want to animated a change of view, however, in your case I can think of a little trick using the mix of opacity, position: absolute and interpolate(), here is a minimal example which you can directly copy and paste to test it:
import React, { Component } from 'react';
import { StyleSheet, Animated, View, ScrollView } from 'react-native';
class AnimationExample extends Component {
constructor(props) {
super(props)
this.state = {
showBlueView: false,
animatedOpacityValue: new Animated.Value(0),
}
}
handleScroll = (event) => {
const { animatedOpacityValue, showBlueView } = this.state;
const scrollPosition = event.nativeEvent.contentOffset.y;
if (scrollPosition > 100 && !showBlueView) {
Animated.timing(animatedOpacityValue, {
toValue: 1,
}).start(() => this.setState({ showBlueView: true }))
}
if (scrollPosition < 100 && showBlueView) {
Animated.timing(animatedOpacityValue, {
toValue: 0,
}).start(() => this.setState({ showBlueView: false }))
}
}
render() {
const { animatedOpacityValue } = this.state;
return (
<ScrollView
style={styles.scrollView}
onScroll={this.handleScroll}
scrollEventThrottle={16}
>
<View style={styles.green} />
<View style={styles.animatedViewsPositioner}>
<Animated.View
style={{
...styles.red,
opacity: animatedOpacityValue.interpolate({
inputRange: [0, 1],
outputRange: [1, 0],
}),
}}
/>
<Animated.View
style={{
...styles.blue,
opacity: animatedOpacityValue.interpolate({
inputRange: [0, 1],
outputRange: [0, 1],
}),
}}
/>
</View>
</ScrollView>
)
}
}
const styles = StyleSheet.create({
scrollView: {
flex: 1,
},
green: {
height: 600,
width: '100%',
backgroundColor: 'green',
},
red: {
height: 300,
width: '100%',
backgroundColor: 'red',
},
blue: {
position: 'absolute',
height: 300,
width: '100%',
backgroundColor: 'blue',
},
animatedViewsPositioner: {
position: 'relative',
},
})
In the example above, I first access the scroll position by applying a handleScroll function to the scrollView. Make sure you have scrollEventThrottle set to 16 to ensure the function is triggered every second, but beware of possible performance issue caused by that (if you care, you might take a look at this for more info).
To achieve a view change triggered when user scroll to certain position (which is actually not, but it looks like that), I use a view to wrap both red and blue views, the red one is default with opacity: 1, while the blue one with default opacity: 0, sitting on top of the red one.
I hide the red view and show the blue one by animating their opacity using interpolate(). With the help of that, both opacity values are controlled by one animatedValue animatedOpacityValue put in the state. I added a state showBlueView to optimise the performance by avoid constantly setting states triggered by onScroll.
Here's an update to add touchableOpacities on both views, simply achieve by hiding the blue view when it's unused.
First, add a log function:
log = (stringToPrint) => () => {
console.log(stringToPrint)
}
Next, change the scrollView like this by adding two touchableOpacity
<ScrollView
style={styles.scrollView}
onScroll={this.handleScroll}
scrollEventThrottle={16}
>
<View style={styles.green} />
<View style={styles.animatedViewsPositioner}>
<Animated.View
style={{
...styles.red,
opacity: animatedOpacityValue.interpolate({
inputRange: [0, 1],
outputRange: [1, 0],
}),
}}
>
<TouchableOpacity
style={{ backgroundColor: 'black', width: 80, height: 30 }}
onPress={this.log('click on red')}
/>
</Animated.View>
{showBlueView && (
<Animated.View
style={{
...styles.blue,
opacity: animatedOpacityValue.interpolate({
inputRange: [0, 1],
outputRange: [0, 1],
}),
}}
>
<TouchableOpacity
style={{ backgroundColor: 'black', width: 80, height: 30 }}
onPress={this.log('click on blue')}
/>
</Animated.View>
)}
</View>
</ScrollView>
Note that I added showBlueView && to hide the blue view when its opacity is 0, so that it will not block any click event applied to the red view (even though the blue view is hidden, it is actually on top of the red view with opacity: 0).

#Andus 's ans with Animated.event
The idea is to get the latest scrollY then wrap it to view's opacity. The example input range of blue target is 0-50 and got opacity 1 to 0. That means it would fade out when scrolling down the first 50 px.
The red one is the reverse one with input range 0-200 and out to opacity 0 to 1.(fade in)
import React, { Component } from 'react';
import { StyleSheet, Animated, View, ScrollView, SafeAreaView } from 'react-native';
export default class AnimationExample extends Component {
constructor(props) {
super(props)
this.state = {
scrollY: new Animated.Value(0)
}
}
render() {
const {scrollY} = this.state;
return (
<SafeAreaView style={{flex: 1}}>
<ScrollView
style={styles.scrollView}
onScroll={Animated.event(
[{nativeEvent: {contentOffset: {y: this.state.scrollY}}}]
)}
scrollEventThrottle={16}
>
<View style={styles.animatedViewsPositioner}>
<Animated.View
style={[styles.box, styles.target, {
opacity: scrollY.interpolate({
inputRange: [0, 50],
outputRange: [1, 0],
}),
}]}
/>
<Animated.View
style={[styles.box, styles.origin, {
opacity: scrollY.interpolate({
inputRange: [0, 200],
outputRange: [0, 1],
}),
}]}
/>
</View>
</ScrollView>
</SafeAreaView>
)
}
}
const styles = StyleSheet.create({
scrollView: {
flex: 1,
},
box: {
height: 1000,
width: '100%',
position: 'absolute'
},
origin: {
backgroundColor: 'red',
zIndex: 1
},
target: {
backgroundColor: 'blue',
zIndex: 2
},
animatedViewsPositioner: {
position: 'relative',
backgroundColor: 'pink',
height: 10000
},
})

If you are using ScrollView in displaying the View, I believe you can use the onScroll callback to get the position of your screen inside the ScrollView and change the height and color dynamically when your user scroll to the top.
<ScrollView onScroll={this.handleScroll} />
And getting the position,
handleScroll: function(event: Object) {
console.log(event.nativeEvent.contentOffset.y);
},
Reference: Get current scroll position of ScrollView in React Native

Related

borderRadius doesn't work on image when resizeMode is set to 'contain'

I've been trying to build an Image carousel with a bunch of randomly selected images. I wanted to maintain their aspect ratios so I set the resizeMode to 'contain'. Somehow that step leads to the loss of any set borderRadius! What could be the reason? And if that step doesn't work at all, any other ideas on how to maintain the correct aspect ratio + get the corners rounded?
Thanks a lot for your help!
here's the code:
import React, { useCallback, memo, useRef, useState } from "react";
import {
FlatList,
View,
Dimensions,
Text,
StyleSheet,
Image,
} from "react-native";
const images = [
Image1,
Image2,
Image3,
Image4,
Image5,
Image6,
Image7,
Image8,
Image9,
Image10,
Image11,
Image12,
Image13,
Image14,
Image15,
Image16,
Image17,
Image18,
Image19,
Image20,
Image21,
Image22,
Image23,
Image24,
Image25,
Image26,
Image27,
Image28,
Image29,
Image30,
Image31,
Image32,
Image33,
Image34,
Image35,
Image36,
Image37,
Image38,
Image39,
Image40,
Image41,
]
const { width: windowWidth, height: windowHeight } = Dimensions.get("window");
const randomImage = () =>
images[Math.floor(Math.random() * images.length)];
const styles = StyleSheet.create({
slide: {
height: windowHeight,
width: windowWidth,
//justifyContent: "center",
alignItems: "center",
},
slideImage: {
height: '70%',
width: '90%',
borderRadius: 20,
marginTop: 20,
},
slideTitle: {
fontSize: 24,
marginTop: 0,
},
slideSubtitle: {
fontSize: 18,
marginTop: 10,
},
pagination: {
position: "absolute",
bottom: 8,
justifyContent: "center",
flexDirection: "row",
marginBottom: 12
},
paginationDot: {
width: 8,
height: 8,
borderRadius: 4,
marginHorizontal: 2,
},
paginationDotActive: { backgroundColor: "lightblue" },
paginationDotInactive: { backgroundColor: "gray" },
carousel: {},
});
const slideList = Array.from({ length: 999 }).map((_, i) => {
return {
id: i,
image: randomImage,
title: `This is the title ${i + 1}!`,
subtitle: `This is the subtitle ${i + 1}!`,
};
});
const Slide = memo(function Slide({ data }) {
return (
<View style={styles.slide}>
<Image resizeMode = 'contain' source = {randomImage()} style={styles.slideImage}></Image>
<Text style={styles.slideTitle}>{data.title}</Text>
<Text style={styles.slideSubtitle}>{data.subtitle}</Text>
</View>
);
});
function Pagination({ index }) {
return (
<View style={styles.pagination} pointerEvents="none">
{slideList.map((_, i) => {
return (
<View
key={i}
style={[
styles.paginationDot,
index === i
? styles.paginationDotActive
: styles.paginationDotInactive,
]}
/>
);
})}
</View>
);
}
export default function Carousel() {
const [index, setIndex] = useState(0);
const indexRef = useRef(index);
indexRef.current = index;
const onScroll = useCallback((event) => {
const slideSize = event.nativeEvent.layoutMeasurement.width;
const index = event.nativeEvent.contentOffset.x / slideSize;
const roundIndex = Math.round(index);
const distance = Math.abs(roundIndex - index);
// Prevent one pixel triggering setIndex in the middle
// of the transition. With this we have to scroll a bit
// more to trigger the index change.
const isNoMansLand = 0.4 < distance;
if (roundIndex !== indexRef.current && !isNoMansLand) {
setIndex(roundIndex);
}
}, []);
const flatListOptimizationProps = {
initialNumToRender: 0,
maxToRenderPerBatch: 1,
removeClippedSubviews: true,
scrollEventThrottle: 16,
windowSize: 2,
keyExtractor: useCallback(s => String(s.id), []),
getItemLayout: useCallback(
(_, index) => ({
index,
length: windowWidth,
offset: index * windowWidth,
}),
[]
),
};
const renderItem = useCallback(function renderItem({ item }) {
return <Slide data={item} />;
}, []);
return (
<>
<FlatList
data={slideList}
style={styles.carousel}
renderItem={renderItem}
pagingEnabled
horizontal
showsHorizontalScrollIndicator={false}
bounces={false}
onScroll={onScroll}
{...flatListOptimizationProps}
/>
<Pagination index={index}></Pagination>
</>
);
}
``
Actually borderRadius works but you can't see it because of an incorrect ratio.
If your image has a 16:9 ratio, for example, 1600x900 dimensions, then you need to set width and height with the same ratio.
<Image
source={ 1600x900 }
resizeMode="contain"
style={{
width: 300,
height: 300,
borderRadius: 15,
backgroundColor: 'red'
}} />
The result will be:
Because the image has width and height 300, ie 1:1 ratio. If you modify width and height like 320 and 180, ie 16:9, then the image fills all the space and borders will be visible.
Another workaround is to wrap your image with view that hides the overflow
<View
style={{
width: 300,
height: 300,
borderRadius: 150,
overflow: 'hidden',
}}
>
<Image
source={item.image}
style={{
width: 300,
height: 300,
}}
resizeMode='contain'
/>
</View>

How to set dynamic height for each row with react-native-swipe-list-view?

Description
I am working on a react-native project using expo SDK36.
I want to do a swipe left/right list view. I use react-native-swipe-list-view to achieve it.
So far everything worked perfectly, the default example uses a fixed height: 50 per row, while I want to set the height of each row dynamically.
Every attempt where a failure, note that I already use <SwipeListView recalculateHiddenLayout={true} />
This is bad for the UX, since the default line is having a small height: 50, it is nearly impossible to drag the line on iOS and android properly.
Reproduction
Snack: https://snack.expo.io/#kopax/react-native-swipe-list-view-408
import React from 'react';
import { Dimensions, Text, View, StyleSheet } from 'react-native';
import Constants from 'expo-constants';
// You can import from local files
import SwipeListView from './components/SwipeListView';
// or any pure javascript modules available in npm
import { Card } from 'react-native-paper';
export default class App extends React.Component {
render() {
return (
<View style={styles.container}>
<Text style={styles.paragraph}>
Change code in the editor and watch it change on your phone! Save to get a shareable url.
</Text>
<Card>
<SwipeListView
dimensions={Dimensions.get('window')}
listViewData={Array(20).fill('').map((d, i) => ({
...d,
title: `Item ${i}`,
description: `This is a very long description for item number #${i},
it should be so long that you cannot see all the content,
the issue is about fixing the dynamic height for each row`
}))
}
minHeight={200}
/>
</Card>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
},
paragraph: {
margin: 24,
fontSize: 18,
fontWeight: 'bold',
textAlign: 'center',
},
});
This is my components/SwipeListView.js
import React, { Component } from 'react';
import {
Animated,
Image,
StyleSheet,
TouchableOpacity,
TouchableHighlight,
View,
} from 'react-native';
import {
Avatar,
Button,
Text,
Title,
Subheading,
TouchableRipple,
withTheme,
} from 'react-native-paper';
import { SwipeListView as SwipeListViewDefault } from 'react-native-swipe-list-view';
/* eslint-disable react/prop-types, react/destructuring-assignment, react/no-access-state-in-setstate */
class SwipeListView extends Component {
leftBtnRatio = 0.25;
rightBtnRatio = 0.75;
constructor(props) {
super(props);
this.state = {
listType: 'FlatList',
listViewData: props.listViewData
.map((data, i) => ({ key: `${i}`, ...data })),
};
this.rowTranslateAnimatedValues = {};
props.listViewData
.forEach((data, i) => {
this.rowTranslateAnimatedValues[`${i}`] = new Animated.Value(1);
});
}
getStyles() {
const { minHeight, theme } = this.props;
const { colors } = theme;
return StyleSheet.create({
rowFrontContainer: {
overflow: 'hidden',
},
rowFront: {
alignItems: 'center',
backgroundColor: colors.surface,
borderBottomColor: colors.accent,
borderBottomWidth: 1,
justifyContent: 'center',
minHeight: '100%',
flex: 1,
},
rowBack: {
alignItems: 'center',
backgroundColor: colors.surface,
flexDirection: 'row',
justifyContent: 'space-between',
paddingLeft: 15,
minHeight: '100%',
},
backBtn: {
alignItems: 'center',
bottom: 0,
justifyContent: 'center',
position: 'absolute',
top: 0,
},
backLeftBtn: {
backgroundColor: colors.primary,
left: 0,
width: `${this.leftBtnRatio * 100}%`,
},
backRightBtn: {
backgroundColor: colors.accent,
right: 0,
width: `${this.rightBtnRatio * 100}%`,
},
});
}
onRowDidOpen = (rowKey) => {
console.log('This row opened', rowKey);
};
onSwipeValueChange = swipeData => {
const { dimensions } = this.props;
const { key, value } = swipeData;
if (value < -dimensions.width * this.rightBtnRatio && !this.animationIsRunning) {
this.animationIsRunning = true;
Animated.timing(this.rowTranslateAnimatedValues[key], {
toValue: 0,
duration: 200,
}).start(() => {
const newData = [...this.state.listViewData];
const prevIndex = this.state.listViewData.findIndex(item => item.key === key);
newData.splice(prevIndex, 1);
this.setState({listViewData: newData});
this.animationIsRunning = false;
});
}
};
closeRow(rowMap, rowKey) {
if (rowMap[rowKey]) {
rowMap[rowKey].closeRow();
}
}
deleteRow(rowMap, rowKey) {
this.closeRow(rowMap, rowKey);
const newData = [...this.state.listViewData];
const prevIndex = this.state.listViewData.findIndex(
(item) => item.key === rowKey,
);
newData.splice(prevIndex, 1);
this.setState({ listViewData: newData });
}
render() {
const { minHeight, dimensions, theme } = this.props;
const { colors } = theme;
const styles = this.getStyles();
return (
<SwipeListViewDefault
data={this.state.listViewData}
renderItem={data => (
<Animated.View
style={[styles.rowFrontContainer, {
height: this.rowTranslateAnimatedValues[data.item.key].interpolate({
inputRange: [0, 1],
outputRange: [0, minHeight],
})}]}
>
<TouchableRipple
onPress={() => console.log('You touched me')}
style={styles.rowFront}
underlayColor={colors.background}
>
<View>
<Title>{data.item.title}</Title>
<Text>
{data.item.description}
</Text>
</View>
</TouchableRipple>
</Animated.View>
)}
renderHiddenItem={(data, rowMap) => (
<View style={styles.rowBack}>
<TouchableOpacity
style={[
styles.backLeftBtn,
styles.backBtn,
]}
onPress={() => this.closeRow(rowMap, data.item.key)}
>
<Text>Tap pour annuler</Text>
</TouchableOpacity>
<TouchableOpacity
style={[
styles.backRightBtn,
styles.backBtn,
]}
onPress={() => this.deleteRow(rowMap, data.item.key)}
>
<Animated.View
style={[
styles.trash,
{
transform: [
{
scale: this.rowTranslateAnimatedValues[
data.item.key
].interpolate({
inputRange: [
45,
90,
],
outputRange: [0, 1],
extrapolate:
'clamp',
}),
},
],
},
]}
>
<Text>Swipe left to delete</Text>
</Animated.View>
</TouchableOpacity>
</View>
)}
leftOpenValue={dimensions.width * this.leftBtnRatio}
rightOpenValue={-dimensions.width * this.rightBtnRatio}
previewRowKey={'0'}
previewOpenValue={-40}
previewOpenDelay={3000}
onRowDidOpen={this.onRowDidOpen}
onSwipeValueChange={this.onSwipeValueChange}
recalculateHiddenLayout={true}
/>
);
}
}
export default withTheme(SwipeListView);
Expect
I expect when using recalculateHiddenLayout={true}, to get the hidden row height calculated dynamically
Result Screenshots
On the web, I am able to set the height:
but I when using iOS and Android, the height is forced.
Environment
OS: ios/android/web
RN Version: expo SDK36
How can I set the height of each row dynamically?
Important edit
The problem is the fixed value here in the animation:
height: this.rowTranslateAnimatedValues[data.item.key].interpolate({
inputRange: [0, 1],
outputRange: [0, 200], // <--- here
})}]}
I have replaced it in the example with props.minHeight:
height: this.rowTranslateAnimatedValues[data.item.key].interpolate({
inputRange: [0, 1],
outputRange: [0, this.props.minHeight],
})}]}
It doesn't permit dynamic height, how can I get the row height dynamically?

Declaring array for use in React Native AutoComplete search engine

Not sure where I go about declaring the array with which I want to search from, any assistance would be appreciated. I believe my issue is that I am declaring the "services' array in the incorrect area but I am not sure where else to put it! Or if the commas are the right character to be using in between strings/services
import React, { useState, Component } from 'react';
import { StyleSheet, StatusBar, View, Text, Button, TouchableOpacity } from 'react-native';
import AutoComplete from 'react-native-autocomplete-input';
class CareProviderSequenceScreen extends Component {
constructor (props) {
super (props);
this.state = {
services: [],
query: '',
}
}
render() {
const query = this.state;
const services = {
"Pick up my Prescription",
'Pick up groceries',
'Pick up dry cleaning',
'Pick up my pet',
}
return (
<View style={styles.container}>
<Autocomplete
autoCapitalize="none"
autoCorrect={false}
containerStyle={styles.autocompleteContainer}
//data to show in suggestion
data={services.length === 1 && comp(query, services[0].title) ? [] : services}
//default value if you want to set something in input
defaultValue={query}
/*onchange of the text changing the state of the query which will trigger
the findFilm method to show the suggestions*/
onChangeText={text => this.setState({ query: text })}
placeholder="Enter your need"
renderItem={({ item }) => (
//you can change the view you want to show in suggestion from here
<TouchableOpacity onPress={() => this.setState({ query: item.title })}>
<Text style={styles.itemText}>
{item.title} ({item.release_date})
</Text>
</TouchableOpacity>
)}
/>
<View style={styles.descriptionContainer}>
{services.length > 0 ? (
<Text style={styles.infoText}>{this.state.query}</Text>
) : (
<Text style={styles.infoText}>Enter The Film Title</Text>
)}
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
backgroundColor: '#F5FCFF',
flex: 1,
padding: 16,
marginTop: 40,
},
autocompleteContainer: {
backgroundColor: '#ffffff',
borderWidth: 0,
},
descriptionContainer: {
flex: 1,
justifyContent: 'center',
},
itemText: {
fontSize: 15,
paddingTop: 5,
paddingBottom: 5,
margin: 2,
},
infoText: {
textAlign: 'center',
fontSize: 16,
},
});
export default CareProviderSequenceScreen ;
CareProviderSequenceScreen .navigationOptions = () => ({
title: "Home & Personal Care",
headerTintColor: '#9EBBD7',
headerStyle: {
height: 65,
backgroundColor: '#1E5797',
shadowColor: "#000",
shadowOffset: {
width: 0,
height: 1,
},
shadowOpacity: 0.20,
shadowRadius: 1.41,
elevation: 2,}
});
First, you are assigning an object to services array.
Second, you are not accessing the query state properly. It should be
const { query } = this.state

On scroll React Native Animated do the animation but flickering

In my react native app I'm trying to animate opacity of a View.
When I scroll, I saw the animation do the job, but it’s flickering at the same time. I don’t know why.
Video example : https://cdn.discordapp.com/attachments/102861040538120192/560165613092339734/video.mov
Here is the code I made
const Scrollable = () => {
const largeHeaderSize = useState({
height: 0,
y: 0
});
const animatedValueScrollY = new Animated.Value(largeHeaderSize.height);
const [scrollY, setScrollY] = useState(0);
const headerOpacity = animatedValueScrollY.interpolate({
inputRange: [0, largeHeaderSize.height],
outputRange: [1, 0],
extrapolate: "clamp"
});
return (
<SafeAreaView>
<Animated.View
style={{
borderBottomWidth:
scrollY >= largeHeaderSize.height ? StyleSheet.hairlineWidth : 0
}}>
<View>
<Animated.View style={{ zIndex: 1, opacity: headerOpacity }}>
<Text>Title</Text>
</Animated.View>
</View>
</Animated.View>
<Animated.ScrollView
onScroll={Animated.event(
[{ nativeEvent: { contentOffset: { y: animatedValueScrollY } } }],
{
useNativeDriver: true,
listener: event => setScrollY(event.nativeEvent.contentOffset.y)
}
)}
scrollEventThrottle={16}
contentInset={{
top: 0,
bottom: 50
}}
contentOffset={{
y: 0
}}
/>
</SafeAreaView>
);
};
How I can I solve that?
The solution is to use the useRef hook like so:
const animatedValueScrollY = useRef(new Animated.Value(0))
const headerOpacity = animatedValueScrollY.current.interpolate({
inputRange: [0, largeHeaderSize.height],
outputRange: [1, 0],
extrapolate: 'clamp'
});

react native how to animate two views side by side

I want to animate two views side by side. But the height of the views is not that what I want. I want to set the height of the visible view.
Here is a video of my problem:
https://imgur.com/a/se8Vj
and here is a example of the expo: https://snack.expo.io/ByFSjLt5W
I can't find the problem why the height is not right.
my component card have this code:
<Card
title='LOGIN'
wrapperStyle={{
margin: 0
}}
containerStyle={{
elevation: 20,
margin: 40,
borderWidth:0,
top: -150,
}}
titleStyle={{
textAlign: 'left'
}}
dividerStyle={{
marginTop: 0,
marginBottom: 0
}}
>
<Animated.View
style={{
transform: [{
translateX: this.state.offsetEmail
}]
}}
>
<FormLabel>Email</FormLabel>
<FormInput
focus={true}
placeholder='Email address...'
selectionColor='#fff'
underlineColorAndroid='#0D47A1'
keyboardType='email-address'
onChangeText={(email) => this._setEmail.bind(this)(email)}
/>
{this.state.email.length > 0 &&
<Button
title='weiter'
onPress={() => { Keyboard.dismiss(); this._transitionToPassword(); } }
/>
}
</Animated.View>
<Animated.View
style={{
transform: [{
translateX: this.state.offsetPassword
}]
}}
>
<FormLabel>Email</FormLabel>
<FormLabel>{this.state.email}</FormLabel>
<FormLabel>Password</FormLabel>
<FormInput
secureTextEntry
underlineColorAndroid='#0D47A1'
placeholder='Password...'
onChangeText={(password) => this._setPassword.bind(this)(password)}
/>
</Animated.View>
</Card>
my constructor:
constructor(props) {
super(props);
this.state = {
email: false,
password: false,
showPassword: false,
showSignInButton: false,
offsetEmail: new Animated.Value(0),
offsetPassword: new Animated.Value(width)
};
}
and my function to animate:
_transitionToPassword() {
Animated.parallel([
Animated.timing(this.state.offsetEmail, {
toValue: -width
}),
Animated.timing(this.state.offsetPassword, {
toValue: 0
})
]).start();
}
and my width:
const { width } = Dimensions.get('window');
Your Views are rendered one below the other. Before applying the animation you should first should fix your style to make them render side by side. You can use flex: 1, flexDirection: row and overflow: hidden to try to achieve it.
Check the docs for more tips about styling and flex layout: https://facebook.github.io/react-native/docs/flexbox.html
Hope it helps.

Categories

Resources